Merge "Update PCM to ignore invalid modem updates and add UT." into main
diff --git a/flags/data.aconfig b/flags/data.aconfig
index f93999b..d956104 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -25,10 +25,13 @@
 
 # OWNER=linggm TARGET=24Q3
 flag {
-  name: "auto_data_switch_rat_ss"
+  name: "auto_data_switch_uses_data_enabled"
   namespace: "telephony"
-  description: "Whether switch for better rat and signal strength"
-  bug:"260928808"
+  description: "Separately consider the backup phone's data allowed and data enabled."
+  bug: "338552223"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
 
 # OWNER=linggm TARGET=24Q2
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
index 27ab0b1..2eea80a 100644
--- a/flags/satellite.aconfig
+++ b/flags/satellite.aconfig
@@ -25,4 +25,12 @@
     namespace: "telephony"
     description: "This flag enables satellite internet support."
     bug:"326972202"
+}
+
+# OWNER=xalle TARGET=24Q3
+flag {
+    name: "satellite_persistent_logging"
+    namespace: "telephony"
+    description: "This flag enables satellite persistent logging"
+    bug:"339877723"
 }
\ No newline at end of file
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index 32e8f2d..9a5dabc 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -65,3 +65,15 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+# OWNER=songferngwang TARGET=24Q3
+flag {
+  name: "reset_primary_sim_default_values"
+  namespace: "telephony"
+  description: "Reset the default values to the remaining sim"
+  bug: "339394518"
+
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index 2d7b643..2679cfe 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -17,7 +17,7 @@
     bug:"303780982"
 }
 
-# OWNER=arunvoddu TARGET=24Q3
+# OWNER=arunvoddu TARGET=24Q4
 flag {
     name: "carrier_restriction_status"
     is_exported: true
@@ -50,3 +50,11 @@
     description: "This flag cleans up the OpenLogicalChannelRecord once SIM is removed"
     bug:"335046531"
 }
+
+# OWNER=arunvoddu TARGET=24Q4
+flag {
+    name: "set_carrier_restriction_status"
+    namespace: "telephony"
+    description: "This flag controls the visibility of the setCarrierRestrictionStatus API in carrierRestrictionRules class."
+    bug:"342411308"
+}
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index fe181ae..48e7b0d 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -23,7 +23,7 @@
 
 // Holds atoms to store on persist storage in case of power cycle or process crash.
 // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation.
-// Next id: 72
+// Next id: 82
 message PersistAtoms {
     /* Aggregated RAT usage during the call. */
     repeated VoiceCallRatUsage voice_call_rat_usage = 1;
@@ -237,6 +237,36 @@
 
     /* Timestamp of last data_network_validation pull. */
     optional int64 data_network_validation_pull_timestamp_millis = 71;
+
+    /* Snapshot of carrier roaming satellite session. */
+    repeated CarrierRoamingSatelliteSession carrier_roaming_satellite_session = 72;
+
+    /* Timestamp of last carrier_roaming_satellite_session pull. */
+    optional int64 carrier_roaming_satellite_session_pull_timestamp_millis = 73;
+
+    /* Snapshot of carrier roaming satellite controller stats. */
+    repeated CarrierRoamingSatelliteControllerStats carrier_roaming_satellite_controller_stats = 74;
+
+    /* Timestamp of last carrier_roaming_satellite_controller_stats pull. */
+    optional int64 carrier_roaming_satellite_controller_stats_pull_timestamp_millis = 75;
+
+    /* Snapshot of satellite entitlement. */
+    repeated SatelliteEntitlement satellite_entitlement = 76;
+
+    /* Timestamp of last satellite_entitlement pull. */
+    optional int64 satellite_entitlement_pull_timestamp_millis = 77;
+
+    /* Snapshot of satellite config updater. */
+    repeated SatelliteConfigUpdater satellite_config_updater = 78;
+
+    /* Timestamp of last satellite_config_updater pull. */
+    optional int64 satellite_config_updater_pull_timestamp_millis = 79;
+
+    /** Snapshot of satellite access controller. */
+    repeated SatelliteAccessController satellite_access_controller = 80;
+
+    /* Timestamp of last satellite access controller pull. */
+    optional int64 satellite_access_controller_pull_timestamp_millis = 81;
 }
 
 // The canonical versions of the following enums live in:
@@ -288,6 +318,8 @@
     optional bool is_iwlan_cross_sim_at_connected = 39;
     optional bool vonr_enabled = 40;
     optional bool is_ntn = 41;
+    optional bool supports_business_call_composer = 42;
+    optional int32 call_composer_status = 43;
 
     // Internal use only
     optional int64 setup_begin_millis = 10001;
@@ -318,6 +350,7 @@
     optional int32 count = 15;
     optional bool is_managed_profile = 16;
     optional bool is_ntn = 17;
+    optional bool is_emergency = 18;
 
     // Internal use only
     optional int32 hashCode = 10001;
@@ -382,6 +415,7 @@
     optional bool is_iwlan_cross_sim = 23;
     optional bool is_ntn = 24;
     optional bool is_satellite_transport = 25;
+    optional bool is_provisioning_profile = 26;
 }
 
 message CellularServiceState {
@@ -680,6 +714,9 @@
     optional int32 count_of_demo_mode_incoming_datagram_fail = 23;
     optional int32 count_of_datagram_type_keep_alive_success = 24;
     optional int32 count_of_datagram_type_keep_alive_fail = 25;
+    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;
 }
 
 message SatelliteSession {
@@ -695,6 +732,7 @@
     optional int32 count_of_incoming_datagram_success = 10;
     optional int32 count_of_incoming_datagram_failed = 11;
     optional bool is_demo_mode = 12;
+    optional int32 max_ntn_signal_strength_level = 13;
 }
 
 message SatelliteIncomingDatagram {
@@ -739,3 +777,59 @@
     optional bool handover_attempted = 6;
     optional int32 network_validation_count = 7;
 }
+
+message CarrierRoamingSatelliteSession {
+    optional int32 carrier_id = 1;
+    optional bool is_ntn_roaming_in_home_country = 2;
+    optional int32 total_satellite_mode_time_sec = 3;
+    optional int32 number_of_satellite_connections = 4;
+    optional int32 avg_duration_of_satellite_connection_sec = 5;
+    optional int32 satellite_connection_gap_min_sec = 6;
+    optional int32 satellite_connection_gap_avg_sec = 7;
+    optional int32 satellite_connection_gap_max_sec = 8;
+    optional int32 rsrp_avg = 9;
+    optional int32 rsrp_median = 10;
+    optional int32 rssnr_avg = 11;
+    optional int32 rssnr_median = 12;
+    optional int32 count_of_incoming_sms = 13;
+    optional int32 count_of_outgoing_sms = 14;
+    optional int32 count_of_incoming_mms = 15;
+    optional int32 count_of_outgoing_mms = 16;
+}
+
+message CarrierRoamingSatelliteControllerStats {
+    optional int32 config_data_source = 1;
+    optional int32 count_of_entitlement_status_query_request = 2;
+    optional int32 count_of_satellite_config_update_request = 3;
+    optional int32 count_of_satellite_notification_displayed = 4;
+    optional int32 satellite_session_gap_min_sec = 5;
+    optional int32 satellite_session_gap_avg_sec = 6;
+    optional int32 satellite_session_gap_max_sec = 7;
+}
+
+message SatelliteEntitlement {
+    optional int32 carrier_id = 1;
+    optional int32 result = 2;
+    optional int32 entitlement_status = 3;
+    optional bool is_retry = 4;
+    optional int32 count = 5;
+}
+
+message SatelliteConfigUpdater {
+    optional int32 config_version = 1;
+    optional int32 oem_config_result = 2;
+    optional int32 carrier_config_result = 3;
+    optional int32 count = 4;
+}
+
+message SatelliteAccessController {
+    optional int32 access_control_type = 1;
+    optional int64 location_query_time_millis = 2;
+    optional int64 on_device_lookup_time_millis = 3;
+    optional int64 total_checking_time_millis = 4;
+    optional bool is_allowed = 5;
+    optional bool is_emergency = 6;
+    optional int32 result_code = 7;
+    repeated string country_codes = 8;
+    optional int32 config_data_source = 9;
+}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 68fd6ab..82b4c2b8e 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -33,6 +33,7 @@
 
 import com.android.ims.internal.ConferenceParticipant;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
@@ -634,7 +635,7 @@
      *
      * @hide
      */
-    public void setEmergencyCallInfo(CallTracker ct) {
+    public void setEmergencyCallInfo(CallTracker ct, Phone.DialArgs dialArgs) {
         if (ct != null) {
             Phone currentPhone = ct.getPhone();
             if (currentPhone != null) {
@@ -677,20 +678,52 @@
         } else {
             Rlog.e(TAG, "setEmergencyCallInfo: call tracker is null");
         }
+
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            if (mEmergencyNumberInfo == null) {
+                Rlog.d(TAG, "setEmergencyCallInfo: create EmergencyNumber");
+                setNonDetectableEmergencyCallInfo((dialArgs != null) ? dialArgs.eccCategory
+                        : EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                        new ArrayList<String>());
+            }
+            if (dialArgs != null && dialArgs.intentExtras != null
+                    && dialArgs.intentExtras.getBoolean(
+                            PhoneConstants.EXTRA_USE_EMERGENCY_ROUTING, false)
+                    && mEmergencyNumberInfo.getEmergencyCallRouting()
+                        != EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY) {
+                int eccCategory = dialArgs.intentExtras.getInt(
+                        PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                        mEmergencyNumberInfo.getEmergencyServiceCategoryBitmask());
+                Rlog.d(TAG, "setEmergencyCallInfo: enforce emergency routing eccCategory="
+                        + eccCategory);
+                List<String> emergencyUrns = dialArgs.intentExtras.getStringArrayList(
+                        PhoneConstants.EXTRA_EMERGENCY_URNS);
+                if (emergencyUrns == null || emergencyUrns.isEmpty()) {
+                    emergencyUrns = mEmergencyNumberInfo.getEmergencyUrns();
+                }
+                mEmergencyNumberInfo = new EmergencyNumber(mEmergencyNumberInfo.getNumber(),
+                        mEmergencyNumberInfo.getCountryIso(),
+                        mEmergencyNumberInfo.getMnc(),
+                        eccCategory,
+                        emergencyUrns,
+                        mEmergencyNumberInfo.getEmergencyNumberSourceBitmask(),
+                        EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+            }
+        }
     }
 
     /**
      * Set the non-detectable emergency number information.
      */
-    public void setNonDetectableEmergencyCallInfo(int eccCategory) {
-        if (!mIsEmergencyCall) {
-            mIsEmergencyCall = true;
-            mEmergencyNumberInfo = new EmergencyNumber(mAddress, ""/*countryIso*/,
-                                    ""/*mnc*/, eccCategory,
-                                    new ArrayList<String>(),
-                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
-                                    EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
-        }
+    public void setNonDetectableEmergencyCallInfo(int eccCategory,
+            @NonNull List<String> emergencyUrns) {
+        Rlog.d(TAG, "setNonDetectableEmergencyCallInfo: eccCategory=" + eccCategory
+                + ", emergencyUrns=" + emergencyUrns);
+        mIsEmergencyCall = true;
+        mEmergencyNumberInfo = new EmergencyNumber(mAddress, ""/*countryIso*/, ""/*mnc*/,
+                                eccCategory, emergencyUrns,
+                                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 9113514..26d4e1b 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -160,7 +160,7 @@
     public GsmCdmaCallTracker(GsmCdmaPhone phone, FeatureFlags featureFlags) {
         super(featureFlags);
 
-        if (mFeatureFlags.minimalTelephonyCdmCheck()
+        if (TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)
                 && !phone.getContext().getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY_CALLING)) {
             throw new UnsupportedOperationException("GsmCdmaCallTracker requires calling");
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index e06520a..cc07047 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -139,7 +139,7 @@
         mHandler = new MyHandler(mOwner.getLooper());
 
         mAddress = dc.number;
-        setEmergencyCallInfo(mOwner);
+        setEmergencyCallInfo(mOwner, null);
 
         String forwardedNumber = TextUtils.isEmpty(dc.forwardedNumber) ? null : dc.forwardedNumber;
         Rlog.i(LOG_TAG, "create, forwardedNumber=" + Rlog.pii(LOG_TAG, forwardedNumber));
@@ -186,13 +186,13 @@
 
         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
         if (dialArgs.isEmergency) {
-            setEmergencyCallInfo(mOwner);
+            setEmergencyCallInfo(mOwner, null);
 
             // 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
             // non-detectable emergency call from IMS.
             if (getEmergencyNumberInfo() == null) {
-                setNonDetectableEmergencyCallInfo(dialArgs.eccCategory);
+                setNonDetectableEmergencyCallInfo(dialArgs.eccCategory, new ArrayList<String>());
             }
         }
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index c4d9a17..93a0c2f 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -470,7 +470,7 @@
     };
 
     private boolean hasCalling() {
-        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
         return mContext.getPackageManager().hasSystemFeature(
             PackageManager.FEATURE_TELEPHONY_CALLING);
     }
@@ -3039,12 +3039,12 @@
 
     @Override
     public void registerForCallWaiting(Handler h, int what, Object obj) {
-        mCT.registerForCallWaiting(h, what, obj);
+        if (mCT != null) mCT.registerForCallWaiting(h, what, obj);
     }
 
     @Override
     public void unregisterForCallWaiting(Handler h) {
-        mCT.unregisterForCallWaiting(h);
+        if (mCT != null) mCT.unregisterForCallWaiting(h);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index eafb4ba..49d1adc 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -74,6 +74,7 @@
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats;
 import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.HexDump;
@@ -746,7 +747,9 @@
         // data will be tracked when the message is processed (processMessagePart).
         if (result != Intents.RESULT_SMS_HANDLED && result != Activity.RESULT_OK) {
             mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), is3gpp2(), smsSource, result);
-            mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result);
+            mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result,
+                    TelephonyManager.from(mContext)
+                            .isEmergencyNumber(smsb.getOriginatingAddress()));
             if (mPhone != null) {
                 TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
                 if (telephonyAnalytics != null) {
@@ -1019,7 +1022,8 @@
                     + (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)");
             logeWithLocalLog(errorMsg, tracker.getMessageId());
             mPhone.getSmsStats().onIncomingSmsError(
-                    is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU);
+                    is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU,
+                    TelephonyManager.from(mContext).isEmergencyNumber(tracker.getAddress()));
             if (mPhone != null) {
                 TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
                 if (telephonyAnalytics != null) {
@@ -1048,7 +1052,9 @@
                                 SmsConstants.FORMAT_3GPP, timestamps, false,
                                 tracker.getMessageId());
                         mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(),
-                                messageCount, RESULT_SMS_NULL_MESSAGE, tracker.getMessageId());
+                                messageCount, RESULT_SMS_NULL_MESSAGE, tracker.getMessageId(),
+                                TelephonyManager.from(mContext)
+                                        .isEmergencyNumber(tracker.getAddress()));
                         return false;
                     }
                 }
@@ -1083,7 +1089,8 @@
             mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(),
                     format, timestamps, wapPushResult, tracker.getMessageId());
             mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(), messageCount,
-                    result, tracker.getMessageId());
+                    result, tracker.getMessageId(), TelephonyManager.from(mContext)
+                            .isEmergencyNumber(tracker.getAddress()));
             // result is Activity.RESULT_OK if an ordered broadcast was sent
             if (result == Activity.RESULT_OK) {
                 return true;
@@ -1103,7 +1110,11 @@
         mMetrics.writeIncomingSmsSession(mPhone.getPhoneId(), tracker.getSource(),
                 format, timestamps, block, tracker.getMessageId());
         mPhone.getSmsStats().onIncomingSmsSuccess(is3gpp2(), tracker.getSource(),
-                messageCount, block, tracker.getMessageId());
+                messageCount, block, tracker.getMessageId(),
+                TelephonyManager.from(mContext).isEmergencyNumber(tracker.getAddress()));
+        CarrierRoamingSatelliteSessionStats sessionStats =
+                CarrierRoamingSatelliteSessionStats.getInstance(mPhone.getSubId());
+        sessionStats.onIncomingSms(mPhone.getSubId());
         if (mPhone != null) {
             TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
             if (telephonyAnalytics != null) {
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 83d58af..a14ae89 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -255,19 +255,19 @@
     }
 
     private boolean hasCalling() {
-        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
         return mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_TELEPHONY_CALLING);
     }
 
     private boolean hasData() {
-        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
         return mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_TELEPHONY_DATA);
     }
 
     private boolean hasMessaging() {
-        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
         return mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_TELEPHONY_MESSAGING);
     }
@@ -645,8 +645,11 @@
         // Otherwise, if user just inserted their first SIM, or there's one primary and one
         // opportunistic subscription active (activeSubInfos.size() > 1), we automatically
         // set the primary to be default SIM and return.
-        if (mPrimarySubList.size() == 1 && (change != PRIMARY_SUB_REMOVED
-                || mActiveModemCount == 1)) {
+        boolean conditionForOnePrimarySim =
+                mFeatureFlags.resetPrimarySimDefaultValues() ? mPrimarySubList.size() == 1
+                        : mPrimarySubList.size() == 1
+                        && (change != PRIMARY_SUB_REMOVED || mActiveModemCount == 1);
+        if (conditionForOnePrimarySim) {
             int subId = mPrimarySubList.get(0);
             if (DBG) log("updateDefaultValues: to only primary sub " + subId);
             if (hasData()) mSubscriptionManagerService.setDefaultDataSubId(subId);
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 324e881..aa62acb 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -150,10 +150,10 @@
  */
 
 public abstract class Phone extends Handler implements PhoneInternalInterface {
-    private static final String LOG_TAG = "Phone";
 
     protected final static Object lockForRadioTechnologyChange = new Object();
 
+    private final String mLogTag;
     protected final int USSD_MAX_QUEUE = 10;
 
     // Key used to read and write the saved network selection numeric value
@@ -597,6 +597,7 @@
                     boolean unitTestMode, int phoneId,
                     TelephonyComponentFactory telephonyComponentFactory,
                     FeatureFlags featureFlags) {
+        mLogTag = "Phone-" + phoneId;
         mPhoneId = phoneId;
         mName = name;
         mNotifier = notifier;
@@ -638,10 +639,10 @@
          */
         mDoesRilSendMultipleCallRing = TelephonyProperties.ril_sends_multiple_call_ring()
                 .orElse(true);
-        Rlog.d(LOG_TAG, "mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
+        Rlog.d(mLogTag, "mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
 
         mCallRingDelay = TelephonyProperties.call_ring_delay().orElse(3000);
-        Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+        Rlog.d(mLogTag, "mCallRingDelay=" + mCallRingDelay);
 
         // Initialize SMS stats
         mSmsStats = new SmsStats(this);
@@ -785,7 +786,7 @@
 
         switch(msg.what) {
             case EVENT_CALL_RING:
-                Rlog.d(LOG_TAG, "Event EVENT_CALL_RING Received state=" + getState());
+                Rlog.d(mLogTag, "Event EVENT_CALL_RING Received state=" + getState());
                 ar = (AsyncResult)msg.obj;
                 if (ar.exception == null) {
                     PhoneConstants.State state = getState();
@@ -801,7 +802,7 @@
                 break;
 
             case EVENT_CALL_RING_CONTINUE:
-                Rlog.d(LOG_TAG, "Event EVENT_CALL_RING_CONTINUE Received state=" + getState());
+                Rlog.d(mLogTag, "Event EVENT_CALL_RING_CONTINUE Received state=" + getState());
                 if (getState() == PhoneConstants.State.RINGING) {
                     sendIncomingCallRingNotification(msg.arg1);
                 }
@@ -814,7 +815,7 @@
             case EVENT_INITIATE_SILENT_REDIAL:
                 // This is an ImsPhone -> GsmCdmaPhone redial
                 // See ImsPhone#initiateSilentRedial
-                Rlog.d(LOG_TAG, "Event EVENT_INITIATE_SILENT_REDIAL Received");
+                Rlog.d(mLogTag, "Event EVENT_INITIATE_SILENT_REDIAL Received");
                 ar = (AsyncResult) msg.obj;
                 if ((ar.exception == null) && (ar.result != null)) {
                     SilentRedialParam result = (SilentRedialParam) ar.result;
@@ -828,13 +829,13 @@
                         // one with a callback registered to TelephonyConnection. Notify the
                         // redial happened over that Phone so that it can be replaced with the
                         // new GSM/CDMA Connection.
-                        Rlog.d(LOG_TAG, "Notify redial connection changed cn: " + cn);
+                        Rlog.d(mLogTag, "Notify redial connection changed cn: " + cn);
                         if (mImsPhone != null) {
                             // Don't care it is null or not.
                             mImsPhone.notifyRedialConnectionChanged(cn);
                         }
                     } catch (CallStateException e) {
-                        Rlog.e(LOG_TAG, "silent redial failed: " + e);
+                        Rlog.e(mLogTag, "silent redial failed: " + e);
                         if (mImsPhone != null) {
                             mImsPhone.notifyRedialConnectionChanged(null);
                         }
@@ -847,7 +848,7 @@
                 if (ar.exception == null) {
                     handleSrvccStateChanged((int[]) ar.result);
                 } else {
-                    Rlog.e(LOG_TAG, "Srvcc exception: " + ar.exception);
+                    Rlog.e(mLogTag, "Srvcc exception: " + ar.exception);
                 }
                 break;
 
@@ -866,7 +867,7 @@
                     try {
                         mUsageSettingFromModem = ((int[]) ar.result)[0];
                     } catch (NullPointerException | ClassCastException e) {
-                        Rlog.e(LOG_TAG, "Invalid response for usage setting " + ar.result);
+                        Rlog.e(mLogTag, "Invalid response for usage setting " + ar.result);
                         break;
                     }
 
@@ -881,9 +882,9 @@
                         if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) {
                             mIsUsageSettingSupported = false;
                         }
-                        Rlog.w(LOG_TAG, "Unexpected failure to retrieve usage setting " + ce);
+                        Rlog.w(mLogTag, "Unexpected failure to retrieve usage setting " + ce);
                     } catch (ClassCastException unused) {
-                        Rlog.e(LOG_TAG, "Invalid Exception for usage setting " + ar.exception);
+                        Rlog.e(mLogTag, "Invalid Exception for usage setting " + ar.exception);
                         break; // technically extraneous, but good hygiene
                     }
                 }
@@ -896,9 +897,9 @@
                         if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) {
                             mIsUsageSettingSupported = false;
                         }
-                        Rlog.w(LOG_TAG, "Unexpected failure to set usage setting " + ce);
+                        Rlog.w(mLogTag, "Unexpected failure to set usage setting " + ce);
                     } catch (ClassCastException unused) {
-                        Rlog.e(LOG_TAG, "Invalid Exception for usage setting " + ar.exception);
+                        Rlog.e(mLogTag, "Invalid Exception for usage setting " + ar.exception);
                         break; // technically extraneous, but good hygiene
                     }
                 }
@@ -932,7 +933,7 @@
     }
 
     private void handleSrvccStateChanged(int[] ret) {
-        Rlog.d(LOG_TAG, "handleSrvccStateChanged");
+        Rlog.d(mLogTag, "handleSrvccStateChanged");
 
         ArrayList<Connection> conn = null;
         Phone imsPhone = mImsPhone;
@@ -949,7 +950,7 @@
                         conn = imsPhone.getHandoverConnection();
                         migrateFrom(imsPhone);
                     } else {
-                        Rlog.d(LOG_TAG, "HANDOVER_STARTED: mImsPhone null");
+                        Rlog.d(mLogTag, "HANDOVER_STARTED: mImsPhone null");
                     }
                     break;
                 case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
@@ -1187,7 +1188,7 @@
                     to.add((Registrant) from.get(i));
                 }
             } else {
-                Rlog.d(LOG_TAG, "msg is null");
+                Rlog.d(mLogTag, "msg is null");
             }
         }
     }
@@ -1484,7 +1485,7 @@
      */
     @UnsupportedAppUsage
     public void setNetworkSelectionModeAutomatic(Message response) {
-        Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode");
+        Rlog.d(mLogTag, "setNetworkSelectionModeAutomatic, querying current mode");
         // we don't want to do this unnecessarily - it actually causes
         // the radio to repeat network selection and is costly
         // first check if we're already in automatic mode
@@ -1519,11 +1520,11 @@
         nsm.operatorAlphaShort = "";
 
         if (doAutomatic) {
-            Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - set network selection auto");
+            Rlog.d(mLogTag, "setNetworkSelectionModeAutomatic - set network selection auto");
             Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
             mCi.setNetworkSelectionModeAutomatic(msg);
         } else {
-            Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring");
+            Rlog.d(mLogTag, "setNetworkSelectionModeAutomatic - already auto, ignoring");
             // let the calling application know that the we are ignoring automatic mode switch.
             if (nsm.message != null) {
                 nsm.message.arg1 = ALREADY_IN_AUTO_SELECTION;
@@ -1617,10 +1618,10 @@
 
             // commit and log the result.
             if (!editor.commit()) {
-                Rlog.e(LOG_TAG, "failed to commit network selection preference");
+                Rlog.e(mLogTag, "failed to commit network selection preference");
             }
         } else {
-            Rlog.e(LOG_TAG, "Cannot update network selection preference due to invalid subId " +
+            Rlog.e(mLogTag, "Cannot update network selection preference due to invalid subId " +
                     subId);
         }
     }
@@ -1631,7 +1632,7 @@
      * @param nsm PLMN info of the selected network
      */
     protected void updateManualNetworkSelection(NetworkSelectMessage nsm)  {
-        Rlog.e(LOG_TAG, "updateManualNetworkSelection() should be overridden");
+        Rlog.e(mLogTag, "updateManualNetworkSelection() should be overridden");
     }
 
     /**
@@ -1641,7 +1642,7 @@
         // look for our wrapper within the asyncresult, skip the rest if it
         // is null.
         if (!(ar.userObj instanceof NetworkSelectMessage)) {
-            Rlog.e(LOG_TAG, "unexpected result from user object.");
+            Rlog.e(mLogTag, "unexpected result from user object.");
             return;
         }
 
@@ -1705,12 +1706,12 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putInt(CLIR_KEY + getSubId(), commandInterfaceCLIRMode);
-        Rlog.i(LOG_TAG, "saveClirSetting: " + CLIR_KEY + getSubId() + "="
+        Rlog.i(mLogTag, "saveClirSetting: " + CLIR_KEY + getSubId() + "="
                 + commandInterfaceCLIRMode);
 
         // Commit and log the result.
         if (!editor.commit()) {
-            Rlog.e(LOG_TAG, "Failed to commit CLIR preference");
+            Rlog.e(mLogTag, "Failed to commit CLIR preference");
         }
     }
 
@@ -1932,13 +1933,13 @@
         IccFileHandler fh;
 
         if (uiccApplication == null) {
-            Rlog.d(LOG_TAG, "getIccFileHandler: uiccApplication == null, return null");
+            Rlog.d(mLogTag, "getIccFileHandler: uiccApplication == null, return null");
             fh = null;
         } else {
             fh = uiccApplication.getIccFileHandler();
         }
 
-        Rlog.d(LOG_TAG, "getIccFileHandler: fh=" + fh);
+        Rlog.d(mLogTag, "getIccFileHandler: fh=" + fh);
         return fh;
     }
 
@@ -2025,7 +2026,7 @@
      * Retrieves the SignalStrengthController of the phone instance.
      */
     public SignalStrengthController getSignalStrengthController() {
-        Log.wtf(LOG_TAG, "getSignalStrengthController return null.");
+        Log.wtf(mLogTag, "getSignalStrengthController return null.");
         return null;
     }
 
@@ -2059,7 +2060,7 @@
      * Update voice mail count related fields and notify listeners
      */
     public void updateVoiceMail() {
-        Rlog.e(LOG_TAG, "updateVoiceMail() should be overridden");
+        Rlog.e(mLogTag, "updateVoiceMail() should be overridden");
     }
 
     public AppType getCurrentUiccAppType() {
@@ -2185,7 +2186,7 @@
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
             status = sp.getInt(CF_STATUS + subId, IccRecords.CALL_FORWARDING_STATUS_UNKNOWN);
-            Rlog.d(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: for subId " + subId + "= " +
+            Rlog.d(mLogTag, "getCallForwardingIndicatorFromSharedPref: for subId " + subId + "= " +
                     status);
             // Check for old preference if status is UNKNOWN for current subId. This part of the
             // code is needed only when upgrading from M to N.
@@ -2199,9 +2200,9 @@
                         status = sp.getInt(CF_STATUS, IccRecords.CALL_FORWARDING_STATUS_DISABLED);
                         setCallForwardingIndicatorInSharedPref(
                                 status == IccRecords.CALL_FORWARDING_STATUS_ENABLED ? true : false);
-                        Rlog.d(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: " + status);
+                        Rlog.d(mLogTag, "getCallForwardingIndicatorFromSharedPref: " + status);
                     } else {
-                        Rlog.d(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: returning " +
+                        Rlog.d(mLogTag, "getCallForwardingIndicatorFromSharedPref: returning " +
                                 "DISABLED as status for matching subscriberId not found");
                     }
 
@@ -2213,7 +2214,7 @@
                 }
             }
         } else {
-            Rlog.e(LOG_TAG, "getCallForwardingIndicatorFromSharedPref: invalid subId " + subId);
+            Rlog.e(mLogTag, "getCallForwardingIndicatorFromSharedPref: invalid subId " + subId);
         }
         return status;
     }
@@ -2222,7 +2223,7 @@
         int status = enable ? IccRecords.CALL_FORWARDING_STATUS_ENABLED :
                 IccRecords.CALL_FORWARDING_STATUS_DISABLED;
         int subId = getSubId();
-        Rlog.i(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
+        Rlog.i(mLogTag, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
                 " in pref " + CF_STATUS + subId);
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -2265,7 +2266,7 @@
      */
     public boolean getCallForwardingIndicator() {
         if (getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
-            Rlog.e(LOG_TAG, "getCallForwardingIndicator: not possible in CDMA");
+            Rlog.e(mLogTag, "getCallForwardingIndicator: not possible in CDMA");
             return false;
         }
         IccRecords r = getIccRecords();
@@ -2276,7 +2277,7 @@
         if (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) {
             callForwardingIndicator = getCallForwardingIndicatorFromSharedPref();
         }
-        Rlog.v(LOG_TAG, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null
+        Rlog.v(mLogTag, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null
                     ? r.getVoiceCallForwardingFlag() : "null") + ", sharedPrefFlag="
                     + getCallForwardingIndicatorFromSharedPref());
         return (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_ENABLED);
@@ -2473,7 +2474,7 @@
             for (String pair : result.trim().split(",")) {
                 String[] networkTypesValues = (pair.trim().toLowerCase(Locale.ROOT)).split("=");
                 if (networkTypesValues.length != 2) {
-                    Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair);
+                    Rlog.e(mLogTag, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair);
                     continue;
                 }
                 int key = convertAllowedNetworkTypeDbNameToMapIndex(networkTypesValues[0]);
@@ -2493,7 +2494,7 @@
                 }
             }
         } catch (NumberFormatException e) {
-            Rlog.e(LOG_TAG, "allowedNetworkTypes NumberFormat exception" + e);
+            Rlog.e(mLogTag, "allowedNetworkTypes NumberFormat exception" + e);
         }
 
         for (int key : oldAllowedNetworkTypes.keySet()) {
@@ -2612,7 +2613,7 @@
     protected void updateAllowedNetworkTypes(Message response) {
         int modemRaf = getRadioAccessFamily();
         if (modemRaf == RadioAccessFamily.RAF_UNKNOWN) {
-            Rlog.d(LOG_TAG, "setPreferredNetworkType: Abort, unknown RAF: "
+            Rlog.d(mLogTag, "setPreferredNetworkType: Abort, unknown RAF: "
                     + modemRaf);
             if (response != null) {
                 CommandException ex;
@@ -2721,7 +2722,7 @@
      * @param onComplete a callback message when the action is completed
      */
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
-        Rlog.d(LOG_TAG, "unexpected setUiTTYMode method call");
+        Rlog.d(mLogTag, "unexpected setUiTTYMode method call");
     }
 
     /**
@@ -2848,7 +2849,7 @@
     public boolean eraseDataInSharedPreferences() {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
-        Rlog.d(LOG_TAG, "Erase all data saved in SharedPreferences");
+        Rlog.d(mLogTag, "Erase all data saved in SharedPreferences");
         editor.clear();
         return editor.commit();
     }
@@ -3094,7 +3095,7 @@
                     isVideoCallOrConference(mImsPhone.getBackgroundCall()) ||
                     isVideoCallOrConference(mImsPhone.getRingingCall());
         }
-        Rlog.d(LOG_TAG, "isImsVideoCallOrConferencePresent: " + isPresent);
+        Rlog.d(mLogTag, "isImsVideoCallOrConferencePresent: " + isPresent);
         return isPresent;
     }
 
@@ -3119,7 +3120,7 @@
         int subId = getSubId();
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
 
-            Rlog.d(LOG_TAG, "setVoiceMessageCount: Storing Voice Mail Count = " + countWaiting +
+            Rlog.d(mLogTag, "setVoiceMessageCount: Storing Voice Mail Count = " + countWaiting +
                     " for mVmCountKey = " + VM_COUNT + subId + " in preferences.");
 
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -3127,16 +3128,16 @@
             editor.putInt(VM_COUNT + subId, countWaiting);
             editor.apply();
         } else {
-            Rlog.e(LOG_TAG, "setVoiceMessageCount in sharedPreference: invalid subId " + subId);
+            Rlog.e(mLogTag, "setVoiceMessageCount in sharedPreference: invalid subId " + subId);
         }
         // store voice mail count in SIM
         IccRecords records = UiccController.getInstance().getIccRecords(
                 mPhoneId, UiccController.APP_FAM_3GPP);
         if (records != null) {
-            Rlog.d(LOG_TAG, "setVoiceMessageCount: updating SIM Records");
+            Rlog.d(mLogTag, "setVoiceMessageCount: updating SIM Records");
             records.setVoiceMessageWaiting(1, countWaiting);
         } else {
-            Rlog.d(LOG_TAG, "setVoiceMessageCount: SIM Records not found");
+            Rlog.d(mLogTag, "setVoiceMessageCount: SIM Records not found");
         }
         // notify listeners of voice mail
         notifyMessageWaitingIndicator();
@@ -3152,7 +3153,7 @@
             int countFromSP = sp.getInt(VM_COUNT + subId, invalidCount);
             if (countFromSP != invalidCount) {
                 countVoiceMessages = countFromSP;
-                Rlog.d(LOG_TAG, "getStoredVoiceMessageCount: from preference for subId " + subId +
+                Rlog.d(mLogTag, "getStoredVoiceMessageCount: from preference for subId " + subId +
                         "= " + countVoiceMessages);
             } else {
                 // Check for old preference if count not found for current subId. This part of the
@@ -3165,10 +3166,10 @@
                         // get voice mail count from preferences
                         countVoiceMessages = sp.getInt(VM_COUNT, 0);
                         setVoiceMessageCount(countVoiceMessages);
-                        Rlog.d(LOG_TAG, "getStoredVoiceMessageCount: from preference = " +
+                        Rlog.d(mLogTag, "getStoredVoiceMessageCount: from preference = " +
                                 countVoiceMessages);
                     } else {
-                        Rlog.d(LOG_TAG, "getStoredVoiceMessageCount: returning 0 as count for " +
+                        Rlog.d(mLogTag, "getStoredVoiceMessageCount: returning 0 as count for " +
                                 "matching subscriberId not found");
 
                     }
@@ -3180,7 +3181,7 @@
                 }
             }
         } else {
-            Rlog.e(LOG_TAG, "getStoredVoiceMessageCount: invalid subId " + subId);
+            Rlog.e(mLogTag, "getStoredVoiceMessageCount: invalid subId " + subId);
         }
         return countVoiceMessages;
     }
@@ -3713,17 +3714,17 @@
                             }
                         }
                     } catch (NumberFormatException e) {
-                        Rlog.e(LOG_TAG, "Exception in getProvisioningUrlBaseFromFile: " + e);
+                        Rlog.e(mLogTag, "Exception in getProvisioningUrlBaseFromFile: " + e);
                     }
                 }
             }
             return null;
         } catch (FileNotFoundException e) {
-            Rlog.e(LOG_TAG, "Carrier Provisioning Urls file not found");
+            Rlog.e(mLogTag, "Carrier Provisioning Urls file not found");
         } catch (XmlPullParserException e) {
-            Rlog.e(LOG_TAG, "Xml parser exception reading Carrier Provisioning Urls file: " + e);
+            Rlog.e(mLogTag, "Xml parser exception reading Carrier Provisioning Urls file: " + e);
         } catch (IOException e) {
-            Rlog.e(LOG_TAG, "I/O exception reading Carrier Provisioning Urls file: " + e);
+            Rlog.e(mLogTag, "I/O exception reading Carrier Provisioning Urls file: " + e);
         }
         return null;
     }
@@ -3735,9 +3736,9 @@
         String url = getProvisioningUrlBaseFromFile();
         if (TextUtils.isEmpty(url)) {
             url = mContext.getResources().getString(R.string.mobile_provisioning_url);
-            Rlog.d(LOG_TAG, "getMobileProvisioningUrl: url from resource =" + url);
+            Rlog.d(mLogTag, "getMobileProvisioningUrl: url from resource =" + url);
         } else {
-            Rlog.d(LOG_TAG, "getMobileProvisioningUrl: url from File =" + url);
+            Rlog.d(mLogTag, "getMobileProvisioningUrl: url from File =" + url);
         }
         // Populate the iccid, imei and phone number in the provisioning url.
         if (!TextUtils.isEmpty(url)) {
@@ -3811,7 +3812,7 @@
      * version scoped to their packages
      */
     public void notifyNewRingingConnectionP(Connection cn) {
-        Rlog.i(LOG_TAG, String.format(
+        Rlog.i(mLogTag, String.format(
                 "notifyNewRingingConnection: phoneId=[%d], connection=[%s], registrants=[%s]",
                 getPhoneId(), cn, getNewRingingConnectionRegistrantsAsString()));
         if (!mIsVoiceCapable)
@@ -3867,12 +3868,12 @@
     private void sendIncomingCallRingNotification(int token) {
         if (mIsVoiceCapable && !mDoesRilSendMultipleCallRing &&
                 (token == mCallRingContinueToken)) {
-            Rlog.d(LOG_TAG, "Sending notifyIncomingRing");
+            Rlog.d(mLogTag, "Sending notifyIncomingRing");
             notifyIncomingRing();
             sendMessageDelayed(
                     obtainMessage(EVENT_CALL_RING_CONTINUE, token, 0), mCallRingDelay);
         } else {
-            Rlog.d(LOG_TAG, "Ignoring ring notification request,"
+            Rlog.d(mLogTag, "Ignoring ring notification request,"
                     + " mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing
                     + " token=" + token
                     + " mCallRingContinueToken=" + mCallRingContinueToken
@@ -3912,7 +3913,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public IsimRecords getIsimRecords() {
-        Rlog.e(LOG_TAG, "getIsimRecords() is only supported on LTE devices");
+        Rlog.e(mLogTag, "getIsimRecords() is only supported on LTE devices");
         return null;
     }
 
@@ -3945,7 +3946,7 @@
      */
     public void setVoiceMessageWaiting(int line, int countWaiting) {
         // This function should be overridden by class GsmCdmaPhone.
-        Rlog.e(LOG_TAG, "Error! This function should never be executed, inactive Phone.");
+        Rlog.e(mLogTag, "Error! This function should never be executed, inactive Phone.");
     }
 
     /**
@@ -4204,7 +4205,7 @@
                 isImsRegistered = sst.isImsRegistered();
             }
         }
-        Rlog.d(LOG_TAG, "isImsRegistered =" + isImsRegistered);
+        Rlog.d(mLogTag, "isImsRegistered =" + isImsRegistered);
         return isImsRegistered;
     }
 
@@ -4218,7 +4219,7 @@
         if (imsPhone != null) {
             isWifiCallingEnabled = imsPhone.isWifiCallingEnabled();
         }
-        Rlog.d(LOG_TAG, "isWifiCallingEnabled =" + isWifiCallingEnabled);
+        Rlog.d(mLogTag, "isWifiCallingEnabled =" + isWifiCallingEnabled);
         return isWifiCallingEnabled;
     }
 
@@ -4232,7 +4233,7 @@
         if (imsPhone != null) {
             isAvailable = imsPhone.isImsCapabilityAvailable(capability, regTech);
         }
-        Rlog.d(LOG_TAG, "isImsCapabilityAvailable, capability=" + capability + ", regTech="
+        Rlog.d(mLogTag, "isImsCapabilityAvailable, capability=" + capability + ", regTech="
                 + regTech + ", isAvailable=" + isAvailable);
         return isAvailable;
     }
@@ -4256,7 +4257,7 @@
         if (imsPhone != null) {
             isVolteEnabled = imsPhone.isVoiceOverCellularImsEnabled();
         }
-        Rlog.d(LOG_TAG, "isVoiceOverCellularImsEnabled=" + isVolteEnabled);
+        Rlog.d(mLogTag, "isVoiceOverCellularImsEnabled=" + isVolteEnabled);
         return isVolteEnabled;
     }
 
@@ -4270,7 +4271,7 @@
         if (imsPhone != null) {
             regTech = imsPhone.getImsRegistrationTech();
         }
-        Rlog.d(LOG_TAG, "getImsRegistrationTechnology =" + regTech);
+        Rlog.d(mLogTag, "getImsRegistrationTechnology =" + regTech);
         return regTech;
     }
 
@@ -4852,7 +4853,7 @@
     public boolean isDeviceIdle() {
         DeviceStateMonitor dsm = getDeviceStateMonitor();
         if (dsm == null) {
-            Rlog.e(LOG_TAG, "isDeviceIdle: DeviceStateMonitor is null");
+            Rlog.e(mLogTag, "isDeviceIdle: DeviceStateMonitor is null");
             return false;
         }
         return !dsm.shouldEnableHighPowerConsumptionIndications();
@@ -4866,7 +4867,7 @@
     public void notifyDeviceIdleStateChanged(boolean isIdle) {
         SignalStrengthController ssc = getSignalStrengthController();
         if (ssc == null) {
-            Rlog.e(LOG_TAG, "notifyDeviceIdleStateChanged: SignalStrengthController is null");
+            Rlog.e(mLogTag, "notifyDeviceIdleStateChanged: SignalStrengthController is null");
             return;
         }
         ssc.onDeviceIdleStateChanged(isIdle);
@@ -4915,15 +4916,15 @@
     /**
      * @return The data network controller
      */
-    public @Nullable DataNetworkController getDataNetworkController() {
+    public @NonNull DataNetworkController getDataNetworkController() {
         return mDataNetworkController;
     }
 
     /**
      * @return The data settings manager
      */
-    public @Nullable DataSettingsManager getDataSettingsManager() {
-        if (mDataNetworkController == null) return null;
+    @NonNull
+    public DataSettingsManager getDataSettingsManager() {
         return mDataNetworkController.getDataSettingsManager();
     }
 
@@ -5298,7 +5299,7 @@
      * @param type for callback mode entry.
      */
     public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) {
-        Rlog.d(LOG_TAG, "startCallbackMode:type=" + type);
+        Rlog.d(mLogTag, "startCallbackMode:type=" + type);
         mNotifier.notifyCallbackModeStarted(this, type);
     }
 
@@ -5309,7 +5310,7 @@
      */
     public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
             @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
-        Rlog.d(LOG_TAG, "stopCallbackMode:type=" + type + ", reason=" + reason);
+        Rlog.d(mLogTag, "stopCallbackMode:type=" + type + ", reason=" + reason);
         mNotifier.notifyCallbackModeStopped(this, type, reason);
     }
 
@@ -5347,7 +5348,7 @@
         pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
         pw.println(" getUnitTestMode()=" + getUnitTestMode());
         pw.println(" getState()=" + getState());
-        pw.println(" getIccSerialNumber()=" + Rlog.pii(LOG_TAG, getIccSerialNumber()));
+        pw.println(" getIccSerialNumber()=" + Rlog.pii(mLogTag, getIccSerialNumber()));
         pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded());
         pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator());
         pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator());
@@ -5532,18 +5533,14 @@
     }
 
     private void logd(String s) {
-        Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
+        Rlog.d(mLogTag, "[" + mPhoneId + "] " + s);
     }
 
     private void logi(String s) {
-        Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
+        Rlog.i(mLogTag, "[" + mPhoneId + "] " + s);
     }
 
     private void loge(String s) {
-        Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
-    }
-
-    private static String pii(String s) {
-        return Rlog.pii(LOG_TAG, s);
+        Rlog.e(mLogTag, "[" + mPhoneId + "] " + s);
     }
 }
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 9a8a297..8abebe2 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -466,16 +466,19 @@
     private final class BinderServiceDeathRecipient implements IBinder.DeathRecipient {
         private IBinder mBinder;
         private final int mService;
+        private long mLinkedFlags;
 
         BinderServiceDeathRecipient(int service) {
             mService = service;
+            mLinkedFlags = 0;
         }
 
         public void linkToDeath(IBinder service) throws RemoteException {
             if (service != null) {
                 riljLog("Linked to death for service " + serviceToString(mService));
                 mBinder = service;
-                mBinder.linkToDeath(this, (int) mServiceCookies.get(mService).incrementAndGet());
+                mLinkedFlags = mServiceCookies.get(mService).incrementAndGet();
+                mBinder.linkToDeath(this, (int) mLinkedFlags);
             } else {
                 riljLoge("Unable to link to death for service " + serviceToString(mService));
             }
@@ -483,8 +486,9 @@
 
         public synchronized void unlinkToDeath() {
             if (mBinder != null) {
-                mBinder.unlinkToDeath(this, 0);
+                mBinder.unlinkToDeath(this, (int) mLinkedFlags);
                 mBinder = null;
+                mLinkedFlags = 0;
             }
         }
 
@@ -494,10 +498,10 @@
             if (mFeatureFlags.combineRilDeathHandle()) {
                 mRilHandler.sendMessageAtFrontOfQueue(mRilHandler.obtainMessage(
                         EVENT_AIDL_PROXY_DEAD, mService, 0 /* ignored arg2 */,
-                        mServiceCookies.get(mService).get()));
+                        mLinkedFlags));
             } else {
                 mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_AIDL_PROXY_DEAD, mService,
-                        0 /* ignored arg2 */, mServiceCookies.get(mService).get()));
+                        0 /* ignored arg2 */, mLinkedFlags));
             }
             unlinkToDeath();
         }
@@ -811,9 +815,12 @@
     public synchronized RadioServiceProxy getRadioServiceProxy(int service) {
         if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service);
         if ((service >= HAL_SERVICE_IMS) && !isRadioServiceSupported(service)) {
-            riljLogw("getRadioServiceProxy: " + serviceToString(service) + " for "
-                    + HIDL_SERVICE_NAME[mPhoneId] + " is not supported\n"
-                    + android.util.Log.getStackTraceString(new RuntimeException()));
+            // Suppress the excessive logging for HAL_SERVICE_IMS when not supported.
+            if (service != HAL_SERVICE_IMS) {
+                riljLogw("getRadioServiceProxy: " + serviceToString(service) + " for "
+                        + HIDL_SERVICE_NAME[mPhoneId] + " is not supported\n"
+                        + android.util.Log.getStackTraceString(new RuntimeException()));
+            }
             return mServiceProxies.get(service);
         }
         if (!mIsCellularSupported) {
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index 7fc499e..077ee0b 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -30,6 +30,7 @@
 import android.os.UserManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
@@ -243,7 +244,8 @@
                             message.mMessageCount);
                     if (phone != null) {
                         phone.getSmsStats().onDroppedIncomingMultipartSms(message.mIs3gpp2, rows,
-                                message.mMessageCount);
+                                message.mMessageCount, TelephonyManager.from(context)
+                                        .isEmergencyNumber(message.mAddress));
                         TelephonyAnalytics telephonyAnalytics = phone.getTelephonyAnalytics();
                         if (telephonyAnalytics != null) {
                             SmsMmsAnalytics smsMmsAnalytics =
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 136e997..bc1e1a8 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -781,22 +781,16 @@
             if (isSmsDomainSelectionEnabled()) {
                 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
                 boolean isEmergency = tm.isEmergencyNumber(tracker.mDestAddress);
-                DomainSelectionConnectionHolder holder = getDomainSelectionConnection(isEmergency);
-
-                // If the DomainSelectionConnection is not available,
-                // fallback to the legacy implementation.
-                if (holder != null && holder.getConnection() != null) {
-                    // This may be invoked by another thread, so this operation is posted and
-                    // handled through the execution flow of SmsDispatchersController.
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = holder;
-                    args.arg2 = new PendingRequest(PendingRequest.TYPE_RETRY_SMS, tracker,
-                            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));
-                    return;
-                }
+                // This may be invoked by another thread, so this operation is posted and
+                // handled through the execution flow of SmsDispatchersController.
+                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,
+                        0, false, 0, 0L, false);
+                args.arg3 = "sendRetrySms";
+                sendMessage(obtainMessage(EVENT_REQUEST_DOMAIN_SELECTION, args));
+                return;
             }
 
             if (mImsSmsDispatcher.isAvailable()) {
@@ -1007,52 +1001,23 @@
      * Returns a {@link DomainSelectionConnectionHolder} according to the flag specified.
      *
      * @param emergency The flag to indicate that the domain selection is for an emergency SMS.
-     * @return A {@link DomainSelectionConnectionHolder} instance or null.
+     * @return A {@link DomainSelectionConnectionHolder} instance.
      */
     @VisibleForTesting
     @Nullable
     protected DomainSelectionConnectionHolder getDomainSelectionConnectionHolder(
             boolean emergency) {
-        return emergency ? mEmergencyDscHolder : mDscHolder;
-    }
-
-    /**
-     * Returns a {@link DomainSelectionConnectionHolder} if the domain selection supports,
-     * return null otherwise.
-     *
-     * @param emergency The flag to indicate that the domain selection is for an emergency SMS.
-     * @return A {@link DomainSelectionConnectionHolder} that grabs the
-     *         {@link DomainSelectionConnection} and its related information to use the domain
-     *         selection architecture.
-     */
-    private DomainSelectionConnectionHolder getDomainSelectionConnection(boolean emergency) {
-        DomainSelectionConnectionHolder holder = getDomainSelectionConnectionHolder(emergency);
-        DomainSelectionConnection connection = (holder != null) ? holder.getConnection() : null;
-
-        if (connection == null) {
-            connection = mDomainSelectionResolverProxy.getDomainSelectionConnection(
-                    mPhone, DomainSelectionService.SELECTOR_TYPE_SMS, emergency);
-
-            if (connection == null) {
-                // Domain selection architecture is not supported.
-                // Use the legacy architecture.
-                return null;
+        if (emergency) {
+            if (mEmergencyDscHolder == null) {
+                mEmergencyDscHolder = new DomainSelectionConnectionHolder(emergency);
             }
-        }
-
-        if (holder == null) {
-            holder = new DomainSelectionConnectionHolder(emergency);
-
-            if (emergency) {
-                mEmergencyDscHolder = holder;
-            } else {
-                mDscHolder = holder;
+            return mEmergencyDscHolder;
+        } else {
+            if (mDscHolder == null) {
+                mDscHolder = new DomainSelectionConnectionHolder(emergency);
             }
+            return mDscHolder;
         }
-
-        holder.setConnection(connection);
-
-        return holder;
     }
 
     /**
@@ -1102,6 +1067,8 @@
      *
      * @param holder The {@link DomainSelectionConnectionHolder} that contains the
      *               {@link DomainSelectionConnection} and its related information.
+     * @param request The {@link PendingRequest} that stores the SMS request
+     *                (data, text, multipart text) to be sent.
      * @param logTag The log string.
      */
     private void requestDomainSelection(@NonNull DomainSelectionConnectionHolder holder,
@@ -1111,6 +1078,21 @@
         // the domain selection by adding this request to the pending list.
         holder.addRequest(request);
 
+        if (holder.getConnection() == null) {
+            DomainSelectionConnection connection =
+                    mDomainSelectionResolverProxy.getDomainSelectionConnection(
+                            mPhone, DomainSelectionService.SELECTOR_TYPE_SMS, holder.isEmergency());
+            if (connection == null) {
+                logd("requestDomainSelection: fallback for " + logTag);
+                // If the domain selection connection is not available,
+                // fallback to the legacy implementation.
+                sendAllPendingRequests(holder, NetworkRegistrationInfo.DOMAIN_UNKNOWN);
+                return;
+            } else {
+                holder.setConnection(connection);
+            }
+        }
+
         if (!isDomainSelectionRequested) {
             if (VDBG) {
                 logd("requestDomainSelection: " + logTag);
@@ -1571,19 +1553,13 @@
         }
 
         if (isSmsDomainSelectionEnabled()) {
-            DomainSelectionConnectionHolder holder = getDomainSelectionConnection(false);
-
-            // If the DomainSelectionConnection is not available,
-            // fallback to the legacy implementation.
-            if (holder != null && holder.getConnection() != null) {
-                sendSmsUsingDomainSelection(holder,
-                        new PendingRequest(PendingRequest.TYPE_DATA, null, callingPackage,
-                                destAddr, scAddr, asArrayList(sentIntent),
-                                asArrayList(deliveryIntent), isForVvm, data, destPort, null, null,
-                                false, 0, false, 0, 0L, false),
-                        "sendData");
-                return;
-            }
+            sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(false),
+                    new PendingRequest(PendingRequest.TYPE_DATA, null, callingPackage,
+                            destAddr, scAddr, asArrayList(sentIntent),
+                            asArrayList(deliveryIntent), isForVvm, data, destPort, null, null,
+                            false, 0, false, 0, 0L, false),
+                    "sendData");
+            return;
         }
 
         if (mImsSmsDispatcher.isAvailable()) {
@@ -1812,20 +1788,14 @@
         if (isSmsDomainSelectionEnabled()) {
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
             boolean isEmergency = tm.isEmergencyNumber(destAddr);
-            DomainSelectionConnectionHolder holder = getDomainSelectionConnection(isEmergency);
-
-            // If the DomainSelectionConnection is not available,
-            // fallback to the legacy implementation.
-            if (holder != null && holder.getConnection() != null) {
-                sendSmsUsingDomainSelection(holder,
-                        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");
-                return;
-            }
+            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");
+            return;
         }
 
         if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
@@ -1961,19 +1931,13 @@
         if (isSmsDomainSelectionEnabled()) {
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
             boolean isEmergency = tm.isEmergencyNumber(destAddr);
-            DomainSelectionConnectionHolder holder = getDomainSelectionConnection(isEmergency);
-
-            // If the DomainSelectionConnection is not available,
-            // fallback to the legacy implementation.
-            if (holder != null && holder.getConnection() != null) {
-                sendSmsUsingDomainSelection(holder,
-                        new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null,
-                                callingPkg, destAddr, scAddr, sentIntents, deliveryIntents, false,
-                                null, 0, parts, messageUri, persistMessage, priority, expectMore,
-                                validityPeriod, messageId, false),
-                        "sendMultipartText");
-                return;
-            }
+            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");
+            return;
         }
 
         if (mImsSmsDispatcher.isAvailable()) {
diff --git a/src/java/com/android/internal/telephony/TelephonyCapabilities.java b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
index 1b4a3a9..71d3b14 100644
--- a/src/java/com/android/internal/telephony/TelephonyCapabilities.java
+++ b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -16,9 +16,12 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.os.SystemProperties;
 
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
 
 /**
@@ -194,4 +197,16 @@
     public static boolean canDistinguishDialingAndConnected(int phoneType) {
         return phoneType == PhoneConstants.PHONE_TYPE_GSM;
     }
+
+    /**
+     * Returns true if Calling/Data/Messaging features should be checked on this device.
+     */
+    public static boolean minimalTelephonyCdmCheck(@NonNull FeatureFlags featureFlags) {
+        // Check SDK version of the vendor partition.
+        final int vendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
+        if (vendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) return false;
+
+        return featureFlags.minimalTelephonyCdmCheck();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java b/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java
index 85d5a35..85413f5 100644
--- a/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java
+++ b/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java
@@ -27,6 +27,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.satellite.SatelliteConfig;
 import com.android.internal.telephony.satellite.SatelliteConfigParser;
+import com.android.internal.telephony.satellite.SatelliteConstants;
+import com.android.internal.telephony.satellite.metrics.ConfigUpdaterMetricsStats;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.server.updates.ConfigUpdateInstallReceiver;
 
@@ -57,6 +59,7 @@
     private final Object mConfigParserLock = new Object();
     @GuardedBy("mConfigParserLock")
     private ConfigParser mConfigParser;
+    @NonNull private final ConfigUpdaterMetricsStats mConfigUpdaterMetricsStats;
 
 
     public static TelephonyConfigUpdateInstallReceiver sReceiverAdaptorInstance =
@@ -72,6 +75,7 @@
 
     public TelephonyConfigUpdateInstallReceiver() {
         super(UPDATE_DIR, NEW_CONFIG_CONTENT_PATH, UPDATE_METADATA_PATH, VERSION);
+        mConfigUpdaterMetricsStats = ConfigUpdaterMetricsStats.getOrCreateInstance();
     }
 
     /**
@@ -97,6 +101,8 @@
         SatelliteConfig satelliteConfig = (SatelliteConfig) parser.getConfig();
         if (satelliteConfig == null) {
             Log.e(TAG, "satelliteConfig is null");
+            mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
+                    SatelliteConstants.CONFIG_UPDATE_RESULT_NO_SATELLITE_DATA);
             return false;
         }
 
@@ -109,12 +115,16 @@
             for (String plmn : plmns) {
                 if (!TelephonyUtils.isValidPlmn(plmn)) {
                     Log.e(TAG, "found invalid plmn : " + plmn);
+                    mConfigUpdaterMetricsStats.reportCarrierConfigError(
+                            SatelliteConstants.CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_PLMN);
                     return false;
                 }
                 Set<Integer> serviceSet = plmnsServices.get(plmn);
                 for (int service : serviceSet) {
                     if (!TelephonyUtils.isValidService(service)) {
                         Log.e(TAG, "found invalid service : " + service);
+                        mConfigUpdaterMetricsStats.reportCarrierConfigError(SatelliteConstants
+                                .CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_SUPPORTED_SERVICES);
                         return false;
                     }
                 }
@@ -149,8 +159,11 @@
                 int previousVersion = getInstance().mConfigParser.mVersion;
                 Log.d(TAG, "previous version is " + previousVersion + " | updated version is "
                         + updatedVersion);
+                mConfigUpdaterMetricsStats.setConfigVersion(updatedVersion);
                 if (updatedVersion <= previousVersion) {
                     Log.e(TAG, "updatedVersion is smaller than previousVersion");
+                    mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
+                            SatelliteConstants.CONFIG_UPDATE_RESULT_INVALID_VERSION);
                     return;
                 }
             }
@@ -167,6 +180,8 @@
 
         if (!copySourceFileToTargetFile(NEW_CONFIG_CONTENT_PATH, VALID_CONFIG_CONTENT_PATH)) {
             Log.e(TAG, "fail to copy to the valid satellite carrier config data");
+            mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
+                    SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
         }
     }
 
@@ -231,6 +246,8 @@
     public ConfigParser getNewConfigParser(String domain, @Nullable byte[] data) {
         if (data == null) {
             Log.d(TAG, "content data is null");
+            mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
+                    SatelliteConstants.CONFIG_UPDATE_RESULT_NO_DATA);
             return null;
         }
         switch (domain) {
@@ -238,6 +255,8 @@
                 return new SatelliteConfigParser(data);
             default:
                 Log.e(TAG, "DOMAIN should be specified");
+                mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
+                        SatelliteConstants.CONFIG_UPDATE_RESULT_INVALID_DOMAIN);
                 return null;
         }
     }
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
index 1d720ac..bc684af 100644
--- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -148,7 +148,9 @@
         @ApnType
         public final int apnType;
         // The qualified networks in preferred order. Each network is a AccessNetworkType.
-        public final @NonNull @RadioAccessNetworkType int[] qualifiedNetworks;
+        @NonNull
+        @RadioAccessNetworkType
+        public final int[] qualifiedNetworks;
         public QualifiedNetworks(@ApnType int apnType, @NonNull int[] qualifiedNetworks) {
             this.apnType = apnType;
             this.qualifiedNetworks = Arrays.stream(qualifiedNetworks)
@@ -313,7 +315,7 @@
 
         /**
          * Called when QualifiedNetworksService requests network validation.
-         *
+         * <p>
          * Since the data network in the connected state corresponding to the given network
          * capability must be validated, a request is tossed to the data network controller.
          * @param networkCapability network capability
@@ -429,7 +431,8 @@
             mPhone.getDataNetworkController().getDataRetryManager().registerCallback(
                     new DataRetryManager.DataRetryManagerCallback(this::post) {
                         @Override
-                        public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
+                        public void onThrottleStatusChanged(
+                                @NonNull List<ThrottleStatus> throttleStatuses) {
                             try {
                                 logl("onThrottleStatusChanged: " + throttleStatuses);
                                 if (mIQualifiedNetworksService != null) {
@@ -471,7 +474,7 @@
      */
     private void bindQualifiedNetworksService() {
         post(() -> {
-            Intent intent = null;
+            Intent intent;
             String packageName = getQualifiedNetworksServicePackageName();
             String className = getQualifiedNetworksServiceClassName();
 
@@ -538,7 +541,7 @@
             b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
                     CarrierConfigManager
                             .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
-            if (b != null && !b.isEmpty()) {
+            if (!b.isEmpty()) {
                 // If carrier config overrides it, use the one from carrier config
                 String carrierConfigPackageName = b.getString(CarrierConfigManager
                         .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
@@ -569,7 +572,7 @@
             b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
                     CarrierConfigManager
                             .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
-            if (b != null && !b.isEmpty()) {
+            if (!b.isEmpty()) {
                 // If carrier config overrides it, use the one from carrier config
                 String carrierConfigClassName = b.getString(CarrierConfigManager
                         .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
@@ -585,7 +588,8 @@
         return className;
     }
 
-    private @NonNull List<QualifiedNetworks> getQualifiedNetworksList() {
+    @NonNull
+    private List<QualifiedNetworks> getQualifiedNetworksList() {
         List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
         for (int i = 0; i < mAvailableNetworks.size(); i++) {
             qualifiedNetworksList.add(new QualifiedNetworks(mAvailableNetworks.keyAt(i),
@@ -617,11 +621,13 @@
     /**
      * @return The available transports.
      */
-    public @NonNull int[] getAvailableTransports() {
+    @NonNull
+    public int[] getAvailableTransports() {
         return mAvailableTransports;
     }
 
-    private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) {
+    @TransportType
+    private static int getTransportFromAccessNetwork(int accessNetwork) {
         return accessNetwork == AccessNetworkType.IWLAN
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
@@ -654,7 +660,8 @@
      * @param apnType APN type
      * @return The preferred transport.
      */
-    public @TransportType int getPreferredTransport(@ApnType int apnType) {
+    @TransportType
+    public int getPreferredTransport(@ApnType int apnType) {
         return mPreferredTransports.get(apnType) == null
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mPreferredTransports.get(apnType);
     }
@@ -666,8 +673,8 @@
      * supported.)
      * @return The preferred transport.
      */
-    public @TransportType int getPreferredTransportByNetworkCapability(
-            @NetCapability int networkCapability) {
+    @TransportType
+    public int getPreferredTransportByNetworkCapability(@NetCapability int networkCapability) {
         int apnType = DataUtils.networkCapabilityToApnType(networkCapability);
         // For non-APN type capabilities, always route to WWAN.
         if (apnType == ApnSetting.TYPE_NONE) {
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 10371ab..7486b61 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -473,6 +473,7 @@
     @Override
     public void handleMessage(@NonNull Message msg) {
         AsyncResult ar;
+        Object obj;
         int phoneId;
         switch (msg.what) {
             case EVENT_SERVICE_STATE_CHANGED:
@@ -491,33 +492,20 @@
                 onSignalStrengthChanged(phoneId);
                 break;
             case EVENT_EVALUATE_AUTO_SWITCH:
-                if (sFeatureFlags.autoDataSwitchRatSs()) {
-                    Object obj = mScheduledEventsToExtras.get(EVENT_EVALUATE_AUTO_SWITCH);
-                    if (obj instanceof EvaluateEventExtra extra) {
-                        mScheduledEventsToExtras.remove(EVENT_EVALUATE_AUTO_SWITCH);
-                        onEvaluateAutoDataSwitch(extra.evaluateReason);
-                    }
-                } else {
-                    int reason = (int) msg.obj;
-                    onEvaluateAutoDataSwitch(reason);
+                obj = mScheduledEventsToExtras.get(EVENT_EVALUATE_AUTO_SWITCH);
+                if (obj instanceof EvaluateEventExtra extra) {
+                    mScheduledEventsToExtras.remove(EVENT_EVALUATE_AUTO_SWITCH);
+                    onEvaluateAutoDataSwitch(extra.evaluateReason);
                 }
                 break;
             case EVENT_STABILITY_CHECK_PASSED:
-                if (sFeatureFlags.autoDataSwitchRatSs()) {
-                    Object obj = mScheduledEventsToExtras.get(EVENT_STABILITY_CHECK_PASSED);
-                    if (obj instanceof StabilityEventExtra extra) {
-                        int targetPhoneId = extra.targetPhoneId;
-                        boolean needValidation = extra.needValidation;
-                        log("require validation on phone " + targetPhoneId
-                                + (needValidation ? "" : " no") + " need to pass");
-                        mScheduledEventsToExtras.remove(EVENT_STABILITY_CHECK_PASSED);
-                        mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
-                    }
-                } else {
-                    int targetPhoneId = msg.arg1;
-                    boolean needValidation = msg.arg2 == 1;
+                obj = mScheduledEventsToExtras.get(EVENT_STABILITY_CHECK_PASSED);
+                if (obj instanceof StabilityEventExtra extra) {
+                    int targetPhoneId = extra.targetPhoneId;
+                    boolean needValidation = extra.needValidation;
                     log("require validation on phone " + targetPhoneId
                             + (needValidation ? "" : " no") + " need to pass");
+                    mScheduledEventsToExtras.remove(EVENT_STABILITY_CHECK_PASSED);
                     mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
                 }
                 break;
@@ -643,15 +631,9 @@
                 ? mAutoDataSwitchAvailabilityStabilityTimeThreshold
                 << mAutoSwitchValidationFailedCount
                 : 0;
-        if (sFeatureFlags.autoDataSwitchRatSs()) {
-            if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) {
-                scheduleEventWithTimer(EVENT_EVALUATE_AUTO_SWITCH, new EvaluateEventExtra(reason),
-                        delayMs);
-            }
-        } else {
-            if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
-                sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH, reason), delayMs);
-            }
+        if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) {
+            scheduleEventWithTimer(EVENT_EVALUATE_AUTO_SWITCH, new EvaluateEventExtra(reason),
+                    delayMs);
         }
     }
 
@@ -700,12 +682,32 @@
                 return;
             }
 
-            if (!defaultDataPhone.isUserDataEnabled() || !backupDataPhone.isDataAllowed()) {
-                mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
-                        EVALUATION_REASON_DATA_SETTINGS_CHANGED);
-                log(debugMessage.append(", immediately back to default as user turns off settings")
-                        .toString());
-                return;
+            DataEvaluation internetEvaluation;
+            if (sFeatureFlags.autoDataSwitchUsesDataEnabled()) {
+                if (!defaultDataPhone.isUserDataEnabled()) {
+                    mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+                            EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+                    log(debugMessage.append(
+                            ", immediately back to default as user turns off default").toString());
+                    return;
+                } else if (!(internetEvaluation = backupDataPhone.getDataNetworkController()
+                        .getInternetEvaluation(false/*ignoreExistingNetworks*/))
+                        .isSubsetOf(DataEvaluation.DataDisallowedReason.NOT_IN_SERVICE)) {
+                    mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone(
+                            DEFAULT_PHONE_INDEX, EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+                    log(debugMessage.append(
+                                    ", immediately back to default because backup ")
+                            .append(internetEvaluation).toString());
+                    return;
+                }
+            } else {
+                if (!defaultDataPhone.isUserDataEnabled() || !backupDataPhone.isDataAllowed()) {
+                    mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+                            EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+                    log(debugMessage.append(
+                            ", immediately back to default as user turns off settings").toString());
+                    return;
+                }
             }
 
             boolean backToDefault = false;
@@ -915,12 +917,15 @@
             }
 
             if (secondaryDataPhone != null) {
-                // check auto switch feature enabled
-                if (secondaryDataPhone.isDataAllowed()) {
+                // check internet data is allowed on the candidate
+                DataEvaluation internetEvaluation = secondaryDataPhone.getDataNetworkController()
+                        .getInternetEvaluation(false/*ignoreExistingNetworks*/);
+                if (!internetEvaluation.containsDisallowedReasons()) {
                     return new StabilityEventExtra(phoneId,
                             isForPerformance, mRequirePingTestBeforeSwitch);
                 } else {
-                    debugMessage.append(", but candidate's data is not allowed");
+                    debugMessage.append(", but candidate's data is not allowed ")
+                            .append(internetEvaluation);
                 }
             }
         }
@@ -932,8 +937,7 @@
      * @return {@code true} If the feature of switching base on RAT and signal strength is enabled.
      */
     private boolean isRatSignalStrengthBasedSwitchEnabled() {
-        return sFeatureFlags.autoDataSwitchRatSs() && mScoreTolerance >= 0
-                && mAutoDataSwitchPerformanceStabilityTimeThreshold >= 0;
+        return mScoreTolerance >= 0 && mAutoDataSwitchPerformanceStabilityTimeThreshold >= 0;
     }
 
     /**
@@ -953,39 +957,27 @@
      */
     private void startStabilityCheck(int targetPhoneId, boolean isForPerformance,
             boolean needValidation) {
-        String combinationIdentifier = targetPhoneId + "" + needValidation;
-        if (sFeatureFlags.autoDataSwitchRatSs()) {
-            StabilityEventExtra eventExtras = (StabilityEventExtra)
-                    mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED,
-                            new StabilityEventExtra(INVALID_PHONE_INDEX, false /*need validation*/,
-                            false /*isForPerformance*/));
-            long delayMs = -1;
-            // Check if already scheduled one with that combination of extras.
-            if (eventExtras.targetPhoneId != targetPhoneId
-                    || eventExtras.needValidation != needValidation
-                    || eventExtras.isForPerformance != isForPerformance) {
-                eventExtras =
-                        new StabilityEventExtra(targetPhoneId, isForPerformance, needValidation);
+        StabilityEventExtra eventExtras = (StabilityEventExtra)
+                mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED,
+                        new StabilityEventExtra(INVALID_PHONE_INDEX, false /*need validation*/,
+                                false /*isForPerformance*/));
+        long delayMs = -1;
+        // Check if already scheduled one with that combination of extras.
+        if (eventExtras.targetPhoneId != targetPhoneId
+                || eventExtras.needValidation != needValidation
+                || eventExtras.isForPerformance != isForPerformance) {
+            eventExtras =
+                    new StabilityEventExtra(targetPhoneId, isForPerformance, needValidation);
 
-                // Reset with new timer.
-                delayMs = isForPerformance
-                        ? mAutoDataSwitchPerformanceStabilityTimeThreshold
-                        : mAutoDataSwitchAvailabilityStabilityTimeThreshold;
-                scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs);
-            }
-            log("startStabilityCheck: "
-                    + (delayMs != -1 ? "scheduling " : "already scheduled ")
-                    + eventExtras);
-        } else if (!hasEqualMessages(EVENT_STABILITY_CHECK_PASSED, combinationIdentifier)) {
-            removeMessages(EVENT_STABILITY_CHECK_PASSED);
-            sendMessageDelayed(obtainMessage(EVENT_STABILITY_CHECK_PASSED, targetPhoneId,
-                            needValidation ? 1 : 0,
-                            combinationIdentifier),
-                    mAutoDataSwitchAvailabilityStabilityTimeThreshold);
-            log("startStabilityCheck: targetPhoneId=" + targetPhoneId
-                    + " isForPerformance=" + isForPerformance
-                    + " needValidation=" + needValidation);
+            // Reset with new timer.
+            delayMs = isForPerformance
+                    ? mAutoDataSwitchPerformanceStabilityTimeThreshold
+                    : mAutoDataSwitchAvailabilityStabilityTimeThreshold;
+            scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs);
         }
+        log("startStabilityCheck: "
+                + (delayMs != -1 ? "scheduling " : "already scheduled ")
+                + eventExtras);
     }
 
     /**
@@ -1075,18 +1067,14 @@
     private void cancelAnyPendingSwitch() {
         mSelectedTargetPhoneId = INVALID_PHONE_INDEX;
         resetFailedCount();
-        if (sFeatureFlags.autoDataSwitchRatSs()) {
-            if (mScheduledEventsToExtras.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
-                if (mEventsToAlarmListener.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
-                    mAlarmManager.cancel(mEventsToAlarmListener.get(EVENT_STABILITY_CHECK_PASSED));
-                } else {
-                    loge("cancelAnyPendingSwitch: EVENT_STABILITY_CHECK_PASSED listener is null");
-                }
-                removeMessages(EVENT_STABILITY_CHECK_PASSED);
-                mScheduledEventsToExtras.remove(EVENT_STABILITY_CHECK_PASSED);
+        if (mScheduledEventsToExtras.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
+            if (mEventsToAlarmListener.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
+                mAlarmManager.cancel(mEventsToAlarmListener.get(EVENT_STABILITY_CHECK_PASSED));
+            } else {
+                loge("cancelAnyPendingSwitch: EVENT_STABILITY_CHECK_PASSED listener is null");
             }
-        } else {
             removeMessages(EVENT_STABILITY_CHECK_PASSED);
+            mScheduledEventsToExtras.remove(EVENT_STABILITY_CHECK_PASSED);
         }
         mPhoneSwitcherCallback.onRequireCancelAnyPendingAutoSwitchValidation();
     }
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 40039f2..20761e2 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -200,8 +200,8 @@
     private @interface DataConfigNetworkType {}
 
     /** Data config update callbacks. */
-    private final @NonNull Set<DataConfigManagerCallback> mDataConfigManagerCallbacks =
-            new ArraySet<>();
+    @NonNull
+    private final Set<DataConfigManagerCallback> mDataConfigManagerCallbacks = new ArraySet<>();
 
     /** DeviceConfig key of anomaly report threshold for back to back ims release-request. */
     private static final String KEY_ANOMALY_IMS_RELEASE_REQUEST = "anomaly_ims_release_request";
@@ -266,55 +266,77 @@
      */
     private boolean mIsApnConfigAnomalyReportEnabled;
 
-    private @NonNull final Phone mPhone;
-    private @NonNull final String mLogTag;
-
-    @NonNull private final FeatureFlags mFeatureFlags;
-    private @NonNull final CarrierConfigManager mCarrierConfigManager;
-    private @NonNull PersistableBundle mCarrierConfig = null;
-    private @NonNull Resources mResources = null;
+    @NonNull
+    private final Phone mPhone;
+    @NonNull
+    private final String mLogTag;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+    @NonNull
+    private final CarrierConfigManager mCarrierConfigManager;
+    @NonNull
+    private PersistableBundle mCarrierConfig = null;
+    @NonNull
+    private Resources mResources = null;
 
     /** The network capability priority map */
-    private @NonNull final Map<Integer, Integer> mNetworkCapabilityPriorityMap =
-            new ConcurrentHashMap<>();
+    @NonNull
+    private final Map<Integer, Integer> mNetworkCapabilityPriorityMap = new ConcurrentHashMap<>();
     /** The data setup retry rules */
-    private @NonNull final List<DataSetupRetryRule> mDataSetupRetryRules = new ArrayList<>();
+    @NonNull
+    private final List<DataSetupRetryRule> mDataSetupRetryRules = new ArrayList<>();
     /** The data handover retry rules */
-    private @NonNull final List<DataHandoverRetryRule> mDataHandoverRetryRules = new ArrayList<>();
+    @NonNull
+    private final List<DataHandoverRetryRule> mDataHandoverRetryRules = new ArrayList<>();
     /** The metered APN types for home network */
-    private @NonNull final @ApnType Set<Integer> mMeteredApnTypes = new HashSet<>();
+    @NonNull
+    @ApnType
+    private final Set<Integer> mMeteredApnTypes = new HashSet<>();
     /** The metered APN types for roaming network */
-    private @NonNull final @ApnType Set<Integer> mRoamingMeteredApnTypes = new HashSet<>();
+    @NonNull
+    @ApnType
+    private final Set<Integer> mRoamingMeteredApnTypes = new HashSet<>();
     /** The network types that only support single data networks */
-    private @NonNull final @NetworkType List<Integer> mSingleDataNetworkTypeList =
-            new ArrayList<>();
-    private @NonNull final @NetCapability Set<Integer> mCapabilitiesExemptFromSingleDataList =
-            new HashSet<>();
+    @NonNull
+    @NetworkType
+    private final List<Integer> mSingleDataNetworkTypeList = new ArrayList<>();
+    @NonNull
+    @NetCapability
+    private final Set<Integer> mCapabilitiesExemptFromSingleDataList = new HashSet<>();
     /** The network types that support temporarily not metered */
-    private @NonNull final @DataConfigNetworkType Set<String> mUnmeteredNetworkTypes =
-            new HashSet<>();
+    @NonNull
+    @DataConfigNetworkType
+    private final Set<String> mUnmeteredNetworkTypes = new HashSet<>();
     /** The network types that support temporarily not metered when roaming */
-    private @NonNull final @DataConfigNetworkType Set<String> mRoamingUnmeteredNetworkTypes =
-            new HashSet<>();
+    @NonNull
+    @DataConfigNetworkType
+    private final Set<String> mRoamingUnmeteredNetworkTypes = new HashSet<>();
     /** A map of network types to the downlink and uplink bandwidth values for that network type */
-    private @NonNull final @DataConfigNetworkType Map<String, DataNetwork.NetworkBandwidth>
-            mBandwidthMap = new ConcurrentHashMap<>();
-    /** A map of network types to the TCP buffer sizes for that network type */
-    private @NonNull final @DataConfigNetworkType Map<String, String> mTcpBufferSizeMap =
+    @NonNull
+    @DataConfigNetworkType
+    private final Map<String, DataNetwork.NetworkBandwidth> mBandwidthMap =
             new ConcurrentHashMap<>();
+    /** A map of network types to the TCP buffer sizes for that network type */
+    @NonNull
+    @DataConfigNetworkType
+    private final Map<String, String> mTcpBufferSizeMap = new ConcurrentHashMap<>();
     /** Rules for handover between IWLAN and cellular network. */
-    private @NonNull final List<HandoverRule> mHandoverRuleList = new ArrayList<>();
+    @NonNull
+    private final List<HandoverRule> mHandoverRuleList = new ArrayList<>();
     /** {@code True} keep IMS network in case of moving to non VOPS area; {@code false} otherwise.*/
     private boolean mShouldKeepNetworkUpInNonVops = false;
     /** The set of network types that enable VOPS even in non VOPS area. */
-    @NonNull private final @CarrierConfigManager.Ims.NetworkType List<Integer>
-            mEnabledVopsNetworkTypesInNonVops = new ArrayList<>();
+    @NonNull
+    @CarrierConfigManager.Ims.NetworkType
+    private final List<Integer> mEnabledVopsNetworkTypesInNonVops = new ArrayList<>();
     /**
      * A map of network types to the estimated downlink values by signal strength 0 - 4 for that
      * network type
      */
-    private @NonNull final @DataConfigNetworkType Map<String, int[]>
-            mAutoDataSwitchNetworkTypeSignalMap = new ConcurrentHashMap<>();
+    @NonNull
+    @DataConfigNetworkType
+    private final Map<String, int[]> mAutoDataSwitchNetworkTypeSignalMap =
+            new ConcurrentHashMap<>();
 
     /**
      * Constructor
@@ -554,14 +576,16 @@
     /**
      * @return The data setup retry rules from carrier config.
      */
-    public @NonNull List<DataSetupRetryRule> getDataSetupRetryRules() {
+    @NonNull
+    public List<DataSetupRetryRule> getDataSetupRetryRules() {
         return Collections.unmodifiableList(mDataSetupRetryRules);
     }
 
     /**
      * @return The data handover retry rules from carrier config.
      */
-    public @NonNull List<DataHandoverRetryRule> getDataHandoverRetryRules() {
+    @NonNull
+    public List<DataHandoverRetryRule> getDataHandoverRetryRules() {
         return Collections.unmodifiableList(mDataHandoverRetryRules);
     }
 
@@ -604,7 +628,9 @@
      *
      * @return The metered network capabilities when connected to a home network.
      */
-    public @NonNull @NetCapability Set<Integer> getMeteredNetworkCapabilities(boolean isRoaming) {
+    @NonNull
+    @NetCapability
+    public Set<Integer> getMeteredNetworkCapabilities(boolean isRoaming) {
         Set<Integer> meteredApnTypes = isRoaming ? mRoamingMeteredApnTypes : mMeteredApnTypes;
         Set<Integer> meteredCapabilities = meteredApnTypes.stream()
                 .map(DataUtils::apnTypeToNetworkCapability)
@@ -688,8 +714,7 @@
     /**
      * Update the voice over PS related config from the carrier config.
      */
-    private void updateVopsConfig() {
-        synchronized (this) {
+    private synchronized void updateVopsConfig() {
             mShouldKeepNetworkUpInNonVops = mCarrierConfig.getBoolean(CarrierConfigManager
                     .Ims.KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL);
             int[] allowedNetworkTypes = mCarrierConfig.getIntArray(
@@ -697,13 +722,14 @@
             if (allowedNetworkTypes != null) {
                 Arrays.stream(allowedNetworkTypes).forEach(mEnabledVopsNetworkTypesInNonVops::add);
             }
-        }
     }
 
     /**
      * @return The list of {@link NetworkType} that only supports single data networks
      */
-    public @NonNull @NetworkType List<Integer> getNetworkTypesOnlySupportSingleDataNetwork() {
+    @NonNull
+    @NetworkType
+    public List<Integer> getNetworkTypesOnlySupportSingleDataNetwork() {
         return Collections.unmodifiableList(mSingleDataNetworkTypeList);
     }
 
@@ -711,7 +737,9 @@
      * @return The list of {@link android.net.NetworkCapabilities.NetCapability} that every of which
      * is exempt from the single PDN check.
      */
-    public @NonNull @NetCapability Set<Integer> getCapabilitiesExemptFromSingleDataNetwork() {
+    @NonNull
+    @NetCapability
+    public Set<Integer> getCapabilitiesExemptFromSingleDataNetwork() {
         return Collections.unmodifiableSet(mCapabilitiesExemptFromSingleDataList);
     }
 
@@ -844,7 +872,8 @@
      * @param displayInfo The {@link TelephonyDisplayInfo} to get the bandwidth for.
      * @return The pre-configured bandwidth estimate from carrier config.
      */
-    public @NonNull DataNetwork.NetworkBandwidth getBandwidthForNetworkType(
+    @NonNull
+    public DataNetwork.NetworkBandwidth getBandwidthForNetworkType(
             @NonNull TelephonyDisplayInfo displayInfo) {
         DataNetwork.NetworkBandwidth bandwidth = mBandwidthMap.get(
                 getDataConfigNetworkType(displayInfo));
@@ -855,6 +884,14 @@
     }
 
     /**
+     * @return What kind of traffic is supported on an unrestricted satellite network.
+     */
+    @CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE
+    public int getSatelliteDataSupportMode() {
+        return mCarrierConfig.getInt(CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT);
+    }
+
+    /**
      * @return Whether data throttling should be reset when the TAC changes from the carrier config.
      */
     public boolean shouldResetDataThrottlingWhenTacChanges() {
@@ -931,7 +968,8 @@
      * Anomaly report thresholds for frequent setup data call failure.
      * @return EventFrequency to trigger the anomaly report
      */
-    public @NonNull EventFrequency getAnomalySetupDataCallThreshold() {
+    @NonNull
+    public EventFrequency getAnomalySetupDataCallThreshold() {
         return mSetupDataCallAnomalyReportThreshold;
     }
 
@@ -940,7 +978,8 @@
      * at {@link TelephonyNetworkAgent#onNetworkUnwanted}
      * @return EventFrequency to trigger the anomaly report
      */
-    public @NonNull EventFrequency getAnomalyNetworkUnwantedThreshold() {
+    @NonNull
+    public EventFrequency getAnomalyNetworkUnwantedThreshold() {
         return mNetworkUnwantedAnomalyReportThreshold;
     }
 
@@ -948,7 +987,8 @@
      * Anomaly report thresholds for back to back release-request of IMS.
      * @return EventFrequency to trigger the anomaly report
      */
-    public @NonNull EventFrequency getAnomalyImsReleaseRequestThreshold() {
+    @NonNull
+    public EventFrequency getAnomalyImsReleaseRequestThreshold() {
         return mImsReleaseRequestAnomalyReportThreshold;
     }
 
@@ -1119,7 +1159,8 @@
      * @return The TCP configuration string for the given display info or the default value from
      *         {@code config_tcp_buffers} if unavailable.
      */
-    public @NonNull String getTcpConfigString(@NonNull TelephonyDisplayInfo displayInfo) {
+    @NonNull
+    public String getTcpConfigString(@NonNull TelephonyDisplayInfo displayInfo) {
         String config = mTcpBufferSizeMap.get(getDataConfigNetworkType(displayInfo));
         if (TextUtils.isEmpty(config)) {
             config = getDefaultTcpConfigString();
@@ -1130,7 +1171,8 @@
     /**
      * @return The fixed TCP buffer size configured based on the device's memory and performance.
      */
-    public @NonNull String getDefaultTcpConfigString() {
+    @NonNull
+    public String getDefaultTcpConfigString() {
         return mResources.getString(com.android.internal.R.string.config_tcp_buffers);
     }
 
@@ -1173,20 +1215,21 @@
     /**
      * @return The bandwidth estimation source.
      */
-    public @DataNetwork.BandwidthEstimationSource int getBandwidthEstimateSource() {
+    @DataNetwork.BandwidthEstimationSource
+    public int getBandwidthEstimateSource() {
         String source = mResources.getString(
                 com.android.internal.R.string.config_bandwidthEstimateSource);
-        switch (source) {
-            case BANDWIDTH_SOURCE_MODEM_STRING_VALUE:
-                return DataNetwork.BANDWIDTH_SOURCE_MODEM;
-            case BANDWIDTH_SOURCE_CARRIER_CONFIG_STRING_VALUE:
-                return DataNetwork.BANDWIDTH_SOURCE_CARRIER_CONFIG;
-            case BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_STRING_VALUE:
-                return DataNetwork.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR;
-            default:
+        return switch (source) {
+            case BANDWIDTH_SOURCE_MODEM_STRING_VALUE -> DataNetwork.BANDWIDTH_SOURCE_MODEM;
+            case BANDWIDTH_SOURCE_CARRIER_CONFIG_STRING_VALUE ->
+                    DataNetwork.BANDWIDTH_SOURCE_CARRIER_CONFIG;
+            case BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_STRING_VALUE ->
+                    DataNetwork.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR;
+            default -> {
                 loge("Invalid bandwidth estimation source config: " + source);
-                return DataNetwork.BANDWIDTH_SOURCE_UNKNOWN;
-        }
+                yield DataNetwork.BANDWIDTH_SOURCE_UNKNOWN;
+            }
+        };
     }
 
     /**
@@ -1195,8 +1238,9 @@
      * @param displayInfo The {@link TelephonyDisplayInfo} used to determine the type.
      * @return The equivalent {@link DataConfigNetworkType}.
      */
-    private static @NonNull @DataConfigNetworkType String getDataConfigNetworkType(
-            @NonNull TelephonyDisplayInfo displayInfo) {
+    @NonNull
+    @DataConfigNetworkType
+    private static String getDataConfigNetworkType(@NonNull TelephonyDisplayInfo displayInfo) {
         int networkType = displayInfo.getNetworkType();
         switch (displayInfo.getOverrideNetworkType()) {
             case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
@@ -1305,7 +1349,8 @@
      *
      * @see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY
      */
-    public @NonNull List<HandoverRule> getHandoverRules() {
+    @NonNull
+    public List<HandoverRule> getHandoverRules() {
         return Collections.unmodifiableList(mHandoverRuleList);
     }
 
@@ -1323,52 +1368,33 @@
      * @param networkType The network type
      * @return The equivalent data config network type
      */
-    private static @NonNull @DataConfigNetworkType String networkTypeToDataConfigNetworkType(
+    @NonNull
+    @DataConfigNetworkType
+    private static String networkTypeToDataConfigNetworkType(
             @NetworkType int networkType) {
-        switch (networkType) {
-            case TelephonyManager.NETWORK_TYPE_GPRS:
-                return DATA_CONFIG_NETWORK_TYPE_GPRS;
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-                return DATA_CONFIG_NETWORK_TYPE_EDGE;
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-                return DATA_CONFIG_NETWORK_TYPE_UMTS;
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-                return DATA_CONFIG_NETWORK_TYPE_HSDPA;
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-                return DATA_CONFIG_NETWORK_TYPE_HSUPA;
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-                return DATA_CONFIG_NETWORK_TYPE_HSPA;
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-                return DATA_CONFIG_NETWORK_TYPE_CDMA;
-            case TelephonyManager.NETWORK_TYPE_EVDO_0:
-                return DATA_CONFIG_NETWORK_TYPE_EVDO_0;
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-                return DATA_CONFIG_NETWORK_TYPE_EVDO_A;
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-                return DATA_CONFIG_NETWORK_TYPE_EVDO_B;
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-                return DATA_CONFIG_NETWORK_TYPE_1xRTT;
-            case TelephonyManager.NETWORK_TYPE_LTE:
-                return DATA_CONFIG_NETWORK_TYPE_LTE;
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-                return DATA_CONFIG_NETWORK_TYPE_EHRPD;
-            case TelephonyManager.NETWORK_TYPE_IDEN:
-                return DATA_CONFIG_NETWORK_TYPE_IDEN;
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-                return DATA_CONFIG_NETWORK_TYPE_HSPAP;
-            case TelephonyManager.NETWORK_TYPE_GSM:
-                return DATA_CONFIG_NETWORK_TYPE_GSM;
-            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
-                return DATA_CONFIG_NETWORK_TYPE_TD_SCDMA;
-            case TelephonyManager.NETWORK_TYPE_IWLAN:
-                return DATA_CONFIG_NETWORK_TYPE_IWLAN;
-            case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                return DATA_CONFIG_NETWORK_TYPE_LTE_CA;
-            case TelephonyManager.NETWORK_TYPE_NR:
-                return DATA_CONFIG_NETWORK_TYPE_NR_SA;
-            default:
-                return "";
-        }
+        return switch (networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS -> DATA_CONFIG_NETWORK_TYPE_GPRS;
+            case TelephonyManager.NETWORK_TYPE_EDGE -> DATA_CONFIG_NETWORK_TYPE_EDGE;
+            case TelephonyManager.NETWORK_TYPE_UMTS -> DATA_CONFIG_NETWORK_TYPE_UMTS;
+            case TelephonyManager.NETWORK_TYPE_HSDPA -> DATA_CONFIG_NETWORK_TYPE_HSDPA;
+            case TelephonyManager.NETWORK_TYPE_HSUPA -> DATA_CONFIG_NETWORK_TYPE_HSUPA;
+            case TelephonyManager.NETWORK_TYPE_HSPA -> DATA_CONFIG_NETWORK_TYPE_HSPA;
+            case TelephonyManager.NETWORK_TYPE_CDMA -> DATA_CONFIG_NETWORK_TYPE_CDMA;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0 -> DATA_CONFIG_NETWORK_TYPE_EVDO_0;
+            case TelephonyManager.NETWORK_TYPE_EVDO_A -> DATA_CONFIG_NETWORK_TYPE_EVDO_A;
+            case TelephonyManager.NETWORK_TYPE_EVDO_B -> DATA_CONFIG_NETWORK_TYPE_EVDO_B;
+            case TelephonyManager.NETWORK_TYPE_1xRTT -> DATA_CONFIG_NETWORK_TYPE_1xRTT;
+            case TelephonyManager.NETWORK_TYPE_LTE -> DATA_CONFIG_NETWORK_TYPE_LTE;
+            case TelephonyManager.NETWORK_TYPE_EHRPD -> DATA_CONFIG_NETWORK_TYPE_EHRPD;
+            case TelephonyManager.NETWORK_TYPE_IDEN -> DATA_CONFIG_NETWORK_TYPE_IDEN;
+            case TelephonyManager.NETWORK_TYPE_HSPAP -> DATA_CONFIG_NETWORK_TYPE_HSPAP;
+            case TelephonyManager.NETWORK_TYPE_GSM -> DATA_CONFIG_NETWORK_TYPE_GSM;
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA -> DATA_CONFIG_NETWORK_TYPE_TD_SCDMA;
+            case TelephonyManager.NETWORK_TYPE_IWLAN -> DATA_CONFIG_NETWORK_TYPE_IWLAN;
+            case TelephonyManager.NETWORK_TYPE_LTE_CA -> DATA_CONFIG_NETWORK_TYPE_LTE_CA;
+            case TelephonyManager.NETWORK_TYPE_NR -> DATA_CONFIG_NETWORK_TYPE_NR_SA;
+            default -> "";
+        };
     }
 
     /**
@@ -1376,7 +1402,8 @@
      *
      * @see CarrierConfigManager#KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY
      */
-    public @NonNull long[] getDataStallRecoveryDelayMillis() {
+    @NonNull
+    public long[] getDataStallRecoveryDelayMillis() {
         return mCarrierConfig.getLongArray(
             CarrierConfigManager.KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY);
     }
@@ -1386,7 +1413,8 @@
      *
      * @see CarrierConfigManager#KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY
      */
-    public @NonNull boolean[] getDataStallRecoveryShouldSkipArray() {
+    @NonNull
+    public boolean[] getDataStallRecoveryShouldSkipArray() {
         return mCarrierConfig.getBooleanArray(
             CarrierConfigManager.KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY);
     }
@@ -1395,7 +1423,8 @@
      * @return The default preferred APN. An empty string if not configured. This is used for the
      * first time boot up where preferred APN is not set.
      */
-    public @NonNull String getDefaultPreferredApn() {
+    @NonNull
+    public String getDefaultPreferredApn() {
         return TextUtils.emptyIfNull(mCarrierConfig.getString(
                 CarrierConfigManager.KEY_DEFAULT_PREFERRED_APN_NAME_STRING));
     }
@@ -1414,7 +1443,9 @@
      * cases that we need to use "user-added" APN for initial attach. The regular way to configure
      * IA APN is by adding "IA" type to the APN in APN config.
      */
-    public @NonNull @ApnType List<Integer> getAllowedInitialAttachApnTypes() {
+    @NonNull
+    @ApnType
+    public List<Integer> getAllowedInitialAttachApnTypes() {
         String[] apnTypesArray = mCarrierConfig.getStringArray(
                 CarrierConfigManager.KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY);
         if (apnTypesArray != null) {
@@ -1461,7 +1492,8 @@
     /**
      * @return The capabilities that network will be forced to mark as cellular transport.
      */
-    public @NetCapability Set<Integer> getForcedCellularTransportCapabilities() {
+    @NetCapability
+    public Set<Integer> getForcedCellularTransportCapabilities() {
         String[] forcedCellularTransportCapabilities = mResources.getStringArray(
                 com.android.internal.R.array.config_force_cellular_transport_capabilities);
 
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index ae7023e..f5eae91 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -59,7 +59,7 @@
      *
      * @param reason The reason for this evaluation.
      */
-    public DataEvaluation(DataEvaluationReason reason) {
+    public DataEvaluation(@NonNull DataEvaluationReason reason) {
         mDataEvaluationReason = reason;
     }
 
@@ -105,14 +105,16 @@
     /**
      * @return List of data disallowed reasons.
      */
-    public @NonNull List<DataDisallowedReason> getDataDisallowedReasons() {
+    @NonNull
+    public List<DataDisallowedReason> getDataDisallowedReasons() {
         return new ArrayList<>(mDataDisallowedReasons);
     }
 
     /**
      * @return The data allowed reason.
      */
-    public @NonNull DataAllowedReason getDataAllowedReason() {
+    @NonNull
+    public DataAllowedReason getDataAllowedReason() {
         return mDataAllowedReason;
     }
 
@@ -128,7 +130,8 @@
     /**
      * @return The candidate data profile for setup data network.
      */
-    public @Nullable DataProfile getCandidateDataProfile() {
+    @Nullable
+    public DataProfile getCandidateDataProfile() {
         return mCandidateDataProfile;
     }
 
@@ -136,7 +139,7 @@
      * @return {@code true} if the evaluation contains disallowed reasons.
      */
     public boolean containsDisallowedReasons() {
-        return mDataDisallowedReasons.size() != 0;
+        return !mDataDisallowedReasons.isEmpty();
     }
 
     /**
@@ -422,7 +425,8 @@
     @Override
     public String toString() {
         StringBuilder evaluationStr = new StringBuilder();
-        evaluationStr.append("Data evaluation: evaluation reason:" + mDataEvaluationReason + ", ");
+        evaluationStr.append("Data evaluation: evaluation reason:")
+                .append(mDataEvaluationReason).append(", ");
         if (!mDataDisallowedReasons.isEmpty()) {
             evaluationStr.append("Data disallowed reasons:");
             for (DataDisallowedReason reason : mDataDisallowedReasons) {
@@ -432,8 +436,8 @@
             evaluationStr.append("Data allowed reason:");
             evaluationStr.append(" ").append(mDataAllowedReason);
         }
-        evaluationStr.append(", candidate profile=" + mCandidateDataProfile);
-        evaluationStr.append(", time=" + DataUtils.systemTimeToString(mEvaluatedTime));
+        evaluationStr.append(", candidate profile=").append(mCandidateDataProfile);
+        evaluationStr.append(", time=").append(DataUtils.systemTimeToString(mEvaluatedTime));
         return evaluationStr.toString();
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 9e84a77..8369874 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -125,6 +125,7 @@
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -136,11 +137,11 @@
 
 /**
  * DataNetwork class represents a single PDN (Packet Data Network).
- *
+ * <p>
  * The life cycle of a data network starts from {@link ConnectingState}. If setup data request
  * succeeds, then it enters {@link ConnectedState}, otherwise it enters
  * {@link DisconnectedState}.
- *
+ * <p>
  * When data network is in {@link ConnectingState}, it can enter {@link HandoverState} if handover
  * between IWLAN and cellular occurs. After handover completes or fails, it return back to
  * {@link ConnectedState}. When the data network is about to be disconnected, it first enters
@@ -149,9 +150,9 @@
  * notifies data disconnected. Note that an unsolicited disconnected event from {@link DataService}
  * or any vendor HAL failure response can immediately move data network from {@link ConnectedState}
  * to {@link DisconnectedState}. {@link DisconnectedState} is the final state of a data network.
- *
+ * <p>
  * State machine diagram:
- *
+ * <p>
  *
  *                                  ┌─────────┐
  *                                  │Handover │
@@ -504,10 +505,12 @@
     private final DisconnectedState mDisconnectedState = new DisconnectedState();
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /** Feature flags */
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
 
     /**
      * The subscription id. This is assigned when the network is created, and not supposed to
@@ -516,7 +519,8 @@
     private final int mSubId;
 
     /** The network score of this network. */
-    private @NonNull NetworkScore mNetworkScore;
+    @NonNull
+    private NetworkScore mNetworkScore;
 
     /**
      * Indicates that
@@ -531,13 +535,15 @@
     private boolean mEverConnected = false;
 
     /** RIL interface. */
-    private final @NonNull CommandsInterface mRil;
+    @NonNull
+    private final CommandsInterface mRil;
 
     /** Local log. */
     private final LocalLog mLocalLog = new LocalLog(128);
 
     /** The callback to receives data network state update. */
-    private final @NonNull DataNetworkCallback mDataNetworkCallback;
+    @NonNull
+    private final DataNetworkCallback mDataNetworkCallback;
 
     /** The log tag. */
     private String mLogTag;
@@ -546,7 +552,8 @@
     private final DataCallSessionStats mDataCallSessionStats;
 
     /** Metrics of per data network validation. */
-    private final @NonNull DataNetworkValidationStats mDataNetworkValidationStats;
+    @NonNull
+    private final DataNetworkValidationStats mDataNetworkValidationStats;
 
     /**
      * The unique context id assigned by the data service in {@link DataCallResponse#getId()}. One
@@ -570,71 +577,92 @@
      * Data service managers for accessing {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} and
      * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN} data services.
      */
-    private final @NonNull SparseArray<DataServiceManager> mDataServiceManagers;
+    @NonNull
+    private final SparseArray<DataServiceManager> mDataServiceManagers;
 
     /** Access networks manager. */
-    private final @NonNull AccessNetworksManager mAccessNetworksManager;
+    @NonNull
+    private final AccessNetworksManager mAccessNetworksManager;
 
     /** Data network controller. */
-    private final @NonNull DataNetworkController mDataNetworkController;
+    @NonNull
+    private final DataNetworkController mDataNetworkController;
 
     /** Data network controller callback. */
-    private final @NonNull DataNetworkController.DataNetworkControllerCallback
+    @NonNull
+    private final DataNetworkController.DataNetworkControllerCallback
             mDataNetworkControllerCallback;
 
     /** Data settings manager callback. */
-    private @NonNull DataSettingsManagerCallback mDataSettingsManagerCallback;
+    @NonNull
+    private DataSettingsManagerCallback mDataSettingsManagerCallback;
 
     /** Data config manager. */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** VCN manager. */
-    private final @Nullable VcnManager mVcnManager;
+    @Nullable
+    private final VcnManager mVcnManager;
 
     /** VCN policy changed listener. */
-    private @Nullable VcnNetworkPolicyChangeListener mVcnPolicyChangeListener;
+    @Nullable
+    private VcnNetworkPolicyChangeListener mVcnPolicyChangeListener;
 
     /** The network agent associated with this data network. */
-    private @NonNull TelephonyNetworkAgent mNetworkAgent;
+    @NonNull
+    private TelephonyNetworkAgent mNetworkAgent;
 
     /** QOS callback tracker. This is only created after network connected on WWAN. */
-    private @Nullable QosCallbackTracker mQosCallbackTracker;
+    @Nullable
+    private QosCallbackTracker mQosCallbackTracker;
 
     /** NAT keepalive tracker. */
-    private @Nullable KeepaliveTracker mKeepaliveTracker;
+    @Nullable
+    private KeepaliveTracker mKeepaliveTracker;
 
     /** The data profile used to establish this data network. */
-    private @NonNull DataProfile mDataProfile;
+    @NonNull
+    private DataProfile mDataProfile;
 
     /**
      * The data profile used for data handover. Some carriers might use different data profile
      * between IWLAN and cellular. Only set before handover started.
      */
-    private @Nullable DataProfile mHandoverDataProfile;
+    @Nullable
+    private DataProfile mHandoverDataProfile;
 
     /** The network capabilities of this data network. */
-    private @NonNull NetworkCapabilities mNetworkCapabilities;
+    @NonNull
+    private NetworkCapabilities mNetworkCapabilities;
 
     /** The matched traffic descriptor returned from setup data call request. */
-    private final @NonNull List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
+    @NonNull
+    private final List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
 
     /** The link properties of this data network. */
-    private @NonNull LinkProperties mLinkProperties;
+    @NonNull
+    private LinkProperties mLinkProperties;
 
     /** The network slice info. */
-    private @Nullable NetworkSliceInfo mNetworkSliceInfo;
+    @Nullable
+    private NetworkSliceInfo mNetworkSliceInfo;
 
     /** The link status (i.e. RRC state). */
-    private @LinkStatus int mLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
+    @LinkStatus
+    private int mLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
 
     /** The network bandwidth. */
-    private @NonNull NetworkBandwidth mNetworkBandwidth = new NetworkBandwidth(14, 14);
+    @NonNull
+    private NetworkBandwidth mNetworkBandwidth = new NetworkBandwidth(14, 14);
 
     /** The TCP buffer sizes config. */
-    private @NonNull String mTcpBufferSizes;
+    @NonNull
+    private String mTcpBufferSizes;
 
     /** The telephony display info. */
-    private @NonNull TelephonyDisplayInfo mTelephonyDisplayInfo;
+    @NonNull
+    private TelephonyDisplayInfo mTelephonyDisplayInfo;
 
     /** Whether {@link NetworkCapabilities#NET_CAPABILITY_TEMPORARILY_NOT_METERED} is supported. */
     private boolean mTempNotMeteredSupported = false;
@@ -646,7 +674,8 @@
     private boolean mCongested = false;
 
     /** The network requests associated with this data network */
-    private final @NonNull NetworkRequestList mAttachedNetworkRequestList =
+    @NonNull
+    private final NetworkRequestList mAttachedNetworkRequestList =
             new NetworkRequestList();
 
     /**
@@ -655,18 +684,21 @@
      * {@link DataServiceCallback#onDataCallListChanged(List)}. The very first update must be
      * from {@link DataServiceCallback#onSetupDataCallComplete(int, DataCallResponse)}.
      */
-    private @Nullable DataCallResponse mDataCallResponse = null;
+    @Nullable
+    private DataCallResponse mDataCallResponse = null;
 
     /**
      * The fail cause from either setup data failure or unsolicited disconnect reported by data
      * service.
      */
-    private @DataFailureCause int mFailCause = DataFailCause.NONE;
+    @DataFailureCause
+    private int mFailCause = DataFailCause.NONE;
 
     /**
      * The tear down reason if the data call is voluntarily deactivated, not due to failure.
      */
-    private @TearDownReason int mTearDownReason = TEAR_DOWN_REASON_NONE;
+    @TearDownReason
+    private int mTearDownReason = TEAR_DOWN_REASON_NONE;
 
     /**
      * The retry delay in milliseconds from setup data failure.
@@ -686,12 +718,14 @@
      * The current transport of the data network. For handover, the current transport will be set
      * after handover completes.
      */
-    private @TransportType int mTransport;
+    @TransportType
+    private int mTransport;
 
     /**
      * The last known data network type.
      */
-    private @NetworkType int mLastKnownDataNetworkType;
+    @NetworkType
+    private int mLastKnownDataNetworkType;
 
     /**
      * The last known roaming state of this data network.
@@ -704,27 +738,33 @@
     private final boolean mIsSatellite;
 
     /** The reason that why setting up this data network is allowed. */
-    private final @NonNull DataAllowedReason mDataAllowedReason;
+    @NonNull
+    private final DataAllowedReason mDataAllowedReason;
 
     /**
      * PCO (Protocol Configuration Options) data received from the network. The first key is the
      * cid of the PCO data, the second key is the PCO id, the value is the PCO data.
      */
-    private final @NonNull Map<Integer, Map<Integer, PcoData>> mPcoData = new ArrayMap<>();
+    @NonNull
+    private final Map<Integer, Map<Integer, PcoData>> mPcoData = new ArrayMap<>();
 
     /** The QOS bearer sessions. */
-    private final @NonNull List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
+    @NonNull
+    private final List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
 
     /** The QOS for the Default Bearer, should be non-null on LTE and NR */
-    private @Nullable Qos mDefaultQos;
+    @Nullable
+    private Qos mDefaultQos;
 
     /**
      * The UIDs of packages that have carrier privilege.
      */
-    private @NonNull int[] mAdministratorUids = new int[0];
+    @NonNull
+    private int[] mAdministratorUids = new int[0];
 
     /** Carrier privileges callback to monitor administrator UID change. */
-    private @Nullable TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback;
+    @Nullable
+    private TelephonyManager.CarrierPrivilegesCallback mCarrierPrivilegesCallback;
 
     /**
      * Carrier service package uid. This UID will not change through the life cycle of data network.
@@ -734,36 +774,42 @@
     /**
      * Link bandwidth estimator callback for receiving latest link bandwidth information.
      */
-    private @Nullable LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
+    @Nullable
+    private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
 
     /**
      * Data config callback for carrier config update.
      */
-    private @Nullable DataConfigManagerCallback mDataConfigManagerCallback;
+    @Nullable
+    private DataConfigManagerCallback mDataConfigManagerCallback;
 
     /**
      * Network validation status for this data network. If the data service provider does not
      * support the network validation feature, should be UNSUPPORTED.
      */
-    private @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus =
+    @PreciseDataConnectionState.NetworkValidationStatus
+    private int mNetworkValidationStatus =
             PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED;
 
     /**
      * Callback used to respond to a network validation request to determine whether the request is
      * successfully submitted. If the request has been submitted, change it to null.
      */
-    private @Nullable Consumer<Integer> mNetworkValidationResultCodeCallback;
+    @Nullable
+    private Consumer<Integer> mNetworkValidationResultCodeCallback;
 
     /**
      * Callback used to listen QNS preference changes.
      */
-    private @Nullable AccessNetworksManagerCallback mAccessNetworksManagerCallback;
+    @Nullable
+    private AccessNetworksManagerCallback mAccessNetworksManagerCallback;
 
     /**
      * PreciseDataConnectionState, the most recently notified. If it has never been notified, it is
      * null.
      */
-    private @Nullable PreciseDataConnectionState mPreciseDataConnectionState;
+    @Nullable
+    private PreciseDataConnectionState mPreciseDataConnectionState;
 
     /**
      * The network bandwidth.
@@ -1004,23 +1050,36 @@
                 && transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
         mDataAllowedReason = dataAllowedReason;
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
-        mAttachedNetworkRequestList.addAll(networkRequestList);
         for (int transportType : mAccessNetworksManager.getAvailableTransports()) {
             mCid.put(transportType, INVALID_CID);
         }
         mTelephonyDisplayInfo = mPhone.getDisplayInfoController().getTelephonyDisplayInfo();
         mTcpBufferSizes = mDataConfigManager.getTcpConfigString(mTelephonyDisplayInfo);
 
-        for (TelephonyNetworkRequest networkRequest : networkRequestList) {
-            networkRequest.setAttachedNetwork(DataNetwork.this);
-            networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_SATISFIED);
-        }
-
+        // network capabilities infer connectivity transport and MMTEL from the requested
+        // capabilities.
+        // TODO: Ideally we shouldn't infer network capabilities base on the requested capabilities,
+        // but currently there are 2 hacks associated with getForcedCellularTransportCapabilities
+        // and IMS service requesting IMS|MMTEL that need to support. When we stop supporting these
+        // cases, we shouldn't consider the requests when determining the network capabilities.
+        mAttachedNetworkRequestList.addAll(networkRequestList);
         // Update the capabilities in the constructor is to make sure the data network has initial
         // capability immediately after created. Doing this connecting state creates the window that
         // DataNetworkController might check if existing data network's capability can satisfy the
         // next network request within this window.
         updateNetworkCapabilities();
+
+        // Remove the requests that can't use the initial capabilities
+        ListIterator<TelephonyNetworkRequest> iter = mAttachedNetworkRequestList.listIterator();
+        while (iter.hasNext()) {
+            TelephonyNetworkRequest request = iter.next();
+            if (request.canBeSatisfiedBy(mNetworkCapabilities)) {
+                request.setAttachedNetwork(DataNetwork.this);
+                request.setState(TelephonyNetworkRequest.REQUEST_STATE_SATISFIED);
+            } else {
+                iter.remove();
+            }
+        }
     }
 
     /**
@@ -1064,7 +1123,8 @@
      *
      * @return The telephony network agent.
      */
-    private @NonNull TelephonyNetworkAgent createNetworkAgent() {
+    @NonNull
+    private TelephonyNetworkAgent createNetworkAgent() {
         final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
         configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
         configBuilder.setLegacyTypeName("MOBILE");
@@ -1097,13 +1157,8 @@
                 mPhone.getPhoneId());
         final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
 
-        // Always prefer IWLAN network for MMS designated network.
-        // TODO(b/293656884) Proper use of primary transport to avoid conflicting with DSDA.
-        boolean isPreferred = mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
-                && getApnTypeNetworkCapability() == NetworkCapabilities.NET_CAPABILITY_MMS;
-
-        mNetworkScore = new NetworkScore.Builder().setTransportPrimary(isPreferred)
-                .setKeepConnectedReason(isHandoverInProgress()
+        mNetworkScore = new NetworkScore.Builder()
+               .setKeepConnectedReason(isHandoverInProgress()
                         ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER
                         : NetworkScore.KEEP_CONNECTED_NONE).build();
 
@@ -2390,6 +2445,7 @@
         }
 
         // Always start with not-restricted, and then remove if needed.
+        // By default, NET_CAPABILITY_NOT_RESTRICTED and NET_CAPABILITY_NOT_CONSTRAINED are included
         builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
 
         // When data is disabled, or data roaming is disabled and the device is roaming, we need
@@ -2428,11 +2484,6 @@
             builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         }
 
-        // mark the network as restricted when service state is non-terrestrial(satellite network)
-        if (mFlags.satelliteInternet() && mIsSatellite) {
-            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
-        }
-
         // 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.
@@ -2474,6 +2525,23 @@
         builder.setLinkDownstreamBandwidthKbps(mNetworkBandwidth.downlinkBandwidthKbps);
         builder.setLinkUpstreamBandwidthKbps(mNetworkBandwidth.uplinkBandwidthKbps);
 
+        // Configure the network as restricted/constrained for unrestricted satellite network.
+        if (mFlags.satelliteInternet() && mIsSatellite && builder.build()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
+            switch (mDataConfigManager.getSatelliteDataSupportMode()) {
+                case CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED
+                        -> builder.removeCapability(
+                                NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+                case CarrierConfigManager.SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED -> {
+                    try {
+                        builder.removeCapability(DataUtils
+                                .NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+                    } catch (Exception ignored) { }
+                }
+                // default case CarrierConfigManager.SATELLITE_DATA_SUPPORT_ALL
+            }
+        }
+
         NetworkCapabilities nc = builder.build();
         if (mNetworkCapabilities == null || mNetworkAgent == null) {
             // This is the first time when network capabilities is created. The agent is not created
@@ -2522,21 +2590,24 @@
     /**
      * @return The network capabilities of this data network.
      */
-    public @NonNull NetworkCapabilities getNetworkCapabilities() {
+    @NonNull
+    public NetworkCapabilities getNetworkCapabilities() {
         return mNetworkCapabilities;
     }
 
     /**
      * @return The link properties of this data network.
      */
-    public @NonNull LinkProperties getLinkProperties() {
+    @NonNull
+    public LinkProperties getLinkProperties() {
         return mLinkProperties;
     }
 
     /**
      * @return The data profile of this data network.
      */
-    public @NonNull DataProfile getDataProfile() {
+    @NonNull
+    public DataProfile getDataProfile() {
         return mDataProfile;
     }
 
@@ -2600,7 +2671,8 @@
      *
      * @return The fail cause. {@link DataFailCause#NONE} if succeeds.
      */
-    private @DataFailureCause int getFailCauseFromDataCallResponse(
+    @DataFailureCause
+    private int getFailCauseFromDataCallResponse(
             @DataServiceCallback.ResultCode int resultCode, @Nullable DataCallResponse response) {
         int failCause = DataFailCause.NONE;
         switch (resultCode) {
@@ -2631,7 +2703,8 @@
      *
      * @param response The data call response from data service.
      */
-    private void updateDataNetwork(@NonNull DataCallResponse response) {
+    private void updateDataNetwork(@Nullable DataCallResponse response) {
+        if (response == null) return;
         mCid.put(mTransport, response.getId());
         LinkProperties linkProperties = new LinkProperties();
 
@@ -2937,8 +3010,8 @@
      * will be performed. {@code null} if the data network is already disconnected or being
      * disconnected.
      */
-    public @Nullable Runnable tearDownWhenConditionMet(@TearDownReason int reason,
-            long timeoutMillis) {
+    @Nullable
+    public Runnable tearDownWhenConditionMet(@TearDownReason int reason, long timeoutMillis) {
         if (getCurrentState() == null || isDisconnected() || isDisconnecting()) {
             loge("tearDownWhenConditionMet: Not in the right state. State=" + getCurrentState());
             return null;
@@ -3201,7 +3274,8 @@
     /**
      * @return The current network type reported by the network service.
      */
-    private @NetworkType int getDataNetworkType() {
+    @NetworkType
+    private int getDataNetworkType() {
         return getDataNetworkType(mTransport);
     }
 
@@ -3211,7 +3285,8 @@
      * @param transport The transport.
      * @return The data network type.
      */
-    private @NetworkType int getDataNetworkType(@TransportType int transport) {
+    @NetworkType
+    private int getDataNetworkType(@TransportType int transport) {
         // WLAN transport can't have network type other than IWLAN. Ideally service state tracker
         // should report the correct RAT, but sometimes race condition could happen that service
         // state is reset to out of service and RAT not updated to IWLAN yet.
@@ -3231,7 +3306,8 @@
     /**
      * @return The physical link status (i.e. RRC state).
      */
-    public @LinkStatus int getLinkStatus() {
+    @LinkStatus
+    public int getLinkStatus() {
         return mLinkStatus;
     }
 
@@ -3254,7 +3330,8 @@
     /**
      * @return Network registration info on the current transport.
      */
-    private @Nullable NetworkRegistrationInfo getNetworkRegistrationInfo() {
+    @Nullable
+    private NetworkRegistrationInfo getNetworkRegistrationInfo() {
         NetworkRegistrationInfo nri = mPhone.getServiceStateTracker().getServiceState()
                 .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, mTransport);
         if (nri == null) {
@@ -3276,7 +3353,8 @@
      *
      * @see #getPriority()
      */
-    public @NetCapability int getApnTypeNetworkCapability() {
+    @NetCapability
+    public int getApnTypeNetworkCapability() {
         if (!mAttachedNetworkRequestList.isEmpty()) {
             // The highest priority network request is always at the top of list.
             return mAttachedNetworkRequestList.get(0).getApnTypeNetworkCapability();
@@ -3317,7 +3395,8 @@
     /**
      * @return The attached network request list.
      */
-    public @NonNull NetworkRequestList getAttachedNetworkRequestList() {
+    @NonNull
+    public NetworkRequestList getAttachedNetworkRequestList() {
         return mAttachedNetworkRequestList;
     }
 
@@ -3366,11 +3445,13 @@
     /**
      * @return The current transport of the data network.
      */
-    public @TransportType int getTransport() {
+    @TransportType
+    public int getTransport() {
         return mTransport;
     }
 
-    private @DataState int getState() {
+    @DataState
+    private int getState() {
         IState state = getCurrentState();
         if (state == null || isDisconnected()) {
             return TelephonyManager.DATA_DISCONNECTED;
@@ -3429,7 +3510,7 @@
     /**
      * Send the precise data connection state to the listener of
      * {@link android.telephony.TelephonyCallback.PreciseDataConnectionStateListener}.
-     *
+     * <p>
      * Note that notify only when {@link DataState} or {@link
      * PreciseDataConnectionState.NetworkValidationStatus} or {@link TelephonyNetworkAgent#getId}
      * changes.
@@ -3449,7 +3530,7 @@
 
     /**
      * Request the data network to handover to the target transport.
-     *
+     * <p>
      * This is the starting point of initiating IWLAN/cellular handover. It will first call
      * {@link DataServiceManager#startHandover(int, Message)} to notify source transport that
      * handover is about to start, and then call {@link DataServiceManager#setupDataCall(int,
@@ -3660,7 +3741,8 @@
     /**
      * @return The last known data network type of the data network.
      */
-    public @NetworkType int getLastKnownDataNetworkType() {
+    @NetworkType
+    public int getLastKnownDataNetworkType() {
         return mLastKnownDataNetworkType;
     }
 
@@ -3674,7 +3756,8 @@
     /**
      * @return The PCO data received from the network.
      */
-    public @NonNull Map<Integer, PcoData> getPcoData() {
+    @NonNull
+    public Map<Integer, PcoData> getPcoData() {
         if (mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                 || mCid.get(mTransport) == INVALID_CID) {
             return Collections.emptyMap();
@@ -3794,7 +3877,8 @@
      * @param reason Data deactivation reason.
      * @return The deactivation reason in string format.
      */
-    public static @NonNull String tearDownReasonToString(@TearDownReason int reason) {
+    @NonNull
+    public static String tearDownReasonToString(@TearDownReason int reason) {
         return switch (reason) {
             case TEAR_DOWN_REASON_NONE -> "NONE";
             case TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED -> "CONNECTIVITY_SERVICE_UNWANTED";
@@ -3849,7 +3933,8 @@
      * @param event The event
      * @return The event in string format.
      */
-    private static @NonNull String eventToString(int event) {
+    @NonNull
+    private static String eventToString(int event) {
         return switch (event) {
             case EVENT_DATA_CONFIG_UPDATED -> "EVENT_DATA_CONFIG_UPDATED";
             case EVENT_ATTACH_NETWORK_REQUEST -> "EVENT_ATTACH_NETWORK_REQUEST";
@@ -3897,7 +3982,8 @@
     /**
      * @return The short name of the data network (e.g. DN-C-1)
      */
-    public @NonNull String name() {
+    @NonNull
+    public String name() {
         return mLogTag;
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 3104d9e..30172db 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -92,6 +92,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SlidingWindowEventCounter;
+import com.android.internal.telephony.TelephonyCapabilities;
 import com.android.internal.telephony.TelephonyComponentFactory;
 import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
@@ -266,17 +267,26 @@
     private final String mLogTag;
     private final LocalLog mLocalLog = new LocalLog(128);
 
-    private final @NonNull DataConfigManager mDataConfigManager;
-    private final @NonNull DataSettingsManager mDataSettingsManager;
-    private final @NonNull DataProfileManager mDataProfileManager;
-    private final @NonNull DataStallRecoveryManager mDataStallRecoveryManager;
-    private final @NonNull AccessNetworksManager mAccessNetworksManager;
-    private final @NonNull DataRetryManager mDataRetryManager;
-    private final @NonNull ImsManager mImsManager;
-    private final @NonNull TelecomManager mTelecomManager;
-    private final @NonNull NetworkPolicyManager mNetworkPolicyManager;
-    private final @NonNull SparseArray<DataServiceManager> mDataServiceManagers =
-            new SparseArray<>();
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataSettingsManager mDataSettingsManager;
+    @NonNull
+    private final DataProfileManager mDataProfileManager;
+    @NonNull
+    private final DataStallRecoveryManager mDataStallRecoveryManager;
+    @NonNull
+    private final AccessNetworksManager mAccessNetworksManager;
+    @NonNull
+    private final DataRetryManager mDataRetryManager;
+    @NonNull
+    private final ImsManager mImsManager;
+    @NonNull
+    private final TelecomManager mTelecomManager;
+    @NonNull
+    private final NetworkPolicyManager mNetworkPolicyManager;
+    @NonNull
+    private final SparseArray<DataServiceManager> mDataServiceManagers = new SparseArray<>();
 
     /** The subscription index associated with this data network controller. */
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -284,35 +294,41 @@
     /** The current service state of the device. */
     // Note that keeping a copy here instead of directly using ServiceStateTracker.getServiceState()
     // is intended for detecting the delta.
-    private @NonNull ServiceState mServiceState;
+    @NonNull
+    private ServiceState mServiceState;
 
     /** The list of SubscriptionPlans, updated when initialized and when plans are changed. */
-    private final @NonNull List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>();
+    @NonNull
+    private final List<SubscriptionPlan> mSubscriptionPlans = new ArrayList<>();
 
     /**
      * The set of network types an unmetered override applies to, set by onSubscriptionOverride
      * and cleared when the device is rebooted or the override expires.
      */
-    private final @NonNull @NetworkType Set<Integer> mUnmeteredOverrideNetworkTypes =
-            new ArraySet<>();
+    @NonNull
+    @NetworkType
+    private final Set<Integer> mUnmeteredOverrideNetworkTypes = new ArraySet<>();
 
     /**
      * The set of network types a congested override applies to, set by onSubscriptionOverride
      * and cleared when the device is rebooted or the override expires.
      */
-    private final @NonNull @NetworkType Set<Integer> mCongestedOverrideNetworkTypes =
-            new ArraySet<>();
+    @NonNull
+    @NetworkType
+    private final Set<Integer> mCongestedOverrideNetworkTypes = new ArraySet<>();
 
     /**
      * The list of all network requests.
      */
-    private final @NonNull NetworkRequestList mAllNetworkRequestList = new NetworkRequestList();
+    @NonNull
+    private final NetworkRequestList mAllNetworkRequestList = new NetworkRequestList();
 
     /**
      * The current data network list, including the ones that are connected, connecting, or
      * disconnecting.
      */
-    private final @NonNull List<DataNetwork> mDataNetworkList = new ArrayList<>();
+    @NonNull
+    private final List<DataNetwork> mDataNetworkList = new ArrayList<>();
 
     /** {@code true} indicating at least one data network exists. */
     private boolean mAnyDataNetworkExisting;
@@ -320,27 +336,33 @@
     /**
      * Contain the last 10 data networks that were connected. This is for debugging purposes only.
      */
-    private final @NonNull List<DataNetwork> mPreviousConnectedDataNetworkList = new ArrayList<>();
+    @NonNull
+    private final List<DataNetwork> mPreviousConnectedDataNetworkList = new ArrayList<>();
 
     /**
      * The internet data network state. Note that this is the best effort if more than one
      * data network supports internet.
      */
-    private @DataState int mInternetDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
+    @DataState
+    private int mInternetDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
 
     /** All the current connected/handover internet networks.  */
-    @NonNull private Set<DataNetwork> mConnectedInternetNetworks = new HashSet<>();
+    @NonNull
+    private Set<DataNetwork> mConnectedInternetNetworks = new HashSet<>();
 
     /**
      * The IMS data network state. For now this is just for debugging purposes.
      */
-    private @DataState int mImsDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
+    @DataState
+    private int mImsDataNetworkState = TelephonyManager.DATA_DISCONNECTED;
 
     /** Overall aggregated link status from internet data networks. */
-    private @LinkStatus int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
+    @LinkStatus
+    private int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
 
     /** Data network controller callbacks. */
-    private final @NonNull Set<DataNetworkControllerCallback> mDataNetworkControllerCallbacks =
+    @NonNull
+    private final Set<DataNetworkControllerCallback> mDataNetworkControllerCallbacks =
             new ArraySet<>();
 
     /** Indicates if packet switch data is restricted by the cellular network. */
@@ -356,48 +378,59 @@
      * Indicates if the data services are bound. Key if the transport type, and value is the boolean
      * indicating service is bound or not.
      */
-    private final @NonNull SparseBooleanArray mDataServiceBound = new SparseBooleanArray();
+    @NonNull
+    private final SparseBooleanArray mDataServiceBound = new SparseBooleanArray();
 
     /** SIM state. */
-    private @SimState int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
+    @SimState
+    private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
 
     /** Data activity. */
-    private @DataActivityType int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+    @DataActivityType
+    private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
 
     /**
      * IMS state callbacks. Key is the IMS feature, value is the callback.
      */
-    private final @NonNull SparseArray<ImsStateCallback> mImsStateCallbacks = new SparseArray<>();
+    @NonNull
+    private final SparseArray<ImsStateCallback> mImsStateCallbacks = new SparseArray<>();
 
     /** Registered IMS features. Unregistered IMS features are removed from the set. */
-    private final @NonNull Set<Integer> mRegisteredImsFeatures = new ArraySet<>();
+    @NonNull
+    private final Set<Integer> mRegisteredImsFeatures = new ArraySet<>();
 
     /** IMS feature package names. Key is the IMS feature, value is the package name. */
-    private final @NonNull SparseArray<String> mImsFeaturePackageName = new SparseArray<>();
+    @NonNull
+    private final SparseArray<String> mImsFeaturePackageName = new SparseArray<>();
 
     /**
      * Networks that are pending IMS de-registration. Key is the data network, value is the function
      * to tear down the network.
      */
-    private final @NonNull Map<DataNetwork, Runnable> mPendingImsDeregDataNetworks =
-            new ArrayMap<>();
+    @NonNull
+    private final Map<DataNetwork, Runnable> mPendingImsDeregDataNetworks = new ArrayMap<>();
 
     /**
      * IMS feature registration callback. The key is the IMS feature, the value is the registration
      * callback. When new SIM inserted, the old callbacks associated with the old subscription index
      * will be unregistered.
      */
-    private final @NonNull SparseArray<RegistrationManager.RegistrationCallback>
+    @NonNull
+    private final SparseArray<RegistrationManager.RegistrationCallback>
             mImsFeatureRegistrationCallbacks = new SparseArray<>();
 
     /** The counter to detect back to back release/request IMS network. */
-    private @NonNull SlidingWindowEventCounter mImsThrottleCounter;
+    @NonNull
+    private SlidingWindowEventCounter mImsThrottleCounter;
     /** Event counter for unwanted network within time window, is used to trigger anomaly report. */
-    private @NonNull SlidingWindowEventCounter mNetworkUnwantedCounter;
+    @NonNull
+    private SlidingWindowEventCounter mNetworkUnwantedCounter;
     /** Event counter for WLAN setup data failure within time window to trigger anomaly report. */
-    private @NonNull SlidingWindowEventCounter mSetupDataCallWlanFailureCounter;
+    @NonNull
+    private SlidingWindowEventCounter mSetupDataCallWlanFailureCounter;
     /** Event counter for WWAN setup data failure within time window to trigger anomaly report. */
-    private @NonNull SlidingWindowEventCounter mSetupDataCallWwanFailureCounter;
+    @NonNull
+    private SlidingWindowEventCounter mSetupDataCallWwanFailureCounter;
 
     /**
      * The capabilities of the latest released IMS request. To detect back to back release/request
@@ -408,7 +441,8 @@
     /** True after try to release an IMS network; False after try to request an IMS network. */
     private boolean mLastImsOperationIsRelease;
 
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /** The broadcast receiver. */
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -429,7 +463,7 @@
     };
 
     private boolean hasCalling() {
-        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
         return mPhone.getContext().getPackageManager().hasSystemFeature(
             PackageManager.FEATURE_TELEPHONY_CALLING);
     }
@@ -437,7 +471,7 @@
     /**
      * The sorted network request list by priority. The highest priority network request stays at
      * the head of the list. The highest priority is 100, the lowest is 0.
-     *
+     * <p>
      * Note this list is not thread-safe. Do not access the list from different threads.
      */
     @VisibleForTesting
@@ -520,7 +554,8 @@
          * @return The first network request in the list that contains all the provided
          * capabilities.
          */
-        public @Nullable TelephonyNetworkRequest get(@NonNull @NetCapability int[] netCaps) {
+        @Nullable
+        public TelephonyNetworkRequest get(@NonNull @NetCapability int[] netCaps) {
             int index = 0;
             while (index < size()) {
                 TelephonyNetworkRequest networkRequest = get(index);
@@ -529,7 +564,7 @@
                         .boxed()
                         .collect(Collectors.toSet())
                         .containsAll(Arrays.stream(netCaps).boxed()
-                                .collect(Collectors.toList()))) {
+                                .toList())) {
                     return networkRequest;
                 }
                 index++;
@@ -560,6 +595,16 @@
         }
 
         /**
+         * Print "capabilities - connectivity transport". e.g. INTERNET|NOT_RESTRICTED-SATELLITE
+         */
+        @NonNull
+        public String toStringSimplified() {
+            return size() > 0 ? DataUtils.networkCapabilitiesToString(get(0).getCapabilities())
+                    + "-" + DataUtils.connectivityTransportsToString(get(0).getTransportTypes())
+                    : "";
+        }
+
+        /**
          * Dump the network request list.
          *
          * @param pw print writer.
@@ -697,19 +742,26 @@
         private static final String RULE_TAG_ROAMING = "roaming";
 
         /** Handover rule type. */
-        public final @HandoverRuleType int type;
+        @HandoverRuleType
+        public final int type;
 
         /** The applicable source access networks for handover. */
-        public final @NonNull @RadioAccessNetworkType Set<Integer> sourceAccessNetworks;
+        @NonNull
+        @RadioAccessNetworkType
+        public final Set<Integer> sourceAccessNetworks;
 
         /** The applicable target access networks for handover. */
-        public final @NonNull @RadioAccessNetworkType Set<Integer> targetAccessNetworks;
+        @NonNull
+        @RadioAccessNetworkType
+        public final Set<Integer> targetAccessNetworks;
 
         /**
          * The network capabilities to any of which this handover rule applies.
          * If is empty, then capability is ignored as a rule matcher.
          */
-        public final @NonNull @NetCapability Set<Integer> networkCapabilities;
+        @NonNull
+        @NetCapability
+        public final Set<Integer> networkCapabilities;
 
         /** {@code true} indicates this policy is only applicable when the device is roaming. */
         public final boolean isOnlyForRoaming;
@@ -1186,7 +1238,7 @@
                 mSubscriptionPlans.clear();
                 mSubscriptionPlans.addAll(Arrays.asList(plans));
                 mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
-                        () -> cb.onSubscriptionPlanOverride()));
+                        cb::onSubscriptionPlanOverride));
                 break;
             case EVENT_SUBSCRIPTION_OVERRIDE:
                 int overrideMask = msg.arg1;
@@ -1206,7 +1258,7 @@
                         }
                     }
                     mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
-                            () -> cb.onSubscriptionPlanOverride()));
+                            cb::onSubscriptionPlanOverride));
                 } else if (overrideMask == NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED) {
                     log("Congested subscription override: override=" + override
                             + ", networkTypes=" + Arrays.stream(networkTypes)
@@ -1220,7 +1272,7 @@
                         }
                     }
                     mDataNetworkControllerCallbacks.forEach(cb -> cb.invokeFromExecutor(
-                            () -> cb.onSubscriptionPlanOverride()));
+                            cb::onSubscriptionPlanOverride));
                 } else {
                     loge("Unknown override mask: " + overrideMask);
                 }
@@ -1359,28 +1411,6 @@
             // When reaching here, it means this data network can satisfy all the network requests.
             logv("Found a compatible data network " + dataNetwork + ". Attaching "
                     + requestList);
-
-            // If WLAN preferred, see whether a more suitable data profile shall be used to satisfy
-            // a short-lived request that doesn't perform handover.
-            int capability = requestList.getFirst().getApnTypeNetworkCapability();
-            int preferredTransport = mAccessNetworksManager
-                    .getPreferredTransportByNetworkCapability(capability);
-            if (capability == NetworkCapabilities.NET_CAPABILITY_MMS
-                    && preferredTransport != dataNetwork.getTransport()
-                    && preferredTransport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                DataProfile candidate = mDataProfileManager
-                        .getDataProfileForNetworkRequest(requestList.getFirst(),
-                                TelephonyManager.NETWORK_TYPE_IWLAN,
-                                mServiceState.isUsingNonTerrestrialNetwork(),
-                                isEsimBootStrapProvisioningActivated(),
-                                false/*ignorePermanentFailure*/);
-                if (candidate != null && !dataNetwork.getDataProfile().equals(candidate)) {
-                    logv("But skipped because found better data profile " + candidate
-                            + DataUtils.networkCapabilityToString(capability) + " preferred on "
-                            + AccessNetworkConstants.transportTypeToString(preferredTransport));
-                    continue;
-                }
-            }
             return dataNetwork.attachNetworkRequests(requestList);
         }
         return false;
@@ -1452,6 +1482,19 @@
      * still allowed in this case.
      */
     public boolean isInternetDataAllowed(boolean ignoreExistingNetworks) {
+        return !getInternetEvaluation(ignoreExistingNetworks).containsDisallowedReasons();
+    }
+
+    /**
+     * @param ignoreExistingNetworks {@code true} to skip the existing network check.
+     * @return The internet evaluation result.
+     * For example, if SIM is absent, or airplane mode is on, then data is NOT allowed.
+     * This API does not reflect the currently internet data network status. It's possible there is
+     * no internet data due to weak cellular signal or network side issue, but internet data is
+     * still allowed in this case.
+     */
+    @NonNull
+    public DataEvaluation getInternetEvaluation(boolean ignoreExistingNetworks) {
         TelephonyNetworkRequest internetRequest = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -1463,7 +1506,7 @@
                 && mDataNetworkList.stream().anyMatch(
                         dataNetwork -> internetRequest.canBeSatisfiedBy(
                                 dataNetwork.getNetworkCapabilities()))) {
-            return true;
+            return new DataEvaluation(DataEvaluationReason.EXTERNAL_QUERY);
         }
 
         // If no existing network can satisfy the request, then check if we can possibly setup
@@ -1471,17 +1514,19 @@
 
         DataEvaluation evaluation = evaluateNetworkRequest(internetRequest,
                 DataEvaluationReason.EXTERNAL_QUERY);
-        if (evaluation.containsOnly(DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK)) {
+        if (evaluation.containsOnly(DataDisallowedReason.ONLY_ALLOWED_SINGLE_NETWORK)
+                && internetRequest.getPriority() > mDataNetworkList.stream()
+                .map(DataNetwork::getPriority)
+                .max(Comparator.comparing(Integer::valueOf))
+                .orElse(0)) {
             // If the only failed reason is only single network allowed, then check if the request
             // can trump the current network.
-            return internetRequest.getPriority() > mDataNetworkList.stream()
-                    .map(DataNetwork::getPriority)
-                    .max(Comparator.comparing(Integer::valueOf))
-                    .orElse(0);
+            evaluation.addDataAllowedReason(DataAllowedReason.NORMAL);
         }
-        return !evaluation.containsDisallowedReasons();
+        return evaluation;
     }
 
+
     /**
      * @return {@code true} if internet is unmetered.
      */
@@ -1510,7 +1555,8 @@
      * @return List of the reasons why internet data is not allowed. An empty list if internet
      * is allowed.
      */
-    public @NonNull List<DataDisallowedReason> getInternetDataDisallowedReasons() {
+    @NonNull
+    public List<DataDisallowedReason> getInternetDataDisallowedReasons() {
         TelephonyNetworkRequest internetRequest = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -1528,7 +1574,8 @@
      * @param reason The reason for evaluation.
      * @return The data evaluation result.
      */
-    private @NonNull DataEvaluation evaluateNetworkRequest(
+    @NonNull
+    private DataEvaluation evaluateNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) {
         DataEvaluation evaluation = new DataEvaluation(reason);
         int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability(
@@ -1746,7 +1793,7 @@
         networkRequest.setEvaluation(evaluation);
         // EXTERNAL_QUERY generates too many log spam.
         if (reason != DataEvaluationReason.EXTERNAL_QUERY) {
-            log(evaluation.toString() + ", network type="
+            log(evaluation + ", network type="
                     + TelephonyManager.getNetworkTypeName(getDataNetworkType(transport))
                     + ", reg state="
                     + NetworkRegistrationInfo.registrationStateToString(
@@ -1820,7 +1867,8 @@
      * @return The grouped unsatisfied network requests. The network requests that have the same
      * network capabilities is grouped into one {@link NetworkRequestList}.
      */
-    private @NonNull List<NetworkRequestList> getGroupedUnsatisfiedNetworkRequests() {
+    @NonNull
+    private List<NetworkRequestList> getGroupedUnsatisfiedNetworkRequests() {
         NetworkRequestList networkRequestList = new NetworkRequestList();
         for (TelephonyNetworkRequest networkRequest : mAllNetworkRequestList) {
             if (networkRequest.getState() == TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED) {
@@ -1841,8 +1889,7 @@
         log("Re-evaluating " + networkRequestLists.stream().mapToInt(List::size).sum()
                 + " unsatisfied network requests in " + networkRequestLists.size()
                 + " groups, " + networkRequestLists.stream().map(
-                        requestList -> DataUtils.networkCapabilitiesToString(
-                                requestList.get(0).getCapabilities()))
+                        NetworkRequestList::toStringSimplified)
                 .collect(Collectors.joining(", ")) + " due to " + reason);
 
         // Second, see if any existing network can satisfy those network requests.
@@ -1875,7 +1922,8 @@
      *
      * @return The data evaluation result.
      */
-    private @NonNull DataEvaluation evaluateDataNetwork(@NonNull DataNetwork dataNetwork,
+    @NonNull
+    private DataEvaluation evaluateDataNetwork(@NonNull DataNetwork dataNetwork,
             @NonNull DataEvaluationReason reason) {
         DataEvaluation evaluation = new DataEvaluation(reason);
         // Bypass all checks for emergency data network.
@@ -1899,22 +1947,12 @@
         // If the network is satellite, then the network must be restricted.
         if (mFeatureFlags.satelliteInternet()) {
             // The IWLAN data network should remain intact even when satellite is connected.
-            if (dataNetwork.getTransport() != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                // On satellite, every data network needs to be restricted.
-                if (mServiceState.isUsingNonTerrestrialNetwork()
-                        && dataNetwork.getNetworkCapabilities()
-                        .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
-                    evaluation.addDataDisallowedReason(
-                            DataDisallowedReason.DATA_NETWORK_TRANSPORT_NOT_ALLOWED);
-                }
-
-                // Check if the transport is compatible with the network
-                if (mServiceState.isUsingNonTerrestrialNetwork() != dataNetwork.isSatellite()) {
-                    // Since we don't support satellite/cellular network handover, we should always
-                    // tear down the network when transport changes.
-                    evaluation.addDataDisallowedReason(
-                            DataDisallowedReason.DATA_NETWORK_TRANSPORT_NOT_ALLOWED);
-                }
+            if (dataNetwork.getTransport() != AccessNetworkConstants.TRANSPORT_TYPE_WLAN
+                    && mServiceState.isUsingNonTerrestrialNetwork() != dataNetwork.isSatellite()) {
+                // Since we don't support satellite/cellular network handover, we should always
+                // tear down the network when transport changes.
+                evaluation.addDataDisallowedReason(
+                        DataDisallowedReason.DATA_NETWORK_TRANSPORT_NOT_ALLOWED);
             }
         }
 
@@ -2116,35 +2154,43 @@
      */
     private boolean canConnectivityTransportSatisfyNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @TransportType int transport) {
-        // When the device is on satellite, only restricted network request can request network.
-        if (mServiceState.isUsingNonTerrestrialNetwork()
-                && networkRequest.getNativeNetworkRequest().hasCapability(
-                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
-            return false;
+        // Check if this is a IWLAN network request.
+        if (transport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+            // If the request would result in bringing up network on IWLAN, then no
+            // need to check if the device is using satellite network.
+            return true;
+        }
+
+        // When the device is on satellite, only restricted/constrained network request can request
+        // network.
+        if (mServiceState.isUsingNonTerrestrialNetwork() && networkRequest.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
+            switch (mDataConfigManager.getSatelliteDataSupportMode()) {
+                case CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED -> {
+                    return false;
+                }
+                case CarrierConfigManager.SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED -> {
+                    try {
+                        if (networkRequest.hasCapability(DataUtils
+                                .NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED)) {
+                            return false;
+                        }
+                    } catch (Exception ignored) { }
+                }
+                // default case CarrierConfigManager.SATELLITE_DATA_SUPPORT_ALL
+            }
         }
 
         // If the network request does not specify cellular or satellite, then it can be
         // satisfied when the device is either on cellular ot satellite.
-        if (!networkRequest.getNativeNetworkRequest().hasTransport(
-                NetworkCapabilities.TRANSPORT_CELLULAR)
-                && !networkRequest.getNativeNetworkRequest().hasTransport(
-                        NetworkCapabilities.TRANSPORT_SATELLITE)) {
-            return true;
-        }
-
-        // Check if this is a IWLAN network request.
-        if (networkRequest.getNativeNetworkRequest().hasTransport(
-                NetworkCapabilities.TRANSPORT_CELLULAR)
-                && transport == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-            // If the cellular request would result in bringing up network on IWLAN, then no
-            // need to check if the device is using satellite network.
+        if (!networkRequest.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+                && !networkRequest.hasTransport(NetworkCapabilities.TRANSPORT_SATELLITE)) {
             return true;
         }
 
         // As a short term solution, allowing some networks to be always marked as cellular
         // transport if certain capabilities are in the network request.
-        if (networkRequest.getNativeNetworkRequest().hasTransport(
-                NetworkCapabilities.TRANSPORT_CELLULAR) && Arrays.stream(
+        if (networkRequest.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) && Arrays.stream(
                         networkRequest.getCapabilities())
                 .anyMatch(mDataConfigManager.getForcedCellularTransportCapabilities()::contains)) {
             return true;
@@ -2154,11 +2200,9 @@
         // the network is satellite, then the request must specify satellite transport and
         // restricted.
         return (mServiceState.isUsingNonTerrestrialNetwork()
-                && networkRequest.getNativeNetworkRequest().hasTransport(
-                        NetworkCapabilities.TRANSPORT_SATELLITE))
+                && networkRequest.hasTransport(NetworkCapabilities.TRANSPORT_SATELLITE))
                 || (!mServiceState.isUsingNonTerrestrialNetwork()
-                        && networkRequest.getNativeNetworkRequest().hasTransport(
-                        NetworkCapabilities.TRANSPORT_CELLULAR));
+                        && networkRequest.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR));
     }
 
     /**
@@ -2221,7 +2265,8 @@
      *
      * @see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY
      */
-    private @NonNull DataEvaluation evaluateDataNetworkHandover(@NonNull DataNetwork dataNetwork) {
+    @NonNull
+    private DataEvaluation evaluateDataNetworkHandover(@NonNull DataNetwork dataNetwork) {
         DataEvaluation dataEvaluation = new DataEvaluation(DataEvaluationReason.DATA_HANDOVER);
         if (!dataNetwork.isConnecting() && !dataNetwork.isConnected()) {
             dataEvaluation.addDataDisallowedReason(DataDisallowedReason.ILLEGAL_STATE);
@@ -2279,10 +2324,7 @@
                     sourceNetworkType);
             NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
                     NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            boolean isWwanInService = false;
-            if (nri != null && nri.isInService()) {
-                isWwanInService = true;
-            }
+            boolean isWwanInService = nri != null && nri.isInService();
             // If WWAN is inService, use the real roaming state reported by modem instead of
             // using the overridden roaming state, otherwise get last known roaming state stored
             // in data network.
@@ -2344,7 +2386,8 @@
      * {@link #evaluateDataNetwork(DataNetwork, DataEvaluationReason)}.
      * @return The tear down reason.
      */
-    private static @TearDownReason int getTearDownReason(@NonNull DataEvaluation dataEvaluation) {
+    @TearDownReason
+    private static int getTearDownReason(@NonNull DataEvaluation dataEvaluation) {
         if (dataEvaluation.containsDisallowedReasons()) {
             switch (dataEvaluation.getDataDisallowedReasons().get(0)) {
                 case DATA_DISABLED:
@@ -2545,14 +2588,14 @@
         RegistrationManager.RegistrationCallback callback =
                 new RegistrationManager.RegistrationCallback() {
                     @Override
-                    public void onRegistered(ImsRegistrationAttributes attributes) {
+                    public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
                         log("IMS " + DataUtils.imsFeatureToString(imsFeature)
                                 + " registered. Attributes=" + attributes);
                         mRegisteredImsFeatures.add(imsFeature);
                     }
 
                     @Override
-                    public void onUnregistered(ImsReasonInfo info) {
+                    public void onUnregistered(@NonNull ImsReasonInfo info) {
                         log("IMS " + DataUtils.imsFeatureToString(imsFeature)
                                 + " deregistered. Info=" + info);
                         mRegisteredImsFeatures.remove(imsFeature);
@@ -2760,8 +2803,8 @@
      * @param dataProfile The data profile.
      * @return The network requests list.
      */
-    private @NonNull NetworkRequestList findSatisfiableNetworkRequests(
-            @NonNull DataProfile dataProfile) {
+    @NonNull
+    private NetworkRequestList findSatisfiableNetworkRequests(@NonNull DataProfile dataProfile) {
         return new NetworkRequestList(mAllNetworkRequestList.stream()
                 .filter(request -> request.getState()
                         == TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED)
@@ -3060,8 +3103,18 @@
             List<NetworkRequestList> groupRequestLists = getGroupedUnsatisfiedNetworkRequests();
             dataSetupRetryEntry.networkRequestList.stream()
                     .filter(request -> groupRequestLists.stream()
-                            .anyMatch(groupRequestList -> groupRequestList
-                                    .get(request.getCapabilities()) != null))
+                            .anyMatch(groupRequestList -> {
+                                // The unsatisfied request has all the requested capabilities.
+                                if (groupRequestList.get(request.getCapabilities()) == null) {
+                                    return false;
+                                }
+                                TelephonyNetworkRequest leading = groupRequestList.getFirst();
+                                // The unsatisfied request covers all the requested transports.
+                                return leading.getTransportTypes().length == 0
+                                        || request.getTransportTypes().length == 0
+                                        || Arrays.stream(request.getTransportTypes())
+                                        .allMatch(leading::hasTransport);
+                            }))
                     .forEach(requestList::add);
         }
         if (requestList.isEmpty()) {
@@ -3701,17 +3754,13 @@
         DataSpecificRegistrationInfo newDsri = newNri.getDataSpecificInfo();
 
         if (newDsri == null) return false;
-        if ((oldDsri == null || oldDsri.getVopsSupportInfo() == null
+        // If previously VoPS was supported (or does not exist), and now the network reports
+        // VoPS not supported, we should evaluate existing data networks to see if they need
+        // to be torn down.
+        return (oldDsri == null || oldDsri.getVopsSupportInfo() == null
                 || oldDsri.getVopsSupportInfo().isVopsSupported())
                 && (newDsri.getVopsSupportInfo() != null && !newDsri.getVopsSupportInfo()
-                .isVopsSupported())) {
-            // If previously VoPS was supported (or does not exist), and now the network reports
-            // VoPS not supported, we should evaluate existing data networks to see if they need
-            // to be torn down.
-            return true;
-        }
-
-        return false;
+                .isVopsSupported());
     }
 
     /**
@@ -3758,17 +3807,13 @@
         DataSpecificRegistrationInfo newDsri = newPsNri.getDataSpecificInfo();
 
         if (oldDsri == null) return false;
-        if ((newDsri == null || newDsri.getVopsSupportInfo() == null
+        // If previously VoPS was not supported, and now the network reports
+        // VoPS supported (or does not report), we should evaluate the unsatisfied network
+        // request to see if the can be satisfied again.
+        return (newDsri == null || newDsri.getVopsSupportInfo() == null
                 || newDsri.getVopsSupportInfo().isVopsSupported())
                 && (oldDsri.getVopsSupportInfo() != null && !oldDsri.getVopsSupportInfo()
-                .isVopsSupported())) {
-            // If previously VoPS was not supported, and now the network reports
-            // VoPS supported (or does not report), we should evaluate the unsatisfied network
-            // request to see if the can be satisfied again.
-            return true;
-        }
-
-        return false;
+                .isVopsSupported());
     }
 
     /**
@@ -3879,28 +3924,32 @@
     /**
      * @return Data config manager instance.
      */
-    public @NonNull DataConfigManager getDataConfigManager() {
+    @NonNull
+    public DataConfigManager getDataConfigManager() {
         return mDataConfigManager;
     }
 
     /**
      * @return Data profile manager instance.
      */
-    public @NonNull DataProfileManager getDataProfileManager() {
+    @NonNull
+    public DataProfileManager getDataProfileManager() {
         return mDataProfileManager;
     }
 
     /**
      * @return Data settings manager instance.
      */
-    public @NonNull DataSettingsManager getDataSettingsManager() {
+    @NonNull
+    public DataSettingsManager getDataSettingsManager() {
         return mDataSettingsManager;
     }
 
     /**
      * @return Data retry manager instance.
      */
-    public @NonNull DataRetryManager getDataRetryManager() {
+    @NonNull
+    public DataRetryManager getDataRetryManager() {
         return mDataRetryManager;
     }
 
@@ -3908,7 +3957,8 @@
      * @return The list of SubscriptionPlans
      */
     @VisibleForTesting
-    public @NonNull List<SubscriptionPlan> getSubscriptionPlans() {
+    @NonNull
+    public List<SubscriptionPlan> getSubscriptionPlans() {
         return mSubscriptionPlans;
     }
 
@@ -3916,7 +3966,9 @@
      * @return The set of network types an unmetered override applies to
      */
     @VisibleForTesting
-    public @NonNull @NetworkType Set<Integer> getUnmeteredOverrideNetworkTypes() {
+    @NonNull
+    @NetworkType
+    public Set<Integer> getUnmeteredOverrideNetworkTypes() {
         return mUnmeteredOverrideNetworkTypes;
     }
 
@@ -3924,7 +3976,9 @@
      * @return The set of network types a congested override applies to
      */
     @VisibleForTesting
-    public @NonNull @NetworkType Set<Integer> getCongestedOverrideNetworkTypes() {
+    @NonNull
+    @NetworkType
+    public Set<Integer> getCongestedOverrideNetworkTypes() {
         return mCongestedOverrideNetworkTypes;
     }
 
@@ -3934,7 +3988,8 @@
      * @param transport The transport.
      * @return The current network type.
      */
-    private @NetworkType int getDataNetworkType(@TransportType int transport) {
+    @NetworkType
+    private int getDataNetworkType(@TransportType int transport) {
         NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, transport);
         if (nri != null) {
@@ -3950,8 +4005,8 @@
      * @param transport The transport.
      * @return The registration state.
      */
-    private @RegistrationState int getDataRegistrationState(@NonNull ServiceState ss,
-            @TransportType int transport) {
+    @RegistrationState
+    private int getDataRegistrationState(@NonNull ServiceState ss, @TransportType int transport) {
         NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, transport);
         if (nri != null) {
@@ -3963,7 +4018,8 @@
     /**
      * @return The data activity. Note this is only updated when screen is on.
      */
-    public @DataActivityType int getDataActivity() {
+    @DataActivityType
+    public int getDataActivity() {
         return mDataActivity;
     }
 
@@ -4109,14 +4165,16 @@
      *
      * @return The data network state.
      */
-    public @DataState int getInternetDataNetworkState() {
+    @DataState
+    public int getInternetDataNetworkState() {
         return mInternetDataNetworkState;
     }
 
     /**
      * @return List of bound data service packages name on WWAN and WLAN.
      */
-    public @NonNull List<String> getDataServicePackages() {
+    @NonNull
+    public List<String> getDataServicePackages() {
         List<String> packages = new ArrayList<>();
         for (int i = 0; i < mDataServiceManagers.size(); i++) {
             packages.add(mDataServiceManagers.valueAt(i).getDataServicePackageName());
@@ -4126,8 +4184,8 @@
 
     /**
      * Request network validation.
-     *
-     * Nnetwork validation request is sent to the DataNetwork that matches the network capability
+     * <p>
+     * Network validation request is sent to the DataNetwork that matches the network capability
      * in the list of DataNetwork owned by the DNC.
      *
      * @param capability network capability {@link NetCapability}
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 2a2a792..c50d50a 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -82,43 +82,51 @@
     private final LocalLog mLocalLog = new LocalLog(128);
 
     /** Data network controller. */
-    private final @NonNull DataNetworkController mDataNetworkController;
+    @NonNull
+    private final DataNetworkController mDataNetworkController;
 
     /** Data config manager. */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Cellular data service. */
-    private final @NonNull DataServiceManager mWwanDataServiceManager;
+    @NonNull
+    private final DataServiceManager mWwanDataServiceManager;
 
     /**
      * All data profiles for the current carrier. Note only data profiles loaded from the APN
      * database will be stored here. The on-demand data profiles (generated dynamically, for
      * example, enterprise data profiles with differentiator) are not stored here.
      */
-    private final @NonNull List<DataProfile> mAllDataProfiles = new ArrayList<>();
+    @NonNull
+    private final List<DataProfile> mAllDataProfiles = new ArrayList<>();
 
     /** The data profile used for initial attach. */
-    private @Nullable DataProfile mInitialAttachDataProfile = null;
+    @Nullable
+    private DataProfile mInitialAttachDataProfile = null;
 
     /** The preferred data profile used for internet. */
-    private @Nullable DataProfile mPreferredDataProfile = null;
+    @Nullable
+    private DataProfile mPreferredDataProfile = null;
 
     /** The last data profile that's successful for internet connection by subscription id. */
-    private final @NonNull LruCache<Integer, DataProfile> mLastInternetDataProfiles =
-            new LruCache<>(256);
+    @NonNull
+    private final LruCache<Integer, DataProfile> mLastInternetDataProfiles = new LruCache<>(256);
 
     /** Preferred data profile set id. */
     private int mPreferredDataProfileSetId = Telephony.Carriers.NO_APN_SET_ID;
 
     /** Data profile manager callbacks. */
-    private final @NonNull Set<DataProfileManagerCallback> mDataProfileManagerCallbacks =
-            new ArraySet<>();
+    @NonNull
+    private final Set<DataProfileManagerCallback> mDataProfileManagerCallbacks = new ArraySet<>();
 
     /** SIM state. */
-    private @SimState int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
+    @SimState
+    private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
 
     /** Feature flags controlling which feature is enabled. */
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /**
      * Data profile manager callback. This should be only used by {@link DataNetworkController}.
@@ -477,7 +485,8 @@
      *
      * @return The preferred data profile.
      */
-    private @Nullable DataProfile getPreferredDataProfileFromDb() {
+    @Nullable
+    private DataProfile getPreferredDataProfileFromDb() {
         Cursor cursor = mPhone.getContext().getContentResolver().query(
                 Uri.withAppendedPath(Telephony.Carriers.PREFERRED_APN_URI,
                         String.valueOf(mPhone.getSubId())), null, null, null,
@@ -502,7 +511,8 @@
     /**
      * @return The preferred data profile from carrier config.
      */
-    private @Nullable DataProfile getPreferredDataProfileFromConfig() {
+    @Nullable
+    private DataProfile getPreferredDataProfileFromConfig() {
         // Check if there is configured default preferred data profile.
         String defaultPreferredApn = mDataConfigManager.getDefaultPreferredApn();
         if (!TextUtils.isEmpty(defaultPreferredApn)) {
@@ -586,10 +596,10 @@
 
     /**
      * Update the data profile used for initial attach.
-     *
+     * <p>
      * Note that starting from Android 13 only APNs that supports "IA" type will be used for
      * initial attach. Please update APN configuration file if needed.
-     *
+     * <p>
      * Some carriers might explicitly require that using "user-added" APN for initial
      * attach. In this case, exception can be configured through
      * {@link CarrierConfigManager#KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY}.
@@ -642,7 +652,8 @@
      * @param apnTypeBitmask APN type
      * @return The APN setting
      */
-    private @NonNull ApnSetting buildDefaultApnSetting(@NonNull String entry,
+    @NonNull
+    private ApnSetting buildDefaultApnSetting(@NonNull String entry,
             @NonNull String apn, @Annotation.ApnType int apnTypeBitmask) {
         return new ApnSetting.Builder()
                 .setEntryName(entry)
@@ -665,7 +676,8 @@
      * This should be set to true for condition-based retry/setup.
      * @return The data profile. {@code null} if can't find any satisfiable data profile.
      */
-    public @Nullable DataProfile getDataProfileForNetworkRequest(
+    @Nullable
+    public DataProfile getDataProfileForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
             boolean isNtn, boolean isEsimBootstrapProvisioning, boolean ignorePermanentFailure) {
         ApnSetting apnSetting = null;
@@ -736,7 +748,8 @@
      * This should be set to true for condition-based retry/setup.
      * @return The APN setting. {@code null} if can't find any satisfiable data profile.
      */
-    private @Nullable ApnSetting getApnSettingForNetworkRequest(
+    @Nullable
+    private ApnSetting getApnSettingForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
             boolean isNtn, boolean isEsimBootStrapProvisioning, boolean ignorePermanentFailure) {
         if (!networkRequest.hasAttribute(
@@ -996,7 +1009,8 @@
      *
      * @return The merged data profile. {@code null} if merging is not possible.
      */
-    private static @Nullable DataProfile mergeDataProfiles(
+    @Nullable
+    private static DataProfile mergeDataProfiles(
             @NonNull DataProfile dp1, @NonNull DataProfile dp2) {
         Objects.requireNonNull(dp1);
         Objects.requireNonNull(dp2);
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index 7454d01..d35e9e8 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -150,53 +150,67 @@
     private static final int RESET_REASON_TAC_CHANGED = 6;
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /** Featureflags. */
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
 
     /** The RIL instance. */
-    private final @NonNull CommandsInterface mRil;
+    @NonNull
+    private final CommandsInterface mRil;
 
     /** Logging tag. */
-    private final @NonNull String mLogTag;
+    @NonNull
+    private final String mLogTag;
 
     /** Local log. */
-    private final @NonNull LocalLog mLocalLog = new LocalLog(128);
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(128);
 
     /** Alarm Manager used to schedule long set up or handover retries. */
-    private final @NonNull AlarmManager mAlarmManager;
+    @NonNull
+    private final AlarmManager mAlarmManager;
 
     /**
      * The data retry callback. This is only used to notify {@link DataNetworkController} to retry
      * setup data network.
      */
-    private @NonNull Set<DataRetryManagerCallback> mDataRetryManagerCallbacks = new ArraySet<>();
+    @NonNull
+    private final Set<DataRetryManagerCallback> mDataRetryManagerCallbacks = new ArraySet<>();
 
     /** Data service managers. */
-    private @NonNull SparseArray<DataServiceManager> mDataServiceManagers;
+    @NonNull
+    private final SparseArray<DataServiceManager> mDataServiceManagers;
 
     /** Data config manager instance. */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Data profile manager. */
-    private final @NonNull DataProfileManager mDataProfileManager;
+    @NonNull
+    private final DataProfileManager mDataProfileManager;
 
     /** Data setup retry rule list. */
-    private @NonNull List<DataSetupRetryRule> mDataSetupRetryRuleList = new ArrayList<>();
+    @NonNull
+    private List<DataSetupRetryRule> mDataSetupRetryRuleList = new ArrayList<>();
 
     /** Data handover retry rule list. */
-    private @NonNull List<DataHandoverRetryRule> mDataHandoverRetryRuleList = new ArrayList<>();
+    @NonNull
+    private List<DataHandoverRetryRule> mDataHandoverRetryRuleList = new ArrayList<>();
 
     /** Data retry entries. */
-    private final @NonNull List<DataRetryEntry> mDataRetryEntries = new ArrayList<>();
+    @NonNull
+    private final List<DataRetryEntry> mDataRetryEntries = new ArrayList<>();
 
     /**
      * Data throttling entries. Note this only stores throttling requested by networks. We intended
      * not to store frameworks-initiated throttling because they are not explicit/strong throttling
      * requests.
      */
-    private final @NonNull List<DataThrottlingEntry> mDataThrottlingEntries = new ArrayList<>();
+    @NonNull
+    private final List<DataThrottlingEntry> mDataThrottlingEntries = new ArrayList<>();
 
     /**
      * Represent a single data setup/handover throttling reported by networks.
@@ -205,31 +219,37 @@
         /**
          * The data profile that is being throttled for setup/handover retry.
          */
-        public final @NonNull DataProfile dataProfile;
+        @NonNull
+        public final DataProfile dataProfile;
 
         /**
          * The associated network request list when throttling happened. Should be {@code null} when
          * retry type is {@link ThrottleStatus#RETRY_TYPE_HANDOVER}.
          */
-        public final @Nullable NetworkRequestList networkRequestList;
+        @Nullable
+        public final NetworkRequestList networkRequestList;
 
         /**
          * The data network that is being throttled for handover retry. Should be
          * {@code null} when retryType is {@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION}.
          */
-        public final @Nullable DataNetwork dataNetwork;
+        @Nullable
+        public final DataNetwork dataNetwork;
 
         /** The transport that the data profile has been throttled on. */
-        public final @TransportType int transport;
+        @TransportType
+        public final int transport;
 
         /** The retry type when throttling expires. */
-        public final @RetryType int retryType;
+        @RetryType
+        public final int retryType;
 
         /**
          * The expiration time of data throttling. This is the time retrieved from
          * {@link SystemClock#elapsedRealtime()}.
          */
-        public final @ElapsedRealtimeLong long expirationTimeMillis;
+        @ElapsedRealtimeLong
+        public final long expirationTimeMillis;
 
         /**
          * Constructor.
@@ -257,7 +277,8 @@
         }
 
         @Override
-        public @NonNull String toString() {
+        @NonNull
+        public String toString() {
             return "[DataThrottlingEntry: dataProfile=" + dataProfile + ", request list="
                     + networkRequestList + ", dataNetwork=" + dataNetwork + ", transport="
                     + AccessNetworkConstants.transportTypeToString(transport) + ", expiration time="
@@ -293,13 +314,17 @@
          * capabilities specified here, then retry will happen. Empty set indicates the retry rule
          * is not using network capabilities.
          */
-        protected @NonNull @NetCapability Set<Integer> mNetworkCapabilities = new ArraySet<>();
+        @NonNull
+        @NetCapability
+        protected Set<Integer> mNetworkCapabilities = new ArraySet<>();
 
         /**
          * The fail causes. If data setup failed with certain fail causes, then retry will happen.
          * Empty set indicates the retry rule is not using the fail causes.
          */
-        protected @NonNull @DataFailureCause Set<Integer> mFailCauses = new ArraySet<>();
+        @NonNull
+        @DataFailureCause
+        protected Set<Integer> mFailCauses = new ArraySet<>();
 
         public DataRetryRule(@NonNull String ruleString) {
             if (TextUtils.isEmpty(ruleString)) {
@@ -353,7 +378,8 @@
          * @return The data network setup retry intervals in milliseconds. If this is empty, then
          * {@link #getMaxRetries()} must return 0.
          */
-        public @NonNull List<Long> getRetryIntervalsMillis() {
+        @NonNull
+        public List<Long> getRetryIntervalsMillis() {
             return mRetryIntervalsMillis;
         }
 
@@ -372,43 +398,44 @@
          * happen. Empty set indicates the retry rule is not using the fail causes.
          */
         @VisibleForTesting
-        public @NonNull @DataFailureCause Set<Integer> getFailCauses() {
+        @NonNull
+        @DataFailureCause
+        public Set<Integer> getFailCauses() {
             return mFailCauses;
         }
     }
 
     /**
      * Represent a rule for data setup retry.
-     *
+     * <p>
      * The syntax of the retry rule:
      * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
      *    are supported. If the capabilities are not specified, then the retry rule only applies
      *    to the current failed APN used in setup data call request.
      * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
-     *
+     * <p>
      * 2. Retry based on {@link DataFailCause}
      * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
-     *
+     * <p>
      * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only
      *    APN-type network capabilities are supported.
      * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
      *     [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
-     *
+     * <p>
      * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval
      *    is specified for retrying the next available APN.
      * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547|
      *     2252|2253|2254, retry_interval=2500"
-     *
+     * <p>
      * For example,
      * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
      * network request is emergency, then retry data network setup every 1 second for up to 20
      * times.
-     *
+     * <p>
      * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
      * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
      * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
      * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
-     *
      */
     public static class DataSetupRetryRule extends DataRetryRule {
         private static final String RULE_TAG_PERMANENT_FAIL_CAUSES = "permanent_fail_causes";
@@ -469,7 +496,9 @@
          * capabilities.
          */
         @VisibleForTesting
-        public @NonNull @NetCapability Set<Integer> getNetworkCapabilities() {
+        @NonNull
+        @NetCapability
+        public Set<Integer> getNetworkCapabilities() {
             return mNetworkCapabilities;
         }
 
@@ -510,22 +539,22 @@
 
     /**
      * Represent a handover data network retry rule.
-     *
+     * <p>
      * The syntax of the retry rule:
      * 1. Retry when handover fails.
      * "retry_interval=[n1|n2|n3|...], [maximum_retries=n]"
-     *
+     * <p>
      * For example,
      * "retry_interval=1000|3000|5000, maximum_retries=10" means handover retry will happen in 1s,
      * 3s, 5s, 5s, 5s....up to 10 times.
-     *
+     * <p>
      * 2. Retry when handover fails with certain fail causes.
      * "retry_interval=[n1|n2|n3|...], fail_causes=[cause1|cause2|cause3|...], [maximum_retries=n]
-     *
+     * <p>
      * For example,
      * "retry_interval=1000, maximum_retries=3, fail_causes=5" means handover retry every 1 second
      * for up to 3 times when handover fails with the cause 5.
-     *
+     * <p>
      * "maximum_retries=0, fail_causes=6|10|67" means handover retry should not happen for those
      * causes.
      */
@@ -573,7 +602,8 @@
         public @interface DataRetryState {}
 
         /** The rule used for this data retry. {@code null} if the retry is requested by network. */
-        public final @Nullable DataRetryRule appliedDataRetryRule;
+        @Nullable
+        public final DataRetryRule appliedDataRetryRule;
 
         /** The retry delay in milliseconds. */
         public final long retryDelayMillis;
@@ -582,13 +612,15 @@
          * Retry elapsed time. This is the system elapsed time retrieved from
          * {@link SystemClock#elapsedRealtime()}.
          */
-        public final @ElapsedRealtimeLong long retryElapsedTime;
+        @ElapsedRealtimeLong
+        public final long retryElapsedTime;
 
         /** The retry state. */
         protected int mRetryState = RETRY_STATE_NOT_RETRIED;
 
         /** Timestamp when a state is set. For debugging purposes only. */
-        protected @ElapsedRealtimeLong long mRetryStateTimestamp = 0;
+        @ElapsedRealtimeLong
+        protected long mRetryStateTimestamp;
 
         /**
          * Constructor
@@ -617,7 +649,8 @@
         /**
          * @return Get the retry state.
          */
-        public @DataRetryState int getState() {
+        @DataRetryState
+        public int getState() {
             return mRetryState;
         }
 
@@ -649,7 +682,8 @@
             protected long mRetryDelayMillis = TimeUnit.SECONDS.toMillis(5);
 
             /** The applied data retry rule. */
-            protected @Nullable DataRetryRule mAppliedDataRetryRule;
+            @Nullable
+            protected DataRetryRule mAppliedDataRetryRule;
 
             /**
              * Set the data retry delay.
@@ -657,7 +691,8 @@
              * @param retryDelayMillis The retry delay in milliseconds.
              * @return This builder.
              */
-            public @NonNull T setRetryDelay(long retryDelayMillis) {
+            @NonNull
+            public T setRetryDelay(long retryDelayMillis) {
                 mRetryDelayMillis = retryDelayMillis;
                 return (T) this;
             }
@@ -668,7 +703,8 @@
              * @param dataRetryRule The rule that used for this data retry.
              * @return This builder.
              */
-            public @NonNull T setAppliedRetryRule(@NonNull DataRetryRule dataRetryRule) {
+            @NonNull
+            public T setAppliedRetryRule(@NonNull DataRetryRule dataRetryRule) {
                 mAppliedDataRetryRule = dataRetryRule;
                 return (T) this;
             }
@@ -703,16 +739,20 @@
         public @interface SetupRetryType {}
 
         /** Setup retry type. Could be retry by same data profile or same capability. */
-        public final @SetupRetryType int setupRetryType;
+        @SetupRetryType
+        public final int setupRetryType;
 
         /** The network requests to satisfy when retry happens. */
-        public final @NonNull NetworkRequestList networkRequestList;
+        @NonNull
+        public final NetworkRequestList networkRequestList;
 
         /** The data profile that will be used for retry. */
-        public final @Nullable DataProfile dataProfile;
+        @Nullable
+        public final DataProfile dataProfile;
 
         /** The transport to retry data setup. */
-        public final @TransportType int transport;
+        @TransportType
+        public final int transport;
 
         /**
          * Constructor
@@ -743,11 +783,12 @@
          * @return Retry type in string format.
          */
         private static String retryTypeToString(@SetupRetryType int setupRetryType) {
-            switch (setupRetryType) {
-                case RETRY_TYPE_DATA_PROFILE: return "BY_PROFILE";
-                case RETRY_TYPE_NETWORK_REQUESTS: return "BY_NETWORK_REQUESTS";
-                default: return "Unknown(" + setupRetryType + ")";
-            }
+            return switch (setupRetryType) {
+                case RETRY_TYPE_DATA_PROFILE -> "BY_PROFILE";
+                case RETRY_TYPE_NETWORK_REQUESTS -> "BY_NETWORK_REQUESTS";
+                case RETRY_TYPE_UNKNOWN -> "UNKNOWN";
+                default -> "Unknown(" + setupRetryType + ")";
+            };
         }
 
         @Override
@@ -768,16 +809,20 @@
          */
         public static class Builder<T extends Builder<T>> extends DataRetryEntry.Builder<T> {
             /** Data setup retry type. Could be retry by same data profile or same capabilities. */
-            private @SetupRetryType int mSetupRetryType = RETRY_TYPE_UNKNOWN;
+            @SetupRetryType
+            private int mSetupRetryType = RETRY_TYPE_UNKNOWN;
 
             /** The network requests to satisfy when retry happens. */
-            private @NonNull NetworkRequestList mNetworkRequestList;
+            @NonNull
+            private NetworkRequestList mNetworkRequestList;
 
             /** The data profile that will be used for retry. */
-            private @Nullable DataProfile mDataProfile;
+            @Nullable
+            private DataProfile mDataProfile;
 
             /** The transport to retry data setup. */
-            private @TransportType int mTransport = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+            @TransportType
+            private int mTransport = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
 
             /**
              * Set the data retry type.
@@ -786,7 +831,8 @@
              * capabilities.
              * @return This builder.
              */
-            public @NonNull Builder<T> setSetupRetryType(@SetupRetryType int setupRetryType) {
+            @NonNull
+            public Builder<T> setSetupRetryType(@SetupRetryType int setupRetryType) {
                 mSetupRetryType = setupRetryType;
                 return this;
             }
@@ -797,7 +843,8 @@
              * @param networkRequestList The network requests to satisfy when retry happens.
              * @return This builder.
              */
-            public @NonNull Builder<T> setNetworkRequestList(
+            @NonNull
+            public Builder<T> setNetworkRequestList(
                     @NonNull NetworkRequestList networkRequestList) {
                 mNetworkRequestList = networkRequestList;
                 return this;
@@ -809,7 +856,8 @@
              * @param dataProfile The data profile that will be used for retry.
              * @return This builder.
              */
-            public @NonNull Builder<T> setDataProfile(@NonNull DataProfile dataProfile) {
+            @NonNull
+            public Builder<T> setDataProfile(@NonNull DataProfile dataProfile) {
                 mDataProfile = dataProfile;
                 return this;
             }
@@ -820,7 +868,8 @@
              * @param transport The transport to retry data setup.
              * @return This builder.
              */
-            public @NonNull Builder<T> setTransport(@TransportType int transport) {
+            @NonNull
+            public Builder<T> setTransport(@TransportType int transport) {
                 mTransport = transport;
                 return this;
             }
@@ -830,7 +879,8 @@
              *
              * @return The instance of {@link DataSetupRetryEntry}.
              */
-            public @NonNull DataSetupRetryEntry build() {
+            @NonNull
+            public DataSetupRetryEntry build() {
                 if (mNetworkRequestList == null) {
                     throw new IllegalArgumentException("network request list is not specified.");
                 }
@@ -854,7 +904,8 @@
      */
     public static class DataHandoverRetryEntry extends DataRetryEntry {
         /** The data network to be retried for handover. */
-        public final @NonNull DataNetwork dataNetwork;
+        @NonNull
+        public final DataNetwork dataNetwork;
 
         /**
          * Constructor.
@@ -886,7 +937,8 @@
          */
         public static class Builder<T extends Builder<T>> extends DataRetryEntry.Builder<T> {
             /** The data network to be retried for handover. */
-            public @NonNull DataNetwork mDataNetwork;
+            @NonNull
+            public DataNetwork mDataNetwork;
 
             /**
              * Set the data retry type.
@@ -895,7 +947,8 @@
              *
              * @return This builder.
              */
-            public @NonNull Builder<T> setDataNetwork(@NonNull DataNetwork dataNetwork) {
+            @NonNull
+            public Builder<T> setDataNetwork(@NonNull DataNetwork dataNetwork) {
                 mDataNetwork = dataNetwork;
                 return this;
             }
@@ -905,7 +958,8 @@
              *
              * @return The instance of {@link DataHandoverRetryEntry}.
              */
-            public @NonNull DataHandoverRetryEntry build() {
+            @NonNull
+            public DataHandoverRetryEntry build() {
                 return new DataHandoverRetryEntry(mDataNetwork,
                         (DataHandoverRetryRule) mAppliedDataRetryRule, mRetryDelayMillis);
             }
@@ -1865,7 +1919,8 @@
      * @param reason The reason
      * @return The reason in string format.
      */
-    private static @NonNull String resetReasonToString(int reason) {
+    @NonNull
+    private static String resetReasonToString(int reason) {
         return switch (reason) {
             case RESET_REASON_DATA_PROFILES_CHANGED -> "DATA_PROFILES_CHANGED";
             case RESET_REASON_RADIO_ON -> "RADIO_ON";
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index 51e5b7d..fb112d9 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -47,6 +47,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SettingsObserver;
+import com.android.internal.telephony.TelephonyCapabilities;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats;
@@ -91,7 +92,8 @@
     private static final int EVENT_INITIALIZE = 11;
 
     private final Phone mPhone;
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
     private final ContentResolver mResolver;
     private final SettingsObserver mSettingsObserver;
     private final String mLogTag;
@@ -100,11 +102,12 @@
     private int mSubId;
 
     /** Data config manager */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Data settings manager callbacks. */
-    private final @NonNull Set<DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
-            new ArraySet<>();
+    @NonNull
+    private final Set<DataSettingsManagerCallback> mDataSettingsManagerCallbacks = new ArraySet<>();
 
     /** Mapping of {@link TelephonyManager.DataEnabledReason} to data enabled values. */
     private final Map<Integer, Boolean> mDataEnabledSettings = new ArrayMap<>();
@@ -271,7 +274,7 @@
     }
 
     private boolean hasCalling() {
-        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
         return mPhone.getContext().getPackageManager().hasSystemFeature(
             PackageManager.FEATURE_TELEPHONY_CALLING);
     }
@@ -718,8 +721,9 @@
      * @param policies New mobile data policies in String format.
      * @return A Set of parsed mobile data policies.
      */
-    public @NonNull @MobileDataPolicy Set<Integer> getMobileDataPolicyEnabled(
-            @NonNull String policies) {
+    @NonNull
+    @MobileDataPolicy
+    public Set<Integer> getMobileDataPolicyEnabled(@NonNull String policies) {
         Set<Integer> mobileDataPolicies = new HashSet<>();
         String[] rulesString = policies.trim().split("\\s*,\\s*");
         for (String rule : rulesString) {
@@ -741,7 +745,8 @@
      * @return Parsed mobile data policy. {@link #INVALID_MOBILE_DATA_POLICY} if string can't be
      * parsed into a mobile data policy.
      */
-    private @MobileDataPolicy int parsePolicyFrom(@NonNull String policy) {
+    @MobileDataPolicy
+    private int parsePolicyFrom(@NonNull String policy) {
         int dataPolicy;
         try {
             // parse as new override policy
@@ -810,20 +815,14 @@
 
     private static String dataEnabledChangedReasonToString(
             @TelephonyManager.DataEnabledChangedReason int reason) {
-        switch (reason) {
-            case TelephonyManager.DATA_ENABLED_REASON_USER:
-                return "USER";
-            case TelephonyManager.DATA_ENABLED_REASON_POLICY:
-                return "POLICY";
-            case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
-                return "CARRIER";
-            case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
-                return "THERMAL";
-            case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE:
-                return "OVERRIDE";
-            default:
-                return "UNKNOWN";
-        }
+        return switch (reason) {
+            case TelephonyManager.DATA_ENABLED_REASON_USER -> "USER";
+            case TelephonyManager.DATA_ENABLED_REASON_POLICY -> "POLICY";
+            case TelephonyManager.DATA_ENABLED_REASON_CARRIER -> "CARRIER";
+            case TelephonyManager.DATA_ENABLED_REASON_THERMAL -> "THERMAL";
+            case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE -> "OVERRIDE";
+            default -> "UNKNOWN";
+        };
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index ec6b40f..b9b60a0 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -140,24 +140,33 @@
     /** Event for duration milliseconds changed. */
     private static final int EVENT_CONTENT_DSRM_DURATION_MILLIS_CHANGED = 5;
 
-    private final @NonNull Phone mPhone;
-    private final @NonNull String mLogTag;
-    private final @NonNull LocalLog mLocalLog = new LocalLog(128);
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final Phone mPhone;
+    @NonNull
+    private final String mLogTag;
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(128);
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /** Data network controller */
-    private final @NonNull DataNetworkController mDataNetworkController;
+    @NonNull
+    private final DataNetworkController mDataNetworkController;
 
     /** Data config manager */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /** Cellular data service */
-    private final @NonNull DataServiceManager mWwanDataServiceManager;
+    @NonNull
+    private final DataServiceManager mWwanDataServiceManager;
 
     /** The data stall recovery action. */
-    private @RecoveryAction int mRecoveryAction;
+    @RecoveryAction
+    private int mRecoveryAction;
     /** The elapsed real time of last recovery attempted */
-    private @ElapsedRealtimeLong long mTimeLastRecoveryStartMs;
+    @ElapsedRealtimeLong
+    private long mTimeLastRecoveryStartMs;
     /** Whether current network is good or not */
     private boolean mIsValidNetwork;
     /** Whether data stall recovery is triggered or not */
@@ -168,11 +177,14 @@
     private boolean mLastActionReported;
     /** The real time for data stall start. */
     @VisibleForTesting
-    public @ElapsedRealtimeLong long mDataStallStartMs;
+    @ElapsedRealtimeLong
+    public long mDataStallStartMs;
     /** Last data stall recovery action. */
-    private @RecoveryAction int mLastAction;
+    @RecoveryAction
+    private int mLastAction;
     /** Last radio power state. */
-    private @RadioPowerState int mRadioPowerState;
+    @RadioPowerState
+    private int mRadioPowerState;
     /** Whether the NetworkCheckTimer start. */
     private boolean mNetworkCheckTimerStarted = false;
     /** Whether radio state changed during data stall. */
@@ -186,15 +198,18 @@
     /** Whether internet network that require validation is connected. */
     private boolean mIsInternetNetworkConnected;
     /** The durations for current recovery action */
-    private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction;
+    @ElapsedRealtimeLong
+    private long mTimeElapsedOfCurrentAction;
     /** Tracks the total number of validation duration a data stall */
     private int mValidationCount;
     /** Tracks the number of validation for current action during a data stall */
     private int mActionValidationCount;
     /** The array for the timers between recovery actions. */
-    private @NonNull long[] mDataStallRecoveryDelayMillisArray;
+    @NonNull
+    private long[] mDataStallRecoveryDelayMillisArray;
     /** The boolean array for the flags. They are used to skip the recovery actions if needed. */
-    private @NonNull boolean[] mSkipRecoveryActionArray;
+    @NonNull
+    private boolean[] mSkipRecoveryActionArray;
 
     /**
      * The content URI for the DSRM recovery actions.
@@ -563,7 +578,7 @@
         if (isValid) {
             if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
                 // Broadcast intent that data stall recovered.
-                broadcastDataStallDetected(getRecoveryAction());
+                broadcastDataStallDetected(mLastAction);
             }
             reset();
         } else if (isRecoveryNeeded(true)) {
@@ -684,7 +699,7 @@
         // Get the information for DSRS state
         final boolean isRecovered = !mDataStalled;
         final int duration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
-        final @RecoveredReason int reason = getRecoveredReason(mIsValidNetwork);
+        @RecoveredReason final int reason = getRecoveredReason(mIsValidNetwork);
         final int durationOfAction = (int) getDurationOfCurrentRecoveryMs();
         if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
             log("mValidationCount=" + mValidationCount
@@ -712,7 +727,7 @@
     private void cleanUpDataNetwork() {
         log("cleanUpDataNetwork: notify clean up data network");
         mDataStallRecoveryManagerCallback.invokeFromExecutor(
-                () -> mDataStallRecoveryManagerCallback.onDataStallReestablishInternet());
+                mDataStallRecoveryManagerCallback::onDataStallReestablishInternet);
     }
 
     /** Recovery Action: RECOVERY_ACTION_RADIO_RESTART */
@@ -817,7 +832,7 @@
     private void setNetworkValidationState(boolean isValid) {
         boolean isLogNeeded = false;
         int timeDuration = 0;
-        int timeDurationOfCurrentAction = 0;
+        int timeDurationOfCurrentAction;
         boolean isFirstDataStall = false;
         boolean isFirstValidationAfterDoRecovery = false;
         @RecoveredReason int reason = getRecoveredReason(isValid);
@@ -891,9 +906,6 @@
             if (mLastAction <= RECOVERY_ACTION_CLEANUP) {
                 ret = RECOVERED_REASON_MODEM;
             }
-            if (mLastAction > RECOVERY_ACTION_CLEANUP) {
-                ret = RECOVERED_REASON_DSRM;
-            }
             if (mIsAirPlaneModeEnableDuringDataStall) {
                 ret = RECOVERED_REASON_USER;
             }
@@ -953,7 +965,8 @@
      * @param reason The recovered reason.
      * @return The recovered reason in string format.
      */
-    private static @NonNull String recoveredReasonToString(@RecoveredReason int reason) {
+    @NonNull
+    private static String recoveredReasonToString(@RecoveredReason int reason) {
         return switch (reason) {
             case RECOVERED_REASON_NONE -> "RECOVERED_REASON_NONE";
             case RECOVERED_REASON_DSRM -> "RECOVERED_REASON_DSRM";
@@ -969,17 +982,14 @@
      * @param state The radio power state
      * @return The radio power state in string format.
      */
-    private static @NonNull String radioPowerStateToString(@RadioPowerState int state) {
-        switch (state) {
-            case TelephonyManager.RADIO_POWER_OFF:
-                return "RADIO_POWER_OFF";
-            case TelephonyManager.RADIO_POWER_ON:
-                return "RADIO_POWER_ON";
-            case TelephonyManager.RADIO_POWER_UNAVAILABLE:
-                return "RADIO_POWER_UNAVAILABLE";
-            default:
-                return "Unknown(" + state + ")";
-        }
+    @NonNull
+    private static String radioPowerStateToString(@RadioPowerState int state) {
+        return switch (state) {
+            case TelephonyManager.RADIO_POWER_OFF -> "RADIO_POWER_OFF";
+            case TelephonyManager.RADIO_POWER_ON -> "RADIO_POWER_ON";
+            case TelephonyManager.RADIO_POWER_UNAVAILABLE -> "RADIO_POWER_UNAVAILABLE";
+            default -> "Unknown(" + state + ")";
+        };
     }
 
     /**
@@ -988,7 +998,8 @@
      * @param action The recovery action
      * @return The recovery action in string format.
      */
-    private static @NonNull String recoveryActionToString(@RecoveryAction int action) {
+    @NonNull
+    private static String recoveryActionToString(@RecoveryAction int action) {
         return switch (action) {
             case RECOVERY_ACTION_GET_DATA_CALL_LIST -> "RECOVERY_ACTION_GET_DATA_CALL_LIST";
             case RECOVERY_ACTION_CLEANUP -> "RECOVERY_ACTION_CLEANUP";
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 49e54e7..20da97f 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -27,6 +27,7 @@
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
 import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.ConnectivityTransport;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.NetCapability;
 import android.telephony.Annotation.NetworkType;
@@ -58,6 +59,7 @@
  * This class contains all the utility methods used by telephony data stack.
  */
 public class DataUtils {
+    public static final int NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED = 37;
     /** The time format for converting time to readable string. */
     private static final SimpleDateFormat TIME_FORMAT =
             new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);
@@ -69,32 +71,30 @@
      * @param capabilityString The capability in string format
      * @return The network capability. -1 if not found.
      */
-    public static @NetCapability int getNetworkCapabilityFromString(
-            @NonNull String capabilityString) {
-        switch (capabilityString.toUpperCase(Locale.ROOT)) {
-            case "MMS": return NetworkCapabilities.NET_CAPABILITY_MMS;
-            case "SUPL": return NetworkCapabilities.NET_CAPABILITY_SUPL;
-            case "DUN": return NetworkCapabilities.NET_CAPABILITY_DUN;
-            case "FOTA": return NetworkCapabilities.NET_CAPABILITY_FOTA;
-            case "IMS": return NetworkCapabilities.NET_CAPABILITY_IMS;
-            case "CBS": return NetworkCapabilities.NET_CAPABILITY_CBS;
-            case "XCAP": return NetworkCapabilities.NET_CAPABILITY_XCAP;
-            case "EIMS": return NetworkCapabilities.NET_CAPABILITY_EIMS;
-            case "INTERNET": return NetworkCapabilities.NET_CAPABILITY_INTERNET;
-            case "MCX": return NetworkCapabilities.NET_CAPABILITY_MCX;
-            case "VSIM": return NetworkCapabilities.NET_CAPABILITY_VSIM;
-            case "BIP" : return NetworkCapabilities.NET_CAPABILITY_BIP;
-            case "ENTERPRISE": return NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
-            case "PRIORITIZE_BANDWIDTH":
-                return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
-            case "PRIORITIZE_LATENCY":
-                return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
-            case "RCS":
-                return NetworkCapabilities.NET_CAPABILITY_RCS;
-            default:
+    @NetCapability
+    public static int getNetworkCapabilityFromString(@NonNull String capabilityString) {
+        return switch (capabilityString.toUpperCase(Locale.ROOT)) {
+            case "MMS" -> NetworkCapabilities.NET_CAPABILITY_MMS;
+            case "SUPL" -> NetworkCapabilities.NET_CAPABILITY_SUPL;
+            case "DUN" -> NetworkCapabilities.NET_CAPABILITY_DUN;
+            case "FOTA" -> NetworkCapabilities.NET_CAPABILITY_FOTA;
+            case "IMS" -> NetworkCapabilities.NET_CAPABILITY_IMS;
+            case "CBS" -> NetworkCapabilities.NET_CAPABILITY_CBS;
+            case "XCAP" -> NetworkCapabilities.NET_CAPABILITY_XCAP;
+            case "EIMS" -> NetworkCapabilities.NET_CAPABILITY_EIMS;
+            case "INTERNET" -> NetworkCapabilities.NET_CAPABILITY_INTERNET;
+            case "MCX" -> NetworkCapabilities.NET_CAPABILITY_MCX;
+            case "VSIM" -> NetworkCapabilities.NET_CAPABILITY_VSIM;
+            case "BIP" -> NetworkCapabilities.NET_CAPABILITY_BIP;
+            case "ENTERPRISE" -> NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+            case "PRIORITIZE_BANDWIDTH" -> NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
+            case "PRIORITIZE_LATENCY" -> NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
+            case "RCS" -> NetworkCapabilities.NET_CAPABILITY_RCS;
+            default -> {
                 loge("Illegal network capability: " + capabilityString);
-                return -1;
-        }
+                yield -1;
+            }
+        };
     }
 
     /**
@@ -105,7 +105,8 @@
      * @param capabilitiesString capability strings joined by {@code |}
      * @return Set of capabilities
      */
-    public static @NetCapability Set<Integer> getNetworkCapabilitiesFromString(
+    @NetCapability
+    public static Set<Integer> getNetworkCapabilitiesFromString(
             @NonNull String capabilitiesString) {
         // e.g. "IMS|" is not allowed
         if (!capabilitiesString.matches("(\\s*[a-zA-Z_]+\\s*)(\\|\\s*[a-zA-Z_]+\\s*)*")) {
@@ -119,69 +120,108 @@
 
     /**
      * Convert a network capability to string.
-     *
+     * <p>
      * This is for debugging and logging purposes only.
      *
      * @param netCap Network capability.
      * @return Network capability in string format.
      */
-    public static @NonNull String networkCapabilityToString(@NetCapability int netCap) {
-        switch (netCap) {
-            case NetworkCapabilities.NET_CAPABILITY_MMS:                  return "MMS";
-            case NetworkCapabilities.NET_CAPABILITY_SUPL:                 return "SUPL";
-            case NetworkCapabilities.NET_CAPABILITY_DUN:                  return "DUN";
-            case NetworkCapabilities.NET_CAPABILITY_FOTA:                 return "FOTA";
-            case NetworkCapabilities.NET_CAPABILITY_IMS:                  return "IMS";
-            case NetworkCapabilities.NET_CAPABILITY_CBS:                  return "CBS";
-            case NetworkCapabilities.NET_CAPABILITY_WIFI_P2P:             return "WIFI_P2P";
-            case NetworkCapabilities.NET_CAPABILITY_IA:                   return "IA";
-            case NetworkCapabilities.NET_CAPABILITY_RCS:                  return "RCS";
-            case NetworkCapabilities.NET_CAPABILITY_XCAP:                 return "XCAP";
-            case NetworkCapabilities.NET_CAPABILITY_EIMS:                 return "EIMS";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_METERED:          return "NOT_METERED";
-            case NetworkCapabilities.NET_CAPABILITY_INTERNET:             return "INTERNET";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED:       return "NOT_RESTRICTED";
-            case NetworkCapabilities.NET_CAPABILITY_TRUSTED:              return "TRUSTED";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_VPN:              return "NOT_VPN";
-            case NetworkCapabilities.NET_CAPABILITY_VALIDATED:            return "VALIDATED";
-            case NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL:       return "CAPTIVE_PORTAL";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING:          return "NOT_ROAMING";
-            case NetworkCapabilities.NET_CAPABILITY_FOREGROUND:           return "FOREGROUND";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED:        return "NOT_CONGESTED";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED:        return "NOT_SUSPENDED";
-            case NetworkCapabilities.NET_CAPABILITY_OEM_PAID:             return "OEM_PAID";
-            case NetworkCapabilities.NET_CAPABILITY_MCX:                  return "MCX";
-            case NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY:
-                return "PARTIAL_CONNECTIVITY";
-            case NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED:
-                return "TEMPORARILY_NOT_METERED";
-            case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE:          return "OEM_PRIVATE";
-            case NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL:     return "VEHICLE_INTERNAL";
-            case NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED:      return "NOT_VCN_MANAGED";
-            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:           return "ENTERPRISE";
-            case NetworkCapabilities.NET_CAPABILITY_VSIM:                 return "VSIM";
-            case NetworkCapabilities.NET_CAPABILITY_BIP:                  return "BIP";
-            case NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT:            return "HEAD_UNIT";
-            case NetworkCapabilities.NET_CAPABILITY_MMTEL:                return "MMTEL";
-            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY:
-                return "PRIORITIZE_LATENCY";
-            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH:
-                return "PRIORITIZE_BANDWIDTH";
-            default:
+    @NonNull
+    public static String networkCapabilityToString(@NetCapability int netCap) {
+        return switch (netCap) {
+            case NetworkCapabilities.NET_CAPABILITY_MMS -> "MMS";
+            case NetworkCapabilities.NET_CAPABILITY_SUPL -> "SUPL";
+            case NetworkCapabilities.NET_CAPABILITY_DUN -> "DUN";
+            case NetworkCapabilities.NET_CAPABILITY_FOTA -> "FOTA";
+            case NetworkCapabilities.NET_CAPABILITY_IMS -> "IMS";
+            case NetworkCapabilities.NET_CAPABILITY_CBS -> "CBS";
+            case NetworkCapabilities.NET_CAPABILITY_WIFI_P2P -> "WIFI_P2P";
+            case NetworkCapabilities.NET_CAPABILITY_IA -> "IA";
+            case NetworkCapabilities.NET_CAPABILITY_RCS -> "RCS";
+            case NetworkCapabilities.NET_CAPABILITY_XCAP -> "XCAP";
+            case NetworkCapabilities.NET_CAPABILITY_EIMS -> "EIMS";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_METERED -> "NOT_METERED";
+            case NetworkCapabilities.NET_CAPABILITY_INTERNET -> "INTERNET";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED -> "NOT_RESTRICTED";
+            case NetworkCapabilities.NET_CAPABILITY_TRUSTED -> "TRUSTED";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_VPN -> "NOT_VPN";
+            case NetworkCapabilities.NET_CAPABILITY_VALIDATED -> "VALIDATED";
+            case NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL -> "CAPTIVE_PORTAL";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING -> "NOT_ROAMING";
+            case NetworkCapabilities.NET_CAPABILITY_FOREGROUND -> "FOREGROUND";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED -> "NOT_CONGESTED";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED -> "NOT_SUSPENDED";
+            case NetworkCapabilities.NET_CAPABILITY_OEM_PAID -> "OEM_PAID";
+            case NetworkCapabilities.NET_CAPABILITY_MCX -> "MCX";
+            case NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY -> "PARTIAL_CONNECTIVITY";
+            case NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED ->
+                    "TEMPORARILY_NOT_METERED";
+            case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE -> "OEM_PRIVATE";
+            case NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL -> "VEHICLE_INTERNAL";
+            case NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED -> "NOT_VCN_MANAGED";
+            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE -> "ENTERPRISE";
+            case NetworkCapabilities.NET_CAPABILITY_VSIM -> "VSIM";
+            case NetworkCapabilities.NET_CAPABILITY_BIP -> "BIP";
+            case NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT -> "HEAD_UNIT";
+            case NetworkCapabilities.NET_CAPABILITY_MMTEL -> "MMTEL";
+            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY -> "PRIORITIZE_LATENCY";
+            case NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH -> "PRIORITIZE_BANDWIDTH";
+            case NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED -> "NOT_BANDWIDTH_CONSTRAINED";
+            default -> {
                 loge("Unknown network capability(" + netCap + ")");
-                return "Unknown(" + netCap + ")";
-        }
+                yield "Unknown(" + netCap + ")";
+            }
+        };
+    }
+
+    /**
+     * Concat an array of {@link NetworkCapabilities.Transport} in string format.
+     *
+     * @param transports an array of connectivity transports
+     * @return a string of the array of transports.
+     */
+    @NonNull
+    public static String connectivityTransportsToString(
+            @NonNull @ConnectivityTransport int[] transports) {
+        return Arrays.stream(transports).mapToObj(DataUtils::connectivityTransportToString)
+                .collect(Collectors.joining("|"));
+    }
+
+    /**
+     * Convert a {@link NetworkCapabilities.Transport} to a string.
+     *
+     * @param transport the connectivity transport
+     * @return the transport in string
+     */
+    @NonNull
+    public static String connectivityTransportToString(
+            @ConnectivityTransport int transport) {
+        return switch (transport) {
+            case NetworkCapabilities.TRANSPORT_CELLULAR -> "CELLULAR";
+            case NetworkCapabilities.TRANSPORT_WIFI -> "WIFI";
+            case NetworkCapabilities.TRANSPORT_BLUETOOTH -> "BLUETOOTH";
+            case NetworkCapabilities.TRANSPORT_ETHERNET -> "ETHERNET";
+            case NetworkCapabilities.TRANSPORT_VPN -> "VPN";
+            case NetworkCapabilities.TRANSPORT_WIFI_AWARE -> "WIFI_AWARE";
+            case NetworkCapabilities.TRANSPORT_LOWPAN -> "LOWPAN";
+            case NetworkCapabilities.TRANSPORT_TEST -> "TEST";
+            case NetworkCapabilities.TRANSPORT_USB -> "USB";
+            case NetworkCapabilities.TRANSPORT_THREAD -> "THREAD";
+            case NetworkCapabilities.TRANSPORT_SATELLITE -> "SATELLITE";
+            default -> "Unknown(" + transport + ")";
+        };
     }
 
     /**
      * Convert network capabilities to string.
-     *
+     * <p>
      * This is for debugging and logging purposes only.
      *
      * @param netCaps Network capabilities.
      * @return Network capabilities in string format.
      */
-    public static @NonNull String networkCapabilitiesToString(
+    @NonNull
+    public static String networkCapabilitiesToString(
             @NetCapability @Nullable Collection<Integer> netCaps) {
         if (netCaps == null || netCaps.isEmpty()) return "";
         return "[" + netCaps.stream()
@@ -191,13 +231,14 @@
 
     /**
      * Convert network capabilities to string.
-     *
+     * <p>
      * This is for debugging and logging purposes only.
      *
      * @param netCaps Network capabilities.
      * @return Network capabilities in string format.
      */
-    public static @NonNull String networkCapabilitiesToString(@NetCapability int[] netCaps) {
+    @NonNull
+    public static String networkCapabilitiesToString(@NetCapability int[] netCaps) {
         if (netCaps == null) return "";
         return "[" + Arrays.stream(netCaps)
                 .mapToObj(DataUtils::networkCapabilityToString)
@@ -210,14 +251,16 @@
      * @param status The validation status.
      * @return The validation status in string format.
      */
-    public static @NonNull String validationStatusToString(@ValidationStatus int status) {
-        switch (status) {
-            case NetworkAgent.VALIDATION_STATUS_VALID: return "VALID";
-            case NetworkAgent.VALIDATION_STATUS_NOT_VALID: return "INVALID";
-            default:
+    @NonNull
+    public static String validationStatusToString(@ValidationStatus int status) {
+        return switch (status) {
+            case NetworkAgent.VALIDATION_STATUS_VALID -> "VALID";
+            case NetworkAgent.VALIDATION_STATUS_NOT_VALID -> "INVALID";
+            default -> {
                 loge("Unknown validation status(" + status + ")");
-                return "UNKNOWN(" + status + ")";
-        }
+                yield "UNKNOWN(" + status + ")";
+            }
+        };
     }
 
     /**
@@ -226,41 +269,26 @@
      * @param networkCapability Network capability.
      * @return APN type.
      */
-    public static @ApnType int networkCapabilityToApnType(@NetCapability int networkCapability) {
-        switch (networkCapability) {
-            case NetworkCapabilities.NET_CAPABILITY_MMS:
-                return ApnSetting.TYPE_MMS;
-            case NetworkCapabilities.NET_CAPABILITY_SUPL:
-                return ApnSetting.TYPE_SUPL;
-            case NetworkCapabilities.NET_CAPABILITY_DUN:
-                return ApnSetting.TYPE_DUN;
-            case NetworkCapabilities.NET_CAPABILITY_FOTA:
-                return ApnSetting.TYPE_FOTA;
-            case NetworkCapabilities.NET_CAPABILITY_IMS:
-                return ApnSetting.TYPE_IMS;
-            case NetworkCapabilities.NET_CAPABILITY_CBS:
-                return ApnSetting.TYPE_CBS;
-            case NetworkCapabilities.NET_CAPABILITY_XCAP:
-                return ApnSetting.TYPE_XCAP;
-            case NetworkCapabilities.NET_CAPABILITY_EIMS:
-                return ApnSetting.TYPE_EMERGENCY;
-            case NetworkCapabilities.NET_CAPABILITY_INTERNET:
-                return ApnSetting.TYPE_DEFAULT;
-            case NetworkCapabilities.NET_CAPABILITY_MCX:
-                return ApnSetting.TYPE_MCX;
-            case NetworkCapabilities.NET_CAPABILITY_IA:
-                return ApnSetting.TYPE_IA;
-            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:
-                return ApnSetting.TYPE_ENTERPRISE;
-            case NetworkCapabilities.NET_CAPABILITY_VSIM:
-                return ApnSetting.TYPE_VSIM;
-            case NetworkCapabilities.NET_CAPABILITY_BIP:
-                return ApnSetting.TYPE_BIP;
-            case NetworkCapabilities.NET_CAPABILITY_RCS:
-                return ApnSetting.TYPE_RCS;
-            default:
-                return ApnSetting.TYPE_NONE;
-        }
+    @ApnType
+    public static int networkCapabilityToApnType(@NetCapability int networkCapability) {
+        return switch (networkCapability) {
+            case NetworkCapabilities.NET_CAPABILITY_MMS -> ApnSetting.TYPE_MMS;
+            case NetworkCapabilities.NET_CAPABILITY_SUPL -> ApnSetting.TYPE_SUPL;
+            case NetworkCapabilities.NET_CAPABILITY_DUN -> ApnSetting.TYPE_DUN;
+            case NetworkCapabilities.NET_CAPABILITY_FOTA -> ApnSetting.TYPE_FOTA;
+            case NetworkCapabilities.NET_CAPABILITY_IMS -> ApnSetting.TYPE_IMS;
+            case NetworkCapabilities.NET_CAPABILITY_CBS -> ApnSetting.TYPE_CBS;
+            case NetworkCapabilities.NET_CAPABILITY_XCAP -> ApnSetting.TYPE_XCAP;
+            case NetworkCapabilities.NET_CAPABILITY_EIMS -> ApnSetting.TYPE_EMERGENCY;
+            case NetworkCapabilities.NET_CAPABILITY_INTERNET -> ApnSetting.TYPE_DEFAULT;
+            case NetworkCapabilities.NET_CAPABILITY_MCX -> ApnSetting.TYPE_MCX;
+            case NetworkCapabilities.NET_CAPABILITY_IA -> ApnSetting.TYPE_IA;
+            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE -> ApnSetting.TYPE_ENTERPRISE;
+            case NetworkCapabilities.NET_CAPABILITY_VSIM -> ApnSetting.TYPE_VSIM;
+            case NetworkCapabilities.NET_CAPABILITY_BIP -> ApnSetting.TYPE_BIP;
+            case NetworkCapabilities.NET_CAPABILITY_RCS -> ApnSetting.TYPE_RCS;
+            default -> ApnSetting.TYPE_NONE;
+        };
     }
 
     /**
@@ -269,41 +297,26 @@
      * @param apnType APN type.
      * @return Network capability.
      */
-    public static @NetCapability int apnTypeToNetworkCapability(@ApnType int apnType) {
-        switch (apnType) {
-            case ApnSetting.TYPE_MMS:
-                return NetworkCapabilities.NET_CAPABILITY_MMS;
-            case ApnSetting.TYPE_SUPL:
-                return NetworkCapabilities.NET_CAPABILITY_SUPL;
-            case ApnSetting.TYPE_DUN:
-                return NetworkCapabilities.NET_CAPABILITY_DUN;
-            case ApnSetting.TYPE_FOTA:
-                return NetworkCapabilities.NET_CAPABILITY_FOTA;
-            case ApnSetting.TYPE_IMS:
-                return NetworkCapabilities.NET_CAPABILITY_IMS;
-            case ApnSetting.TYPE_CBS:
-                return NetworkCapabilities.NET_CAPABILITY_CBS;
-            case ApnSetting.TYPE_XCAP:
-                return NetworkCapabilities.NET_CAPABILITY_XCAP;
-            case ApnSetting.TYPE_EMERGENCY:
-                return NetworkCapabilities.NET_CAPABILITY_EIMS;
-            case ApnSetting.TYPE_DEFAULT:
-                return NetworkCapabilities.NET_CAPABILITY_INTERNET;
-            case ApnSetting.TYPE_MCX:
-                return NetworkCapabilities.NET_CAPABILITY_MCX;
-            case ApnSetting.TYPE_IA:
-                return NetworkCapabilities.NET_CAPABILITY_IA;
-            case ApnSetting.TYPE_BIP:
-                return NetworkCapabilities.NET_CAPABILITY_BIP;
-            case ApnSetting.TYPE_VSIM:
-                return NetworkCapabilities.NET_CAPABILITY_VSIM;
-            case ApnSetting.TYPE_ENTERPRISE:
-                return NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
-            case ApnSetting.TYPE_RCS:
-                return NetworkCapabilities.NET_CAPABILITY_RCS;
-            default:
-                return -1;
-        }
+    @NetCapability
+    public static int apnTypeToNetworkCapability(@ApnType int apnType) {
+        return switch (apnType) {
+            case ApnSetting.TYPE_MMS -> NetworkCapabilities.NET_CAPABILITY_MMS;
+            case ApnSetting.TYPE_SUPL -> NetworkCapabilities.NET_CAPABILITY_SUPL;
+            case ApnSetting.TYPE_DUN -> NetworkCapabilities.NET_CAPABILITY_DUN;
+            case ApnSetting.TYPE_FOTA -> NetworkCapabilities.NET_CAPABILITY_FOTA;
+            case ApnSetting.TYPE_IMS -> NetworkCapabilities.NET_CAPABILITY_IMS;
+            case ApnSetting.TYPE_CBS -> NetworkCapabilities.NET_CAPABILITY_CBS;
+            case ApnSetting.TYPE_XCAP -> NetworkCapabilities.NET_CAPABILITY_XCAP;
+            case ApnSetting.TYPE_EMERGENCY -> NetworkCapabilities.NET_CAPABILITY_EIMS;
+            case ApnSetting.TYPE_DEFAULT -> NetworkCapabilities.NET_CAPABILITY_INTERNET;
+            case ApnSetting.TYPE_MCX -> NetworkCapabilities.NET_CAPABILITY_MCX;
+            case ApnSetting.TYPE_IA -> NetworkCapabilities.NET_CAPABILITY_IA;
+            case ApnSetting.TYPE_BIP -> NetworkCapabilities.NET_CAPABILITY_BIP;
+            case ApnSetting.TYPE_VSIM -> NetworkCapabilities.NET_CAPABILITY_VSIM;
+            case ApnSetting.TYPE_ENTERPRISE -> NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+            case ApnSetting.TYPE_RCS -> NetworkCapabilities.NET_CAPABILITY_RCS;
+            default -> -1;
+        };
     }
 
     /**
@@ -312,37 +325,26 @@
      * @param networkType The network type.
      * @return The access network type.
      */
-    public static @RadioAccessNetworkType int networkTypeToAccessNetworkType(
-            @NetworkType int networkType) {
-        switch (networkType) {
-            case TelephonyManager.NETWORK_TYPE_GPRS:
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-            case TelephonyManager.NETWORK_TYPE_GSM:
-                return AccessNetworkType.GERAN;
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
-                return AccessNetworkType.UTRAN;
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-            case TelephonyManager.NETWORK_TYPE_EVDO_0:
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-                return AccessNetworkType.CDMA2000;
-            case TelephonyManager.NETWORK_TYPE_LTE:
-            case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                return AccessNetworkType.EUTRAN;
-            case TelephonyManager.NETWORK_TYPE_IWLAN:
-                return AccessNetworkType.IWLAN;
-            case TelephonyManager.NETWORK_TYPE_NR:
-                return AccessNetworkType.NGRAN;
-            default:
-                return AccessNetworkType.UNKNOWN;
-        }
+    @RadioAccessNetworkType
+    public static int networkTypeToAccessNetworkType(@NetworkType int networkType) {
+        return switch (networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_EDGE,
+                    TelephonyManager.NETWORK_TYPE_GSM ->
+                    AccessNetworkType.GERAN;
+            case TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_HSDPA,
+                    TelephonyManager.NETWORK_TYPE_HSPAP, TelephonyManager.NETWORK_TYPE_HSUPA,
+                    TelephonyManager.NETWORK_TYPE_HSPA, TelephonyManager.NETWORK_TYPE_TD_SCDMA ->
+                    AccessNetworkType.UTRAN;
+            case TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EVDO_0,
+                    TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_EVDO_B,
+                    TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_EHRPD ->
+                    AccessNetworkType.CDMA2000;
+            case TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_LTE_CA ->
+                    AccessNetworkType.EUTRAN;
+            case TelephonyManager.NETWORK_TYPE_IWLAN -> AccessNetworkType.IWLAN;
+            case TelephonyManager.NETWORK_TYPE_NR -> AccessNetworkType.NGRAN;
+            default -> AccessNetworkType.UNKNOWN;
+        };
     }
 
     /**
@@ -351,7 +353,8 @@
      * @param elapsedTime The elapsed time retrieved from {@link SystemClock#elapsedRealtime()}.
      * @return The string format time.
      */
-    public static @NonNull String elapsedTimeToString(@ElapsedRealtimeLong long elapsedTime) {
+    @NonNull
+    public static String elapsedTimeToString(@ElapsedRealtimeLong long elapsedTime) {
         return (elapsedTime != 0) ? systemTimeToString(System.currentTimeMillis()
                 - SystemClock.elapsedRealtime() + elapsedTime) : "never";
     }
@@ -362,7 +365,8 @@
      * @param systemTime The system time retrieved from {@link System#currentTimeMillis()}.
      * @return The string format time.
      */
-    public static @NonNull String systemTimeToString(@CurrentTimeMillisLong long systemTime) {
+    @NonNull
+    public static String systemTimeToString(@CurrentTimeMillisLong long systemTime) {
         return (systemTime != 0) ? TIME_FORMAT.format(systemTime) : "never";
     }
 
@@ -372,14 +376,16 @@
      * @param imsFeature IMS feature.
      * @return IMS feature in string format.
      */
-    public static @NonNull String imsFeatureToString(@ImsFeature.FeatureType int imsFeature) {
-        switch (imsFeature) {
-            case ImsFeature.FEATURE_MMTEL: return "MMTEL";
-            case ImsFeature.FEATURE_RCS: return "RCS";
-            default:
+    @NonNull
+    public static String imsFeatureToString(@ImsFeature.FeatureType int imsFeature) {
+        return switch (imsFeature) {
+            case ImsFeature.FEATURE_MMTEL -> "MMTEL";
+            case ImsFeature.FEATURE_RCS -> "RCS";
+            default -> {
                 loge("Unknown IMS feature(" + imsFeature + ")");
-                return "Unknown(" + imsFeature + ")";
-        }
+                yield "Unknown(" + imsFeature + ")";
+            }
+        };
     }
 
     /**
@@ -390,7 +396,8 @@
      *
      * @return The network requests after grouping.
      */
-    public static @NonNull List<NetworkRequestList> getGroupedNetworkRequestList(
+    @NonNull
+    public static List<NetworkRequestList> getGroupedNetworkRequestList(
             @NonNull NetworkRequestList networkRequestList, @NonNull FeatureFlags featureFlags) {
         List<NetworkRequestList> requests = new ArrayList<>();
         if (featureFlags.satelliteInternet()) {
@@ -406,8 +413,7 @@
                                 Arrays.stream(networkRequest.getNativeNetworkRequest()
                                                 .getEnterpriseIds())
                                         .boxed().collect(Collectors.toSet()),
-                                Arrays.stream(networkRequest.getNativeNetworkRequest()
-                                                .getTransportTypes())
+                                Arrays.stream(networkRequest.getTransportTypes())
                                         .boxed().collect(Collectors.toSet())
                                 ),
                         v -> new NetworkRequestList()).add(networkRequest);
@@ -454,41 +460,31 @@
      * @param sourceTransport The source transport.
      * @return The target transport.
      */
-    public static @TransportType int getTargetTransport(@TransportType int sourceTransport) {
+    @TransportType
+    public static int getTargetTransport(@TransportType int sourceTransport) {
         return sourceTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
                 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
     }
 
     /**
-     * Get the source transport from target transport. This is only used for handover between
-     * IWLAN and cellular scenario.
-     *
-     * @param targetTransport The target transport.
-     * @return The source transport.
-     */
-    public static @TransportType int getSourceTransport(@TransportType int targetTransport) {
-        return targetTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
-                : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-    }
-
-    /**
      * Convert link status to string.
      *
      * @param linkStatus The link status.
      * @return The link status in string format.
      */
-    public static @NonNull String linkStatusToString(@LinkStatus int linkStatus) {
-        switch (linkStatus) {
-            case DataCallResponse.LINK_STATUS_UNKNOWN: return "UNKNOWN";
-            case DataCallResponse.LINK_STATUS_INACTIVE: return "INACTIVE";
-            case DataCallResponse.LINK_STATUS_ACTIVE: return "ACTIVE";
-            case DataCallResponse.LINK_STATUS_DORMANT: return "DORMANT";
-            default:
+    @NonNull
+    public static String linkStatusToString(@LinkStatus int linkStatus) {
+        return switch (linkStatus) {
+            case DataCallResponse.LINK_STATUS_UNKNOWN -> "UNKNOWN";
+            case DataCallResponse.LINK_STATUS_INACTIVE -> "INACTIVE";
+            case DataCallResponse.LINK_STATUS_ACTIVE -> "ACTIVE";
+            case DataCallResponse.LINK_STATUS_DORMANT -> "DORMANT";
+            default -> {
                 loge("Unknown link status(" + linkStatus + ")");
-                return "UNKNOWN(" + linkStatus + ")";
-        }
+                yield "UNKNOWN(" + linkStatus + ")";
+            }
+        };
     }
 
     /**
@@ -498,17 +494,12 @@
      * @return {@code true} if the access network type is valid.
      */
     public static boolean isValidAccessNetwork(@RadioAccessNetworkType int accessNetworkType) {
-        switch (accessNetworkType) {
-            case AccessNetworkType.GERAN:
-            case AccessNetworkType.UTRAN:
-            case AccessNetworkType.EUTRAN:
-            case AccessNetworkType.CDMA2000:
-            case AccessNetworkType.IWLAN:
-            case AccessNetworkType.NGRAN:
-                return true;
-            default:
-                return false;
-        }
+        return switch (accessNetworkType) {
+            case AccessNetworkType.GERAN, AccessNetworkType.UTRAN, AccessNetworkType.EUTRAN,
+                    AccessNetworkType.CDMA2000, AccessNetworkType.IWLAN, AccessNetworkType.NGRAN ->
+                    true;
+            default -> false;
+        };
     }
 
     /**
@@ -517,17 +508,19 @@
      * @param dataActivity The data activity.
      * @return The data activity in string format.
      */
-    public static @NonNull String dataActivityToString(@DataActivityType int dataActivity) {
-        switch (dataActivity) {
-            case TelephonyManager.DATA_ACTIVITY_NONE: return "NONE";
-            case TelephonyManager.DATA_ACTIVITY_IN: return "IN";
-            case TelephonyManager.DATA_ACTIVITY_OUT: return "OUT";
-            case TelephonyManager.DATA_ACTIVITY_INOUT: return "INOUT";
-            case TelephonyManager.DATA_ACTIVITY_DORMANT: return "DORMANT";
-            default:
+    @NonNull
+    public static String dataActivityToString(@DataActivityType int dataActivity) {
+        return switch (dataActivity) {
+            case TelephonyManager.DATA_ACTIVITY_NONE -> "NONE";
+            case TelephonyManager.DATA_ACTIVITY_IN -> "IN";
+            case TelephonyManager.DATA_ACTIVITY_OUT -> "OUT";
+            case TelephonyManager.DATA_ACTIVITY_INOUT -> "INOUT";
+            case TelephonyManager.DATA_ACTIVITY_DORMANT -> "DORMANT";
+            default -> {
                 loge("Unknown data activity(" + dataActivity + ")");
-                return "UNKNOWN(" + dataActivity + ")";
-        }
+                yield "UNKNOWN(" + dataActivity + ")";
+            }
+        };
     }
 
     private static void loge(String msg) {
diff --git a/src/java/com/android/internal/telephony/data/KeepaliveStatus.java b/src/java/com/android/internal/telephony/data/KeepaliveStatus.java
index 818de96..7b6c38d 100644
--- a/src/java/com/android/internal/telephony/data/KeepaliveStatus.java
+++ b/src/java/com/android/internal/telephony/data/KeepaliveStatus.java
@@ -23,7 +23,7 @@
 /**
  * This class serves to pass around the parameters of Keepalive session
  * status within the telephony framework.
- *
+ * <p>
  * {@hide}
  */
 public class KeepaliveStatus implements Parcelable {
@@ -54,7 +54,8 @@
      * A status code indicating whether this Keepalive session is
      * active, inactive, or pending activation
      */
-    public final @KeepaliveStatusCode int statusCode;
+    @KeepaliveStatusCode
+    public final int statusCode;
 
     /** An error code indicating a lower layer failure, if any */
     public final int errorCode;
@@ -98,7 +99,7 @@
     }
 
     public static final Parcelable.Creator<KeepaliveStatus> CREATOR =
-            new Parcelable.Creator<KeepaliveStatus>() {
+            new Parcelable.Creator<>() {
                 @Override
                 public KeepaliveStatus createFromParcel(Parcel source) {
                     return new KeepaliveStatus(source);
diff --git a/src/java/com/android/internal/telephony/data/KeepaliveTracker.java b/src/java/com/android/internal/telephony/data/KeepaliveTracker.java
index 0a01339..f221779 100644
--- a/src/java/com/android/internal/telephony/data/KeepaliveTracker.java
+++ b/src/java/com/android/internal/telephony/data/KeepaliveTracker.java
@@ -56,19 +56,24 @@
     private static final int EVENT_UNREGISTER_FOR_KEEPALIVE_STATUS = 5;
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /** The parent data network. */
-    private final @NonNull DataNetwork mDataNetwork;
+    @NonNull
+    private final DataNetwork mDataNetwork;
 
     /** The associated network agent. */
-    private final @NonNull TelephonyNetworkAgent mNetworkAgent;
+    @NonNull
+    private final TelephonyNetworkAgent mNetworkAgent;
 
     /** The log tag. */
-    private final @NonNull String mLogTag;
+    @NonNull
+    private final String mLogTag;
 
     /** The keepalive records. */
-    private final @NonNull SparseArray<KeepaliveRecord> mKeepalives = new SparseArray<>();
+    @NonNull
+    private final SparseArray<KeepaliveRecord> mKeepalives = new SparseArray<>();
 
     /**
      * Keepalive session record
@@ -78,7 +83,8 @@
         public int slotIndex;
 
         /** The current status. */
-        public @KeepaliveStatusCode int currentStatus;
+        @KeepaliveStatusCode
+        public int currentStatus;
 
         /**
          * Constructor
diff --git a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
index b92a590..534f191 100644
--- a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
+++ b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
@@ -178,6 +178,7 @@
     private long mRxBytesDeltaAcc;
 
     private ModemActivityInfo mLastModemActivityInfo = null;
+    private final TelephonyCallback mTelephonyCallback = new TelephonyCallbackImpl();
     private int mSignalStrengthDbm;
     private int mSignalLevel;
     private int mDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
@@ -315,8 +316,7 @@
         if (cm != null) {
             cm.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
         }
-        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this),
-                new TelephonyCallbackImpl());
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this), mTelephonyCallback);
         mPlaceholderNetwork = new NetworkBandwidth(UNKNOWN_PLMN);
         initAvgBwPerRatTable();
         registerNrStateFrequencyChange();
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index b7aa251..27b4331 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -53,7 +53,6 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -183,10 +182,12 @@
         }
     }
 
-    private final @NonNull NetworkRequestList mNetworkRequestList = new NetworkRequestList();
+    @NonNull
+    private final NetworkRequestList mNetworkRequestList = new NetworkRequestList();
     protected final RegistrantList mActivePhoneRegistrants;
     private final SubscriptionManagerService mSubscriptionManagerService;
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
     protected final Context mContext;
     private final LocalLog mLocalLog;
     protected PhoneState[] mPhoneStates;
@@ -310,7 +311,7 @@
     private static final int MAX_LOCAL_LOG_LINES = 256;
 
     // Default timeout value of network validation in millisecond.
-    private final static int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
+    private static final int DEFAULT_VALIDATION_EXPIRATION_TIME = 2000;
 
     /** Controller that tracks {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH} */
     @NonNull private final AutoDataSwitchController mAutoDataSwitchController;
@@ -318,21 +319,23 @@
     @NonNull private final AutoDataSwitchController.AutoDataSwitchControllerCallback
             mAutoDataSwitchCallback;
 
-    private ConnectivityManager mConnectivityManager;
+    private final ConnectivityManager mConnectivityManager;
     private int mImsRegistrationTech = REGISTRATION_TECH_NONE;
     @VisibleForTesting
     public final SparseIntArray mImsRegistrationRadioTechMap = new SparseIntArray();
-    private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
+    @NonNull
+    private final List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
 
     /** Data settings manager callback. Key is the phone id. */
-    private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
+    @NonNull
+    private final Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
             new ArrayMap<>();
 
     private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
         public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
         @Override
-        public void onCapabilitiesChanged(Network network,
+        public void onCapabilitiesChanged(@NonNull Network network,
                 NetworkCapabilities networkCapabilities) {
             if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                 if (SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
@@ -350,15 +353,15 @@
         }
 
         @Override
-        public void onLost(Network network) {
+        public void onLost(@NonNull Network network) {
             mAutoDataSwitchController.updateDefaultNetworkCapabilities(null);
         }
     }
 
-    private RegistrationManager.RegistrationCallback mRegistrationCallback =
+    private final RegistrationManager.RegistrationCallback mRegistrationCallback =
             new RegistrationManager.RegistrationCallback() {
         @Override
-        public void onRegistered(ImsRegistrationAttributes attributes) {
+        public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
             int imsRegistrationTech = attributes.getRegistrationTechnology();
             if (imsRegistrationTech != mImsRegistrationTech) {
                 mImsRegistrationTech = imsRegistrationTech;
@@ -367,7 +370,7 @@
         }
 
         @Override
-        public void onUnregistered(ImsReasonInfo info) {
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
             if (mImsRegistrationTech != REGISTRATION_TECH_NONE) {
                 mImsRegistrationTech = REGISTRATION_TECH_NONE;
                 sendMessage(obtainMessage(EVENT_IMS_RADIO_TECH_CHANGED));
@@ -402,14 +405,8 @@
 
     @VisibleForTesting
     public ImsRegisterCallback mImsRegisterCallback =
-            (context, phoneId, cb, executor)-> {
-                try {
-                    ImsManager.getInstance(context, phoneId)
-                            .addRegistrationCallback(cb, executor);
-                } catch (ImsException e) {
-                    throw e;
-                }
-            };
+            (context, phoneId, cb, executor)-> ImsManager.getInstance(context, phoneId)
+                    .addRegistrationCallback(cb, executor);
 
     /**
      * Method to get singleton instance.
@@ -497,7 +494,7 @@
         mRadioConfig = RadioConfig.getInstance();
         mValidator = CellularNetworkValidator.getInstance();
 
-        mCurrentDdsSwitchFailure = new ArrayList<Set<CommandException.Error>>();
+        mCurrentDdsSwitchFailure = new ArrayList<>();
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
         mContext.registerReceiver(mSimStateIntentReceiver, filter);
@@ -545,7 +542,7 @@
                     registerForImsRadioTechChange(context, phoneId);
                 }
             }
-            Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
+            Set<CommandException.Error> ddsFailure = new HashSet<>();
             mCurrentDdsSwitchFailure.add(ddsFailure);
         }
 
@@ -652,7 +649,7 @@
         }
     };
 
-    private BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -874,10 +871,8 @@
                             mEmergencyOverride.mOverrideCompleteFuture.complete(false);
                         }
                     }
-                    mEmergencyOverride = req;
-                } else {
-                    mEmergencyOverride = req;
                 }
+                mEmergencyOverride = req;
 
                 logl("new emergency override - " + mEmergencyOverride);
                 // a new request has been created, remove any previous override complete scheduled.
@@ -960,13 +955,9 @@
 
         mImsRegistrationRadioTechMap.put(phoneId, tech);
 
-        if (subId == INVALID_SUBSCRIPTION_ID) {
-            // Need to update the cached IMS registration tech but no need to do any of the
-            // following. When the SIM removed, REGISTRATION_STATE_NOT_REGISTERED is notified.
-            return false;
-        }
-
-        return true;
+        // Need to update the cached IMS registration tech but no need to do any of the
+        // following. When the SIM removed, REGISTRATION_STATE_NOT_REGISTERED is notified.
+        return subId != INVALID_SUBSCRIPTION_ID;
     }
 
     private synchronized void onMultiSimConfigChanged(int activeModemCount) {
@@ -1023,7 +1014,7 @@
             phone.getDataSettingsManager().registerCallback(
                     mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
 
-            Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
+            Set<CommandException.Error> ddsFailure = new HashSet<>();
             mCurrentDdsSwitchFailure.add(ddsFailure);
 
             if (!mFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
@@ -1072,7 +1063,7 @@
         }
 
         @Override
-        protected void needNetworkFor(NetworkRequest networkRequest) {
+        protected void needNetworkFor(@NonNull NetworkRequest networkRequest) {
             if (VDBG) log("needNetworkFor " + networkRequest);
             Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK);
             msg.obj = networkRequest;
@@ -1080,7 +1071,7 @@
         }
 
         @Override
-        protected void releaseNetworkFor(NetworkRequest networkRequest) {
+        protected void releaseNetworkFor(@NonNull NetworkRequest networkRequest) {
             if (VDBG) log("releaseNetworkFor " + networkRequest);
             Message msg = mPhoneSwitcher.obtainMessage(EVENT_RELEASE_NETWORK);
             msg.obj = networkRequest;
@@ -1157,7 +1148,7 @@
     protected static final boolean REQUESTS_UNCHANGED = false;
     /**
      * Re-evaluate things. Do nothing if nothing's changed.
-     *
+     * <p>
      * Otherwise, go through the requests in priority order adding their phone until we've added up
      * to the max allowed.  Then go through shutting down phones that aren't in the active phone
      * list. Finally, activate all phones in the active phone list.
@@ -1251,13 +1242,11 @@
                 }
                 sendRilCommands(mPreferredDataPhoneId);
             } else {
-                List<Integer> newActivePhones = new ArrayList<Integer>();
+                List<Integer> newActivePhones = new ArrayList<>();
 
-                /**
-                 * If all phones can have PS attached, activate all.
-                 * Otherwise, choose to activate phones according to requests. And
-                 * if list is not full, add mPreferredDataPhoneId.
-                 */
+                // If all phones can have PS attached, activate all.
+                // Otherwise, choose to activate phones according to requests. And
+                // if list is not full, add mPreferredDataPhoneId.
                 if (mMaxDataAttachModemCount == mActiveModemCount) {
                     for (int i = 0; i < mMaxDataAttachModemCount; i++) {
                         newActivePhones.add(i);
@@ -1345,7 +1334,7 @@
 
     /**
      * Switch the Default data for the context of an outgoing emergency call.
-     *
+     * <p>
      * In some cases, we need to try to switch the Default Data subscription before placing the
      * emergency call on DSDS devices. This includes the following situation:
      * - The modem does not support processing GNSS SUPL requests on the non-default data
@@ -1391,16 +1380,6 @@
         }
     }
 
-    private void onPhoneCapabilityChangedInternal(PhoneCapability capability) {
-        int newMaxDataAttachModemCount = TelephonyManager.getDefault()
-                .getNumberOfModemsWithSimultaneousDataConnections();
-        if (mMaxDataAttachModemCount != newMaxDataAttachModemCount) {
-            mMaxDataAttachModemCount = newMaxDataAttachModemCount;
-            logl("Max active phones changed to " + mMaxDataAttachModemCount);
-            onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged");
-        }
-    }
-
     private int phoneIdForRequest(TelephonyNetworkRequest networkRequest) {
         NetworkRequest netRequest = networkRequest.getNativeNetworkRequest();
         int subId = getSubIdFromNetworkSpecifier(netRequest.getNetworkSpecifier());
@@ -1777,7 +1756,7 @@
 
     /**
      * Notify PhoneSwitcher to try to switch data to an opportunistic subscription.
-     *
+     * <p>
      * Set opportunistic data subscription. It's an indication to switch Internet data to this
      * subscription. It has to be an active subscription, and PhoneSwitcher will try to validate
      * it first if needed. If subId is DEFAULT_SUBSCRIPTION_ID, it means we are un-setting
@@ -1856,20 +1835,16 @@
      * @param reason The switch reason.
      * @return The switch reason in string format.
      */
-    private static @NonNull String switchReasonToString(int reason) {
-        switch(reason) {
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN:
-                return "UNKNOWN";
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL:
-                return "MANUAL";
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL:
-                return "IN_CALL";
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_CBRS:
-                return "CBRS";
-            case TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_AUTO:
-                return "AUTO";
-            default: return "UNKNOWN(" + reason + ")";
-        }
+    @NonNull
+    private static String switchReasonToString(int reason) {
+        return switch (reason) {
+            case DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN -> "UNKNOWN";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL -> "MANUAL";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL -> "IN_CALL";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_CBRS -> "CBRS";
+            case DataSwitch.Reason.DATA_SWITCH_REASON_AUTO -> "AUTO";
+            default -> "UNKNOWN(" + reason + ")";
+        };
     }
 
     /**
@@ -1878,16 +1853,14 @@
      * @param state The switching state.
      * @return The switching state in string format.
      */
-    private static @NonNull String switchStateToString(int state) {
-        switch(state) {
-            case TelephonyEvent.EventState.EVENT_STATE_UNKNOWN:
-                return "UNKNOWN";
-            case TelephonyEvent.EventState.EVENT_STATE_START:
-                return "START";
-            case TelephonyEvent.EventState.EVENT_STATE_END:
-                return "END";
-            default: return "UNKNOWN(" + state + ")";
-        }
+    @NonNull
+    private static String switchStateToString(int state) {
+        return switch (state) {
+            case TelephonyEvent.EventState.EVENT_STATE_UNKNOWN -> "UNKNOWN";
+            case TelephonyEvent.EventState.EVENT_STATE_START -> "START";
+            case TelephonyEvent.EventState.EVENT_STATE_END -> "END";
+            default -> "UNKNOWN(" + state + ")";
+        };
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
index ebddbad..a1ac379 100644
--- a/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
+++ b/src/java/com/android/internal/telephony/data/QosCallbackTracker.java
@@ -63,7 +63,8 @@
     private final RcsStats mRcsStats;
 
     // We perform an exact match on the address
-    private final @NonNull Map<Integer, IFilter> mCallbacksToFilter;
+    @NonNull
+    private final Map<Integer, IFilter> mCallbacksToFilter;
 
     private final int mPhoneId;
 
@@ -143,11 +144,6 @@
                                     }
                                 });
                     }
-
-                    @Override
-                    public void onQosCallbackUnregistered(int qosCallbackId) {
-
-                    }
                 });
     }
 
@@ -259,7 +255,6 @@
                         // The filter matches which means it was previously available, and now is
                         // lost
                         if (doFiltersMatch(existingSession, filter)) {
-                            bearerState = DEDICATED_BEARER_EVENT_STATE_DELETED;
                             sendSessionLost(callbackId, existingSession);
                             notifyMetricDedicatedBearerEvent(existingSession, bearerState, true);
                             sessionsReportedToMetric.add(sessionId);
@@ -285,13 +280,13 @@
         });
     }
 
-    private boolean doFiltersMatch(final @NonNull QosBearerSession qosBearerSession,
-            final @NonNull IFilter filter) {
+    private boolean doFiltersMatch(@NonNull final QosBearerSession qosBearerSession,
+                                   @NonNull final IFilter filter) {
         return getMatchingQosBearerFilter(qosBearerSession, filter) != null;
     }
 
-    private boolean matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter,
-            final @NonNull IFilter filter) {
+    private boolean matchesByLocalAddress(@NonNull final QosBearerFilter sessionFilter,
+                                          @NonNull final IFilter filter) {
         int portStart;
         int portEnd;
         if (sessionFilter.getLocalPortRange() == null) {
@@ -320,7 +315,7 @@
     }
 
     private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter,
-            final @NonNull IFilter filter) {
+                                           @NonNull final IFilter filter) {
         int portStart;
         int portEnd;
         boolean result = false;
@@ -350,8 +345,8 @@
     }
 
     private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter,
-            final @NonNull IFilter filter, boolean hasMatchedFilter) {
-        boolean result = false;
+                                      @NonNull final IFilter filter, boolean hasMatchedFilter) {
+        boolean result;
         int protocol = sessionFilter.getProtocol();
         if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP
                 || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) {
@@ -371,8 +366,9 @@
                 ? sessionFilter : qosFilter;
     }
 
-    private @Nullable QosBearerFilter getMatchingQosBearerFilter(
-            @NonNull QosBearerSession qosBearerSession, final @NonNull IFilter filter) {
+    @Nullable
+    private QosBearerFilter getMatchingQosBearerFilter(
+            @NonNull QosBearerSession qosBearerSession, @NonNull final IFilter filter) {
         QosBearerFilter qosFilter = null;
 
         for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
@@ -410,11 +406,11 @@
         return qosFilter;
     }
 
-    private void sendSessionAvailable(final int callbackId, final @NonNull QosBearerSession session,
-            @NonNull IFilter filter) {
+    private void sendSessionAvailable(final int callbackId, @NonNull final QosBearerSession session,
+                                      @NonNull IFilter filter) {
         QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter);
         List<InetSocketAddress> remoteAddresses = new ArrayList<>();
-        if (qosBearerFilter.getRemoteAddresses().size() > 0
+        if (qosBearerFilter != null && !qosBearerFilter.getRemoteAddresses().isEmpty()
                 && qosBearerFilter.getRemotePortRange() != null) {
             remoteAddresses.add(
                     new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(),
@@ -459,17 +455,16 @@
     }
 
     private void notifyMetricDedicatedBearerListenerAdded(final int callbackId,
-            final @NonNull QosBearerSession session) {
+                                                          @NonNull final QosBearerSession session) {
 
-        final int slotId = mPhoneId;
         final int rat = getRatInfoFromSessionInfo(session);
         final int qci = getQCIFromSessionInfo(session);
 
-        mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, slotId, rat, qci);
+        mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, mPhoneId, rat, qci);
     }
 
     private void notifyMetricDedicatedBearerListenerBearerUpdateSession(
-            final int callbackId, final @NonNull QosBearerSession session) {
+            final int callbackId, @NonNull final QosBearerSession session) {
         mRcsStats.onImsDedicatedBearerListenerUpdateSession(callbackId, mPhoneId,
                 getRatInfoFromSessionInfo(session), getQCIFromSessionInfo(session), true);
     }
@@ -522,13 +517,12 @@
 
     private void notifyMetricDedicatedBearerEvent(final QosBearerSession session,
             final int bearerState, final boolean hasListener) {
-        final int slotId = mPhoneId;
         int ratAtEnd = getRatInfoFromSessionInfo(session);
         int qci = getQCIFromSessionInfo(session);
         boolean localConnectionInfoReceived = doesLocalConnectionInfoExist(session);
         boolean remoteConnectionInfoReceived = doesRemoteConnectionInfoExist(session);
 
-        mRcsStats.onImsDedicatedBearerEvent(slotId, ratAtEnd, qci, bearerState,
+        mRcsStats.onImsDedicatedBearerEvent(mPhoneId, ratAtEnd, qci, bearerState,
                 localConnectionInfoReceived, remoteConnectionInfoReceived, hasListener);
     }
 
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
index d90724e..5a81f19 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkAgent.java
@@ -70,7 +70,8 @@
      * The callbacks that are used to pass information to {@link DataNetwork} and
      * {@link QosCallbackTracker}.
      */
-    private final @NonNull Set<TelephonyNetworkAgentCallback> mTelephonyNetworkAgentCallbacks =
+    @NonNull
+    private final Set<TelephonyNetworkAgentCallback> mTelephonyNetworkAgentCallbacks =
             new ArraySet<>();
 
     /**
@@ -110,7 +111,7 @@
 
         /**
          * Called when a qos callback is registered with a filter.
-         *
+         * <p>
          * Any QoS events that are sent with the same callback id after this method is called are a
          * no-op.
          *
@@ -267,7 +268,7 @@
      * @param filter the filter being registered
      */
     @Override
-    public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
+    public void onQosCallbackRegistered(final int qosCallbackId, @NonNull final QosFilter filter) {
         if (mAbandoned) {
             log("The agent is already abandoned. Ignored onQosCallbackRegistered.");
             return;
@@ -278,7 +279,7 @@
 
     /**
      * Called when a qos callback is registered with a filter.
-     *
+     * <p>
      * Any QoS events that are sent with the same callback id after this method is called are a
      * no-op.
      *
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
index a16cee6..ca34ca7 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -265,13 +265,13 @@
     }
 
     @Override
-    public void needNetworkFor(NetworkRequest networkRequest) {
+    public void needNetworkFor(@NonNull NetworkRequest networkRequest) {
         Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
         msg.obj = networkRequest;
         msg.sendToTarget();
     }
 
-    private void onNeedNetworkFor(Message msg) {
+    private void onNeedNetworkFor(@NonNull Message msg) {
         TelephonyNetworkRequest networkRequest =
                 new TelephonyNetworkRequest((NetworkRequest) msg.obj, mPhone, mFlags);
         boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest(
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
index b059100..117eb36 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -24,6 +24,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.os.SystemClock;
+import android.telephony.Annotation.ConnectivityTransport;
 import android.telephony.Annotation.NetCapability;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.DataProfile;
@@ -137,19 +138,22 @@
     );
 
     /** The phone instance. */
-    private final @NonNull Phone mPhone;
+    @NonNull
+    private final Phone mPhone;
 
     /**
      * Native network request from the clients. See {@link NetworkRequest};
      */
-    private final @NonNull NetworkRequest mNativeNetworkRequest;
+    @NonNull
+    private final NetworkRequest mNativeNetworkRequest;
 
     /**
      * The attributes of the network capabilities in this network request. This describes how this
      * network request can be translated to different fields in {@link DataProfile} or perform
      * special actions in telephony.
      */
-    private final @NetCapabilityAttribute int mCapabilitiesAttributes;
+    @NetCapabilityAttribute
+    private final int mCapabilitiesAttributes;
 
     /**
      * Priority of the network request. The network request has higher priority will be satisfied
@@ -160,13 +164,15 @@
     /**
      * Data config manager for retrieving data config.
      */
-    private final @NonNull DataConfigManager mDataConfigManager;
+    @NonNull
+    private final DataConfigManager mDataConfigManager;
 
     /**
      * The attached data network. Note that the data network could be in any state. {@code null}
      * indicates this network request is not satisfied.
      */
-    private @Nullable DataNetwork mAttachedDataNetwork;
+    @Nullable
+    private DataNetwork mAttachedDataNetwork;
 
     /**
      * The state of the network request.
@@ -175,16 +181,20 @@
      * @see #REQUEST_STATE_SATISFIED
      */
     // This is not a boolean because there might be more states in the future.
-    private @RequestState int mState;
+    @RequestState
+    private int mState;
 
     /** The timestamp when this network request enters telephony. */
-    private final @ElapsedRealtimeLong long mCreatedTimeMillis;
+    @ElapsedRealtimeLong
+    private final long mCreatedTimeMillis;
 
     /** The data evaluation result. */
-    private @Nullable DataEvaluation mEvaluation;
+    @Nullable
+    private DataEvaluation mEvaluation;
 
     /** Feature flag. */
-    private final @NonNull FeatureFlags mFeatureFlags;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     /**
      * Constructor
@@ -219,14 +229,17 @@
     /**
      * @see NetworkRequest#getNetworkSpecifier()
      */
-    public @Nullable NetworkSpecifier getNetworkSpecifier() {
+    @Nullable
+    public NetworkSpecifier getNetworkSpecifier() {
         return mNativeNetworkRequest.getNetworkSpecifier();
     }
 
     /**
      * @see NetworkRequest#getCapabilities()
      */
-    public @NonNull @NetCapability int[] getCapabilities() {
+    @NonNull
+    @NetCapability
+    public int[] getCapabilities() {
         return mNativeNetworkRequest.getCapabilities();
     }
 
@@ -238,6 +251,23 @@
     }
 
     /**
+     * @see NetworkRequest#getTransportTypes()
+     */
+    @NonNull
+    @ConnectivityTransport
+    public int[] getTransportTypes() {
+        return mNativeNetworkRequest.getTransportTypes();
+    }
+
+    /**
+     * @return {@code true} if the request can be served on the specified transport.
+     * @see NetworkRequest#hasTransport
+     */
+    public boolean hasTransport(@ConnectivityTransport int transport) {
+        return mNativeNetworkRequest.hasTransport(transport);
+    }
+
+    /**
      * @see NetworkRequest#canBeSatisfiedBy(NetworkCapabilities)
      */
     public boolean canBeSatisfiedBy(@Nullable NetworkCapabilities nc) {
@@ -354,7 +384,8 @@
      * @return The highest priority APN type based network capability from this network request. -1
      * if there is no APN type capabilities in this network request.
      */
-    public @NetCapability int getApnTypeNetworkCapability() {
+    @NetCapability
+    public int getApnTypeNetworkCapability() {
         if (!hasAttribute(CAPABILITY_ATTRIBUTE_APN_SETTING)) return -1;
         return Arrays.stream(getCapabilities()).boxed()
                 .filter(cap -> DataUtils.networkCapabilityToApnType(cap) != ApnSetting.TYPE_NONE)
@@ -364,7 +395,8 @@
     /**
      * @return The native network request.
      */
-    public @NonNull NetworkRequest getNativeNetworkRequest() {
+    @NonNull
+    public NetworkRequest getNativeNetworkRequest() {
         return mNativeNetworkRequest;
     }
 
@@ -373,7 +405,7 @@
      *
      * @param dataNetwork The data network.
      */
-    public void setAttachedNetwork(@NonNull DataNetwork dataNetwork) {
+    public void setAttachedNetwork(@Nullable DataNetwork dataNetwork) {
         mAttachedDataNetwork = dataNetwork;
     }
 
@@ -381,7 +413,8 @@
      * @return The attached network. {@code null} indicates the request is not attached to any
      * network (i.e. the request is unsatisfied).
      */
-    public @Nullable DataNetwork getAttachedNetwork() {
+    @Nullable
+    public DataNetwork getAttachedNetwork() {
         return mAttachedDataNetwork;
     }
 
@@ -397,7 +430,8 @@
     /**
      * @return The state of the network request.
      */
-    public @RequestState int getState() {
+    @RequestState
+    public int getState() {
         return mState;
     }
 
@@ -440,7 +474,8 @@
      * @return Os/App id. {@code null} if the request does not have traffic descriptor based network
      * capabilities.
      */
-    public @Nullable OsAppId getOsAppId() {
+    @Nullable
+    public OsAppId getOsAppId() {
         if (!hasAttribute(CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID)) return null;
 
         // We do not support multiple network capabilities translated to Os/App id at this time.
@@ -471,18 +506,19 @@
      * @param state The request state.
      * @return The request state in string format.
      */
-    private static @NonNull String requestStateToString(
+    @NonNull
+    private static String requestStateToString(
             @TelephonyNetworkRequest.RequestState int state) {
-        switch (state) {
-            case TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED: return "UNSATISFIED";
-            case TelephonyNetworkRequest.REQUEST_STATE_SATISFIED: return "SATISFIED";
-            default: return "UNKNOWN(" + state + ")";
-        }
+        return switch (state) {
+            case TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED -> "UNSATISFIED";
+            case TelephonyNetworkRequest.REQUEST_STATE_SATISFIED -> "SATISFIED";
+            default -> "UNKNOWN(" + state + ")";
+        };
     }
 
     @Override
     public String toString() {
-        return "[" + mNativeNetworkRequest.toString() + ", mPriority=" + mPriority
+        return "[" + mNativeNetworkRequest + ", mPriority=" + mPriority
                 + ", state=" + requestStateToString(mState)
                 + ", mAttachedDataNetwork=" + (mAttachedDataNetwork != null
                 ? mAttachedDataNetwork.name() : null) + ", isMetered=" + isMeteredRequest()
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
index ee8517d..e4ae592 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
@@ -271,7 +271,9 @@
                 mHandler,
                 mRestartBindingRunnable);
 
-        int numPhones = TelephonyManager.getDefault().getActiveModemCount();
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        int numPhones = tm.getSupportedModemCount();
+        logi("numPhones=" + numPhones);
         mConnectionCounts = new int[numPhones];
         for (int i = 0; i < numPhones; i++) {
             mConnectionCounts[i] = 0;
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/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 02dd613..06ebff2 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -50,6 +50,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyCapabilities;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.EmergencyNumberStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -186,7 +187,7 @@
         mFeatureFlags = featureFlags;
         mResources = ctx.getResources();
 
-        if (mFeatureFlags.minimalTelephonyCdmCheck()
+        if (TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)
                 && !ctx.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY_CALLING)) {
             throw new UnsupportedOperationException("EmergencyNumberTracker requires calling");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index dcb3b20..e73eafd 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -3553,8 +3553,10 @@
             if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
 
             int eccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+            List<String> emergencyUrns = new ArrayList<>();
             if (imsCall != null && imsCall.getCallProfile() != null) {
                 eccCategory = imsCall.getCallProfile().getEmergencyServiceCategories();
+                emergencyUrns = imsCall.getCallProfile().getEmergencyUrns();
             }
 
             if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
@@ -3581,13 +3583,14 @@
                 // Since onCallInitiating and onCallProgressing reset mPendingMO,
                 // we can't depend on mPendingMO.
                 if (conn != null) {
-                    logi("onCallStartFailed eccCategory=" + eccCategory);
+                    logi("onCallStartFailed eccCategory=" + eccCategory + ", emergencyUrns="
+                            + emergencyUrns);
                     int reason = reasonInfo.getCode();
                     int extraCode = reasonInfo.getExtraCode();
                     if ((reason == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
                             && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)
                             || (reason == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL)) {
-                        conn.setNonDetectableEmergencyCallInfo(eccCategory);
+                        conn.setNonDetectableEmergencyCallInfo(eccCategory, emergencyUrns);
                     }
                     conn.setImsReasonInfo(reasonInfo);
                     sendCallStartFailedDisconnect(imsCall, reasonInfo);
@@ -3765,11 +3768,13 @@
                     && DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
                 if (conn != null) {
                     int eccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+                    List<String> emergencyUrns = new ArrayList<>();
                     if (imsCall != null && imsCall.getCallProfile() != null) {
                         eccCategory = imsCall.getCallProfile().getEmergencyServiceCategories();
+                        emergencyUrns = imsCall.getCallProfile().getEmergencyUrns();
                         logi("onCallTerminated eccCategory=" + eccCategory);
                     }
-                    conn.setNonDetectableEmergencyCallInfo(eccCategory);
+                    conn.setNonDetectableEmergencyCallInfo(eccCategory, emergencyUrns);
                 }
                 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
                 return;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
old mode 100755
new mode 100644
index aee8867..316f62a
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -286,13 +286,13 @@
 
         mIsEmergency = isEmergency;
         if (isEmergency) {
-            setEmergencyCallInfo(mOwner);
+            setEmergencyCallInfo(mOwner, dialArgs);
 
             if (getEmergencyNumberInfo() == null) {
                 // 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
                 // non-detectable emergency call from IMS.
-                setNonDetectableEmergencyCallInfo(dialArgs.eccCategory);
+                setNonDetectableEmergencyCallInfo(dialArgs.eccCategory, new ArrayList<String>());
             }
         }
 
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index 13fb826..175f5e4 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -309,6 +309,7 @@
         copy.isIwlanCrossSim = call.isIwlanCrossSim;
         copy.isNtn = call.isNtn;
         copy.isSatelliteTransport = call.isSatelliteTransport;
+        copy.isProvisioningProfile = call.isProvisioningProfile;
         return copy;
     }
 
@@ -339,6 +340,7 @@
         proto.isNtn = mSatelliteController != null
                 ? mSatelliteController.isInSatelliteModeForCarrierRoaming(mPhone) : false;
         proto.isSatelliteTransport = isSatellite;
+        proto.isProvisioningProfile = getIsProvisioningProfile();
         return proto;
     }
 
@@ -355,6 +357,17 @@
         return subInfo != null && subInfo.isOpportunistic();
     }
 
+    private boolean getIsProvisioningProfile() {
+        SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+                .getSubscriptionInfoInternal(mPhone.getSubId());
+        try {
+            return subInfo.getProfileClass() == SubscriptionManager.PROFILE_CLASS_PROVISIONING;
+        } catch (Exception ex) {
+            loge("getIsProvisioningProfile: " + ex.getMessage());
+            return false;
+        }
+    }
+
     private boolean getIsOos() {
         ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
         ServiceState serviceState =
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 1078399..a83cd06 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -17,6 +17,8 @@
 package com.android.internal.telephony.metrics;
 
 import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_VERSION;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ROAMING_SATELLITE_SESSION;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION;
@@ -37,7 +39,10 @@
 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT;
 import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS;
 import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_ACCESS_CONTROLLER;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_CONFIG_UPDATER;
 import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_CONTROLLER;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_ENTITLEMENT;
 import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_INCOMING_DATAGRAM;
 import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_OUTGOING_DATAGRAM;
 import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_PROVISION;
@@ -70,6 +75,8 @@
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteSession;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
@@ -89,7 +96,10 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteAccessController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteConfigUpdater;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteEntitlement;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
@@ -178,14 +188,15 @@
 
     public MetricsCollector(Context context, @NonNull FeatureFlags featureFlags) {
         this(context, new PersistAtomsStorage(context),
-                new DeviceStateHelper(context), new VonrHelper(featureFlags), featureFlags);
+                new DeviceStateHelper(context), new VonrHelper(featureFlags),
+                new DefaultNetworkMonitor(context, featureFlags), featureFlags);
     }
 
     /** Allows dependency injection. Used during unit tests. */
     @VisibleForTesting
-    public MetricsCollector(
-            Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper,
-                    VonrHelper vonrHelper, @NonNull FeatureFlags featureFlags) {
+    public MetricsCollector(Context context, PersistAtomsStorage storage,
+            DeviceStateHelper deviceStateHelper, VonrHelper vonrHelper,
+            DefaultNetworkMonitor defaultNetworkMonitor, @NonNull FeatureFlags featureFlags) {
         mStorage = storage;
         mDeviceStateHelper = deviceStateHelper;
         mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
@@ -229,6 +240,11 @@
             registerAtom(SATELLITE_PROVISION);
             registerAtom(SATELLITE_SOS_MESSAGE_RECOMMENDER);
             registerAtom(DATA_NETWORK_VALIDATION);
+            registerAtom(CARRIER_ROAMING_SATELLITE_SESSION);
+            registerAtom(CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS);
+            registerAtom(SATELLITE_ENTITLEMENT);
+            registerAtom(SATELLITE_CONFIG_UPDATER);
+            registerAtom(SATELLITE_ACCESS_CONTROLLER);
             Rlog.d(TAG, "registered");
         } else {
             Rlog.e(TAG, "could not get StatsManager, atoms not registered");
@@ -325,6 +341,16 @@
                 return pullSatelliteSosMessageRecommender(data);
             case DATA_NETWORK_VALIDATION:
                 return pullDataNetworkValidation(data);
+            case CARRIER_ROAMING_SATELLITE_SESSION:
+                return pullCarrierRoamingSatelliteSession(data);
+            case CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS:
+                return pullCarrierRoamingSatelliteControllerStats(data);
+            case SATELLITE_ENTITLEMENT:
+                return pullSatelliteEntitlement(data);
+            case SATELLITE_CONFIG_UPDATER:
+                return pullSatelliteConfigUpdater(data);
+            case SATELLITE_ACCESS_CONTROLLER:
+                return pullSatelliteAccessController(data);
             default:
                 Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
                 return StatsManager.PULL_SKIP;
@@ -958,6 +984,72 @@
         }
     }
 
+    private int pullCarrierRoamingSatelliteSession(List<StatsEvent> data) {
+        CarrierRoamingSatelliteSession[] carrierRoamingSatelliteSessionAtoms =
+                mStorage.getCarrierRoamingSatelliteSessionStats(MIN_COOLDOWN_MILLIS);
+        if (carrierRoamingSatelliteSessionAtoms != null) {
+            Arrays.stream(carrierRoamingSatelliteSessionAtoms)
+                    .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "CARRIER_ROAMING_SATELLITE_SESSION pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
+    private int pullCarrierRoamingSatelliteControllerStats(List<StatsEvent> data) {
+        CarrierRoamingSatelliteControllerStats[] carrierRoamingSatelliteControllerStatsAtoms =
+                mStorage.getCarrierRoamingSatelliteControllerStats(MIN_COOLDOWN_MILLIS);
+        if (carrierRoamingSatelliteControllerStatsAtoms != null) {
+            Arrays.stream(carrierRoamingSatelliteControllerStatsAtoms)
+                    .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS "
+                    + "pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
+    private int pullSatelliteEntitlement(List<StatsEvent> data) {
+        SatelliteEntitlement[] satelliteEntitlementAtoms =
+                mStorage.getSatelliteEntitlementStats(MIN_COOLDOWN_MILLIS);
+        if (satelliteEntitlementAtoms != null) {
+            Arrays.stream(satelliteEntitlementAtoms)
+                    .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "SATELLITE_ENTITLEMENT pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
+    private int pullSatelliteConfigUpdater(List<StatsEvent> data) {
+        SatelliteConfigUpdater[] satelliteConfigUpdaterAtoms =
+                mStorage.getSatelliteConfigUpdaterStats(MIN_COOLDOWN_MILLIS);
+        if (satelliteConfigUpdaterAtoms != null) {
+            Arrays.stream(satelliteConfigUpdaterAtoms)
+                    .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "SATELLITE_CONFIG_UPDATER pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
+    private int pullSatelliteAccessController(List<StatsEvent> data) {
+        SatelliteAccessController[] satelliteAccessControllerAtoms =
+                mStorage.getSatelliteAccessControllerStats(MIN_COOLDOWN_MILLIS);
+        if (satelliteAccessControllerAtoms != null) {
+            Arrays.stream(satelliteAccessControllerAtoms)
+                    .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+            return StatsManager.PULL_SUCCESS;
+        } else {
+            Rlog.w(TAG, "SATELLITE_ACCESS_CONTROLLER pull too frequent, skipping");
+            return StatsManager.PULL_SKIP;
+        }
+    }
+
     /** Registers a pulled atom ID {@code atomId}. */
     private void registerAtom(int atomId) {
         mStatsManager.setPullAtomCallback(atomId, /* metadata= */ null,
@@ -1053,7 +1145,9 @@
                 session.isIwlanCrossSimAtEnd,
                 session.isIwlanCrossSimAtConnected,
                 session.vonrEnabled,
-                session.isNtn);
+                session.isNtn,
+                session.supportsBusinessCallComposer,
+                session.callComposerStatus);
 
     }
 
@@ -1076,7 +1170,8 @@
                 sms.messageId,
                 sms.count,
                 sms.isManagedProfile,
-                sms.isNtn);
+                sms.isNtn,
+                sms.isEmergency);
     }
 
     private static StatsEvent buildStatsEvent(OutgoingSms sms) {
@@ -1132,7 +1227,8 @@
                 dataCallSession.isNonDds,
                 dataCallSession.isIwlanCrossSim,
                 dataCallSession.isNtn,
-                dataCallSession.isSatelliteTransport);
+                dataCallSession.isSatelliteTransport,
+                dataCallSession.isProvisioningProfile);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
@@ -1375,7 +1471,10 @@
                 satelliteController.countOfDemoModeIncomingDatagramSuccess,
                 satelliteController.countOfDemoModeIncomingDatagramFail,
                 satelliteController.countOfDatagramTypeKeepAliveSuccess,
-                satelliteController.countOfDatagramTypeKeepAliveFail);
+                satelliteController.countOfDatagramTypeKeepAliveFail,
+                satelliteController.countOfAllowedSatelliteAccess,
+                satelliteController.countOfDisallowedSatelliteAccess,
+                satelliteController.countOfSatelliteAccessCheckFail);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteSession satelliteSession) {
@@ -1392,7 +1491,8 @@
                 satelliteSession.countOfOutgoingDatagramFailed,
                 satelliteSession.countOfIncomingDatagramSuccess,
                 satelliteSession.countOfIncomingDatagramFailed,
-                satelliteSession.isDemoMode);
+                satelliteSession.isDemoMode,
+                satelliteSession.maxNtnSignalStrengthLevel);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteIncomingDatagram stats) {
@@ -1448,6 +1548,71 @@
                 stats.networkValidationCount);
     }
 
+    private static StatsEvent buildStatsEvent(CarrierRoamingSatelliteSession stats) {
+        return TelephonyStatsLog.buildStatsEvent(
+                CARRIER_ROAMING_SATELLITE_SESSION,
+                stats.carrierId,
+                stats.isNtnRoamingInHomeCountry,
+                stats.totalSatelliteModeTimeSec,
+                stats.numberOfSatelliteConnections,
+                stats.avgDurationOfSatelliteConnectionSec,
+                stats.satelliteConnectionGapMinSec,
+                stats.satelliteConnectionGapAvgSec,
+                stats.satelliteConnectionGapMaxSec,
+                stats.rsrpAvg,
+                stats.rsrpMedian,
+                stats.rssnrAvg,
+                stats.rssnrMedian,
+                stats.countOfIncomingSms,
+                stats.countOfOutgoingSms,
+                stats.countOfIncomingMms,
+                stats.countOfOutgoingMms);
+    }
+
+    private static StatsEvent buildStatsEvent(CarrierRoamingSatelliteControllerStats stats) {
+        return TelephonyStatsLog.buildStatsEvent(
+                CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS,
+                stats.configDataSource,
+                stats.countOfEntitlementStatusQueryRequest,
+                stats.countOfSatelliteConfigUpdateRequest,
+                stats.countOfSatelliteNotificationDisplayed,
+                stats.satelliteSessionGapMinSec,
+                stats.satelliteSessionGapAvgSec,
+                stats.satelliteSessionGapMaxSec);
+    }
+
+    private static StatsEvent buildStatsEvent(SatelliteEntitlement stats) {
+        return TelephonyStatsLog.buildStatsEvent(
+                SATELLITE_ENTITLEMENT,
+                stats.carrierId,
+                stats.result,
+                stats.entitlementStatus,
+                stats.isRetry,
+                stats.count);
+    }
+
+    private static StatsEvent buildStatsEvent(SatelliteConfigUpdater stats) {
+        return TelephonyStatsLog.buildStatsEvent(SATELLITE_CONFIG_UPDATER,
+                stats.configVersion,
+                stats.oemConfigResult,
+                stats.carrierConfigResult,
+                stats.count);
+    }
+
+    private static StatsEvent buildStatsEvent(SatelliteAccessController stats) {
+        return TelephonyStatsLog.buildStatsEvent(
+                SATELLITE_ACCESS_CONTROLLER,
+                stats.accessControlType,
+                stats.locationQueryTimeMillis,
+                stats.onDeviceLookupTimeMillis,
+                stats.totalCheckingTimeMillis,
+                stats.isAllowed,
+                stats.isEmergency,
+                stats.resultCode,
+                stats.countryCodes,
+                stats.configDataSource);
+    }
+
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
     static Phone[] getPhonesIfAny() {
         try {
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index f55d968..12dab7a 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -30,6 +30,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteSession;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
@@ -49,7 +51,10 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteAccessController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteConfigUpdater;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteEntitlement;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
@@ -173,6 +178,7 @@
     /** Maximum number of Satellite relevant stats to store between pulls. */
     private final int mMaxNumSatelliteStats;
     private final int mMaxNumSatelliteControllerStats = 1;
+    private final int mMaxNumCarrierRoamingSatelliteSessionStats = 1;
 
     /** Maximum number of data network validation to store during pulls. */
     private final int mMaxNumDataNetworkValidation;
@@ -761,6 +767,9 @@
                 += stats.countOfDatagramTypeKeepAliveSuccess;
         atom.countOfDatagramTypeKeepAliveFail
                 += stats.countOfDatagramTypeKeepAliveFail;
+        atom.countOfAllowedSatelliteAccess += stats.countOfAllowedSatelliteAccess;
+        atom.countOfDisallowedSatelliteAccess += stats.countOfDisallowedSatelliteAccess;
+        atom.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail;
 
         mAtoms.satelliteController = atomArray;
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
@@ -832,6 +841,71 @@
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
     }
 
+    /** Adds a new {@link CarrierRoamingSatelliteSession} to the storage. */
+    public synchronized void addCarrierRoamingSatelliteSessionStats(
+            CarrierRoamingSatelliteSession stats) {
+        mAtoms.carrierRoamingSatelliteSession = insertAtRandomPlace(
+                mAtoms.carrierRoamingSatelliteSession, stats,
+                mMaxNumCarrierRoamingSatelliteSessionStats);
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
+    /** Adds a new {@link CarrierRoamingSatelliteControllerStats} to the storage. */
+    public synchronized void addCarrierRoamingSatelliteControllerStats(
+            CarrierRoamingSatelliteControllerStats stats) {
+        // CarrierRoamingSatelliteController is a single data point
+        CarrierRoamingSatelliteControllerStats[] atomArray =
+                mAtoms.carrierRoamingSatelliteControllerStats;
+        if (atomArray == null || atomArray.length == 0) {
+            atomArray = new CarrierRoamingSatelliteControllerStats[] {new
+                    CarrierRoamingSatelliteControllerStats()};
+        }
+
+        CarrierRoamingSatelliteControllerStats atom = atomArray[0];
+        atom.configDataSource = stats.configDataSource;
+        atom.countOfEntitlementStatusQueryRequest += stats.countOfEntitlementStatusQueryRequest;
+        atom.countOfSatelliteConfigUpdateRequest += stats.countOfSatelliteConfigUpdateRequest;
+        atom.countOfSatelliteNotificationDisplayed += stats.countOfSatelliteNotificationDisplayed;
+        atom.satelliteSessionGapMinSec = stats.satelliteSessionGapMinSec;
+        atom.satelliteSessionGapAvgSec = stats.satelliteSessionGapAvgSec;
+        atom.satelliteSessionGapMaxSec = stats.satelliteSessionGapMaxSec;
+
+        mAtoms.carrierRoamingSatelliteControllerStats = atomArray;
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
+    /** Adds a new {@link SatelliteEntitlement} to the storage. */
+    public synchronized void addSatelliteEntitlementStats(SatelliteEntitlement stats) {
+        SatelliteEntitlement existingStats = find(stats);
+        if (existingStats != null) {
+            existingStats.count += 1;
+        } else {
+            mAtoms.satelliteEntitlement = insertAtRandomPlace(mAtoms.satelliteEntitlement,
+                    stats, mMaxNumSatelliteStats);
+        }
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
+    /** Adds a new {@link SatelliteConfigUpdater} to the storage. */
+    public synchronized void addSatelliteConfigUpdaterStats(SatelliteConfigUpdater stats) {
+        SatelliteConfigUpdater existingStats = find(stats);
+        if (existingStats != null) {
+            existingStats.count += 1;
+        } else {
+            mAtoms.satelliteConfigUpdater = insertAtRandomPlace(mAtoms.satelliteConfigUpdater,
+                    stats, mMaxNumSatelliteStats);
+        }
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
+    /** Adds a new {@link SatelliteAccessController} to the storage. */
+    public synchronized void addSatelliteAccessControllerStats(SatelliteAccessController stats) {
+        mAtoms.satelliteAccessController =
+                insertAtRandomPlace(mAtoms.satelliteAccessController, stats,
+                        mMaxNumSatelliteStats);
+        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+    }
+
     /**
      * Returns and clears the voice call sessions if last pulled longer than {@code
      * minIntervalMillis} ago, otherwise returns {@code null}.
@@ -1480,7 +1554,7 @@
             long minIntervalMillis) {
         if (getWallTimeMillis() - mAtoms.satelliteSosMessageRecommenderPullTimestampMillis
                 > minIntervalMillis) {
-            mAtoms.satelliteProvisionPullTimestampMillis = getWallTimeMillis();
+            mAtoms.satelliteSosMessageRecommenderPullTimestampMillis = getWallTimeMillis();
             SatelliteSosMessageRecommender[] statsArray = mAtoms.satelliteSosMessageRecommender;
             mAtoms.satelliteSosMessageRecommender = new SatelliteSosMessageRecommender[0];
             saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
@@ -1508,6 +1582,103 @@
         }
     }
 
+    /**
+     * Returns and clears the {@link CarrierRoamingSatelliteSession} stats if last pulled
+     * longer than {@code minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized CarrierRoamingSatelliteSession[] getCarrierRoamingSatelliteSessionStats(
+            long minIntervalMillis) {
+        if (getWallTimeMillis() - mAtoms.carrierRoamingSatelliteSessionPullTimestampMillis
+                > minIntervalMillis) {
+            mAtoms.carrierRoamingSatelliteSessionPullTimestampMillis = getWallTimeMillis();
+            CarrierRoamingSatelliteSession[] statsArray = mAtoms.carrierRoamingSatelliteSession;
+            mAtoms.carrierRoamingSatelliteSession = new CarrierRoamingSatelliteSession[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return statsArray;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns and clears the {@link CarrierRoamingSatelliteControllerStats} stats if last pulled
+     * longer than {@code minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized CarrierRoamingSatelliteControllerStats[]
+            getCarrierRoamingSatelliteControllerStats(long minIntervalMillis) {
+        if (getWallTimeMillis() - mAtoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis
+                > minIntervalMillis) {
+            mAtoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis = getWallTimeMillis();
+            CarrierRoamingSatelliteControllerStats[] statsArray =
+                    mAtoms.carrierRoamingSatelliteControllerStats;
+            mAtoms.carrierRoamingSatelliteControllerStats =
+                    new CarrierRoamingSatelliteControllerStats[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return statsArray;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns and clears the {@link SatelliteEntitlement} stats if last pulled longer than {@code
+     * minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized SatelliteEntitlement[] getSatelliteEntitlementStats(
+            long minIntervalMillis) {
+        if (getWallTimeMillis() - mAtoms.satelliteEntitlementPullTimestampMillis
+                > minIntervalMillis) {
+            mAtoms.satelliteEntitlementPullTimestampMillis = getWallTimeMillis();
+            SatelliteEntitlement[] statsArray = mAtoms.satelliteEntitlement;
+            mAtoms.satelliteEntitlement = new SatelliteEntitlement[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return statsArray;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns and clears the {@link SatelliteConfigUpdater} stats if last pulled longer than {@code
+     * minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized SatelliteConfigUpdater[] getSatelliteConfigUpdaterStats(
+            long minIntervalMillis) {
+        if (getWallTimeMillis() - mAtoms.satelliteConfigUpdaterPullTimestampMillis
+                > minIntervalMillis) {
+            mAtoms.satelliteConfigUpdaterPullTimestampMillis = getWallTimeMillis();
+            SatelliteConfigUpdater[] statsArray = mAtoms.satelliteConfigUpdater;
+            mAtoms.satelliteConfigUpdater = new SatelliteConfigUpdater[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return statsArray;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns and clears the {@link SatelliteAccessController} stats if last pulled longer
+     * than {@code minIntervalMillis} ago, otherwise returns {@code null}.
+     */
+    @Nullable
+    public synchronized SatelliteAccessController[] getSatelliteAccessControllerStats(
+            long minIntervalMillis) {
+        if (getWallTimeMillis() - mAtoms.satelliteAccessControllerPullTimestampMillis
+                > minIntervalMillis) {
+            mAtoms.satelliteAccessControllerPullTimestampMillis = getWallTimeMillis();
+            SatelliteAccessController[] statsArray = mAtoms.satelliteAccessController;
+            mAtoms.satelliteAccessController = new SatelliteAccessController[0];
+            saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+            return statsArray;
+        } else {
+            return null;
+        }
+    }
+
     /** Saves {@link PersistAtoms} to a file in private storage immediately. */
     public synchronized void flushAtoms() {
         saveAtomsToFile(0);
@@ -1664,6 +1835,19 @@
                             DataNetworkValidation.class,
                             mMaxNumDataNetworkValidation
                     );
+            atoms.carrierRoamingSatelliteSession = sanitizeAtoms(
+                    atoms.carrierRoamingSatelliteSession, CarrierRoamingSatelliteSession.class,
+                    mMaxNumSatelliteStats);
+            atoms.carrierRoamingSatelliteControllerStats = sanitizeAtoms(
+                    atoms.carrierRoamingSatelliteControllerStats,
+                    CarrierRoamingSatelliteControllerStats.class, mMaxNumSatelliteControllerStats);
+            atoms.satelliteEntitlement = sanitizeAtoms(atoms.satelliteEntitlement,
+                    SatelliteEntitlement.class, mMaxNumSatelliteStats);
+            atoms.satelliteConfigUpdater = sanitizeAtoms(atoms.satelliteConfigUpdater,
+                    SatelliteConfigUpdater.class, mMaxNumSatelliteStats);
+            atoms.satelliteAccessController = sanitizeAtoms(
+                    atoms.satelliteAccessController, SatelliteAccessController.class,
+                    mMaxNumSatelliteStats);
 
             // out of caution, sanitize also the timestamps
             atoms.voiceCallRatUsagePullTimestampMillis =
@@ -1728,6 +1912,16 @@
                     sanitizeTimestamp(atoms.satelliteSosMessageRecommenderPullTimestampMillis);
             atoms.dataNetworkValidationPullTimestampMillis =
                     sanitizeTimestamp(atoms.dataNetworkValidationPullTimestampMillis);
+            atoms.carrierRoamingSatelliteSessionPullTimestampMillis = sanitizeTimestamp(
+                    atoms.carrierRoamingSatelliteSessionPullTimestampMillis);
+            atoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis = sanitizeTimestamp(
+                    atoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis);
+            atoms.satelliteEntitlementPullTimestampMillis =
+                    sanitizeTimestamp(atoms.satelliteEntitlementPullTimestampMillis);
+            atoms.satelliteConfigUpdaterPullTimestampMillis =
+                    sanitizeTimestamp(atoms.satelliteConfigUpdaterPullTimestampMillis);
+            atoms.satelliteAccessControllerPullTimestampMillis =
+                    sanitizeTimestamp(atoms.satelliteAccessControllerPullTimestampMillis);
             return atoms;
         } catch (NoSuchFileException e) {
             Rlog.d(TAG, "PersistAtoms file not found");
@@ -2117,7 +2311,7 @@
     }
 
     /**
-     * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+     * Returns SatelliteSession atom that has same values or {@code null}
      * if it does not exist.
      */
     private @Nullable SatelliteSession find(
@@ -2136,7 +2330,8 @@
                     && stats.countOfOutgoingDatagramFailed == key.countOfOutgoingDatagramFailed
                     && stats.countOfIncomingDatagramSuccess == key.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed == key.countOfIncomingDatagramFailed
-                    && stats.isDemoMode == key.isDemoMode) {
+                    && stats.isDemoMode == key.isDemoMode
+                    && stats.maxNtnSignalStrengthLevel == key.maxNtnSignalStrengthLevel) {
                 return stats;
             }
         }
@@ -2144,7 +2339,7 @@
     }
 
     /**
-     * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+     * Returns SatelliteSosMessageRecommender atom that has same values or {@code null}
      * if it does not exist.
      */
     private @Nullable SatelliteSosMessageRecommender find(
@@ -2179,6 +2374,35 @@
         return null;
     }
 
+    /**
+     * Returns SatelliteEntitlement atom that has same values or {@code null} if it does not exist.
+     */
+    private @Nullable SatelliteEntitlement find(SatelliteEntitlement key) {
+        for (SatelliteEntitlement stats : mAtoms.satelliteEntitlement) {
+            if (stats.carrierId == key.carrierId
+                    && stats.result == key.result
+                    && stats.entitlementStatus == key.entitlementStatus
+                    && stats.isRetry == key.isRetry) {
+                return stats;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns SatelliteConfigUpdater atom that has same values
+     * or {@code null} if it does not exist.
+     */
+    private @Nullable SatelliteConfigUpdater find(SatelliteConfigUpdater key) {
+        for (SatelliteConfigUpdater stats : mAtoms.satelliteConfigUpdater) {
+            if (stats.configVersion == key.configVersion
+                    && stats.oemConfigResult == key.oemConfigResult
+                    && stats.carrierConfigResult == key.carrierConfigResult) {
+                return stats;
+            }
+        }
+        return null;
+    }
 
     /**
      * Inserts a new element in a random position in an array with a maximum size.
@@ -2437,6 +2661,11 @@
         atoms.satelliteProvisionPullTimestampMillis = currentTime;
         atoms.satelliteSosMessageRecommenderPullTimestampMillis = currentTime;
         atoms.dataNetworkValidationPullTimestampMillis = currentTime;
+        atoms.carrierRoamingSatelliteSessionPullTimestampMillis = currentTime;
+        atoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis = currentTime;
+        atoms.satelliteEntitlementPullTimestampMillis = currentTime;
+        atoms.satelliteConfigUpdaterPullTimestampMillis = currentTime;
+        atoms.satelliteAccessControllerPullTimestampMillis = currentTime;
 
         Rlog.d(TAG, "created new PersistAtoms");
         return atoms;
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
index a9aab58..c2b2753 100644
--- a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -16,17 +16,28 @@
 
 package com.android.internal.telephony.metrics;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
 
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteAccessController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteConfigUpdater;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteEntitlement;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender;
+import com.android.internal.telephony.satellite.SatelliteConstants;
 import com.android.telephony.Rlog;
 
+import java.util.Arrays;
+
 /** Tracks Satellite metrics for each phone */
 public class SatelliteStats {
     private static final String TAG = SatelliteStats.class.getSimpleName();
@@ -77,6 +88,9 @@
         private final int mCountOfDemoModeIncomingDatagramFail;
         private final int mCountOfDatagramTypeKeepAliveSuccess;
         private final int mCountOfDatagramTypeKeepAliveFail;
+        private final int mCountOfAllowedSatelliteAccess;
+        private final int mCountOfDisallowedSatelliteAccess;
+        private final int mCountOfSatelliteAccessCheckFail;
 
         private SatelliteControllerParams(Builder builder) {
             this.mCountOfSatelliteServiceEnablementsSuccess =
@@ -116,6 +130,12 @@
                     builder.mCountOfDatagramTypeKeepAliveSuccess;
             this.mCountOfDatagramTypeKeepAliveFail =
                     builder.mCountOfDatagramTypeKeepAliveFail;
+            this.mCountOfAllowedSatelliteAccess =
+                    builder.mCountOfAllowedSatelliteAccess;
+            this.mCountOfDisallowedSatelliteAccess =
+                    builder.mCountOfDisallowedSatelliteAccess;
+            this.mCountOfSatelliteAccessCheckFail =
+                    builder.mCountOfSatelliteAccessCheckFail;
         }
 
         public int getCountOfSatelliteServiceEnablementsSuccess() {
@@ -218,6 +238,18 @@
             return mCountOfDatagramTypeKeepAliveFail;
         }
 
+        public int getCountOfAllowedSatelliteAccess() {
+            return mCountOfAllowedSatelliteAccess;
+        }
+
+        public int getCountOfDisallowedSatelliteAccess() {
+            return mCountOfDisallowedSatelliteAccess;
+        }
+
+        public int getCountOfSatelliteAccessCheckFail() {
+            return mCountOfSatelliteAccessCheckFail;
+        }
+
         /**
          * A builder class to create {@link SatelliteControllerParams} data structure class
          */
@@ -247,6 +279,9 @@
             private int mCountOfDemoModeIncomingDatagramFail = 0;
             private int mCountOfDatagramTypeKeepAliveSuccess = 0;
             private int mCountOfDatagramTypeKeepAliveFail = 0;
+            private int mCountOfAllowedSatelliteAccess = 0;
+            private int mCountOfDisallowedSatelliteAccess = 0;
+            private int mCountOfSatelliteAccessCheckFail = 0;
 
             /**
              * Sets countOfSatelliteServiceEnablementsSuccess value of {@link SatelliteController}
@@ -495,6 +530,37 @@
             }
 
             /**
+             * Sets countOfAllowedSatelliteAccess value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfAllowedSatelliteAccess(
+                    int countOfAllowedSatelliteAccess) {
+                this.mCountOfAllowedSatelliteAccess =
+                        countOfAllowedSatelliteAccess;
+                return this;
+            }
+
+            /**
+             * Sets countOfDisallowedSatelliteAccess value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfDisallowedSatelliteAccess(
+                    int countOfDisallowedSatelliteAccess) {
+                this.mCountOfDisallowedSatelliteAccess = countOfDisallowedSatelliteAccess;
+                return this;
+            }
+
+            /**
+             * Sets countOfSatelliteAccessCheckFail value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfSatelliteAccessCheckFail(
+                    int countOfSatelliteAccessCheckFail) {
+                this.mCountOfSatelliteAccessCheckFail = countOfSatelliteAccessCheckFail;
+                return this;
+            }
+
+            /**
              * Returns ControllerParams, which contains whole component of
              * {@link SatelliteController} atom
              */
@@ -540,6 +606,9 @@
                     + mCountOfDatagramTypeKeepAliveSuccess
                     + ", countOfDatagramTypeKeepAliveFail="
                     + mCountOfDatagramTypeKeepAliveFail
+                    + ", countOfAllowedSatelliteAccess=" + mCountOfAllowedSatelliteAccess
+                    + ", countOfDisallowedSatelliteAccess=" + mCountOfDisallowedSatelliteAccess
+                    + ", countOfSatelliteAccessCheckFail=" + mCountOfSatelliteAccessCheckFail
                     + ")";
         }
     }
@@ -560,6 +629,7 @@
         private final int mCountOfIncomingDatagramSuccess;
         private final int mCountOfIncomingDatagramFailed;
         private final boolean mIsDemoMode;
+        private final @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
 
         private SatelliteSessionParams(Builder builder) {
             this.mSatelliteServiceInitializationResult =
@@ -575,6 +645,7 @@
             this.mCountOfIncomingDatagramSuccess = builder.mCountOfIncomingDatagramSuccess;
             this.mCountOfIncomingDatagramFailed = builder.mCountOfIncomingDatagramFailed;
             this.mIsDemoMode = builder.mIsDemoMode;
+            this.mMaxNtnSignalStrengthLevel = builder.mMaxNtnSignalStrengthLevel;
         }
 
         public int getSatelliteServiceInitializationResult() {
@@ -621,6 +692,10 @@
             return mIsDemoMode;
         }
 
+        public @NtnSignalStrength.NtnSignalStrengthLevel int getMaxNtnSignalStrengthLevel() {
+            return mMaxNtnSignalStrengthLevel;
+        }
+
         /**
          * A builder class to create {@link SatelliteSessionParams} data structure class
          */
@@ -636,6 +711,8 @@
             private int mCountOfIncomingDatagramSuccess = -1;
             private int mCountOfIncomingDatagramFailed = -1;
             private boolean mIsDemoMode = false;
+            private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel =
+                    NTN_SIGNAL_STRENGTH_NONE;
 
             /**
              * Sets satelliteServiceInitializationResult value of {@link SatelliteSession}
@@ -711,6 +788,13 @@
                 return this;
             }
 
+            /** Sets the max ntn signal strength for the satellite session */
+            public Builder setMaxNtnSignalStrengthLevel(
+                    @NtnSignalStrength.NtnSignalStrengthLevel int maxNtnSignalStrengthLevel) {
+                this.mMaxNtnSignalStrengthLevel = maxNtnSignalStrengthLevel;
+                return this;
+            }
+
             /**
              * Returns SessionParams, which contains whole component of
              * {@link SatelliteSession} atom
@@ -735,6 +819,7 @@
                     + ", CountOfIncomingDatagramSuccess=" + mCountOfIncomingDatagramSuccess
                     + ", CountOfIncomingDatagramFailed=" + mCountOfIncomingDatagramFailed
                     + ", IsDemoMode=" + mIsDemoMode
+                    + ", MaxNtnSignalStrengthLevel=" + mMaxNtnSignalStrengthLevel
                     + ")";
         }
     }
@@ -1206,6 +1291,854 @@
         }
     }
 
+    /**
+     * A data class to contain whole component of {@link CarrierRoamingSatelliteSession} atom.
+     * Refer to {@link #onCarrierRoamingSatelliteSessionMetrics(
+     * CarrierRoamingSatelliteSessionParams)}.
+     */
+    public class CarrierRoamingSatelliteSessionParams {
+        private final int mCarrierId;
+        private final boolean mIsNtnRoamingInHomeCountry;
+        private final int mTotalSatelliteModeTimeSec;
+        private final int mNumberOfSatelliteConnections;
+        private final int mAvgDurationOfSatelliteConnectionSec;
+        private final int mSatelliteConnectionGapMinSec;
+        private final int mSatelliteConnectionGapAvgSec;
+        private final int mSatelliteConnectionGapMaxSec;
+        private final int mRsrpAvg;
+        private final int mRsrpMedian;
+        private final int mRssnrAvg;
+        private final int mRssnrMedian;
+        private final int mCountOfIncomingSms;
+        private final int mCountOfOutgoingSms;
+        private final int mCountOfIncomingMms;
+        private final int mCountOfOutgoingMms;
+
+        private CarrierRoamingSatelliteSessionParams(Builder builder) {
+            this.mCarrierId = builder.mCarrierId;
+            this.mIsNtnRoamingInHomeCountry = builder.mIsNtnRoamingInHomeCountry;
+            this.mTotalSatelliteModeTimeSec = builder.mTotalSatelliteModeTimeSec;
+            this.mNumberOfSatelliteConnections = builder.mNumberOfSatelliteConnections;
+            this.mAvgDurationOfSatelliteConnectionSec =
+                    builder.mAvgDurationOfSatelliteConnectionSec;
+            this.mSatelliteConnectionGapMinSec = builder.mSatelliteConnectionGapMinSec;
+            this.mSatelliteConnectionGapAvgSec = builder.mSatelliteConnectionGapAvgSec;
+            this.mSatelliteConnectionGapMaxSec = builder.mSatelliteConnectionGapMaxSec;
+            this.mRsrpAvg = builder.mRsrpAvg;
+            this.mRsrpMedian = builder.mRsrpMedian;
+            this.mRssnrAvg = builder.mRssnrAvg;
+            this.mRssnrMedian = builder.mRssnrMedian;
+            this.mCountOfIncomingSms = builder.mCountOfIncomingSms;
+            this.mCountOfOutgoingSms = builder.mCountOfOutgoingSms;
+            this.mCountOfIncomingMms = builder.mCountOfIncomingMms;
+            this.mCountOfOutgoingMms = builder.mCountOfOutgoingMms;
+        }
+
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
+        public boolean getIsNtnRoamingInHomeCountry() {
+            return mIsNtnRoamingInHomeCountry;
+        }
+
+        public int getTotalSatelliteModeTimeSec() {
+            return mTotalSatelliteModeTimeSec;
+        }
+
+        public int getNumberOfSatelliteConnections() {
+            return mNumberOfSatelliteConnections;
+        }
+
+        public int getAvgDurationOfSatelliteConnectionSec() {
+            return mAvgDurationOfSatelliteConnectionSec;
+        }
+
+        public int getSatelliteConnectionGapMinSec() {
+            return mSatelliteConnectionGapMinSec;
+        }
+
+        public int getSatelliteConnectionGapAvgSec() {
+            return mSatelliteConnectionGapAvgSec;
+        }
+
+        public int getSatelliteConnectionGapMaxSec() {
+            return mSatelliteConnectionGapMaxSec;
+        }
+
+        public int getRsrpAvg() {
+            return mRsrpAvg;
+        }
+
+        public int getRsrpMedian() {
+            return mRsrpMedian;
+        }
+
+        public int getRssnrAvg() {
+            return mRssnrAvg;
+        }
+
+        public int getRssnrMedian() {
+            return mRssnrMedian;
+        }
+
+        public int getCountOfIncomingSms() {
+            return mCountOfIncomingSms;
+        }
+
+        public int getCountOfOutgoingSms() {
+            return mCountOfOutgoingSms;
+        }
+
+        public int getCountOfIncomingMms() {
+            return mCountOfIncomingMms;
+        }
+
+        public int getCountOfOutgoingMms() {
+            return mCountOfOutgoingMms;
+        }
+
+        /**
+         * A builder class to create {@link CarrierRoamingSatelliteSessionParams} data structure
+         * class
+         */
+        public static class Builder {
+            private int mCarrierId = -1;
+            private boolean mIsNtnRoamingInHomeCountry = false;
+            private int mTotalSatelliteModeTimeSec = 0;
+            private int mNumberOfSatelliteConnections = 0;
+            private int mAvgDurationOfSatelliteConnectionSec = 0;
+            private int mSatelliteConnectionGapMinSec = 0;
+            private int mSatelliteConnectionGapAvgSec = 0;
+            private int mSatelliteConnectionGapMaxSec = 0;
+            private int mRsrpAvg = 0;
+            private int mRsrpMedian = 0;
+            private int mRssnrAvg = 0;
+            private int mRssnrMedian = 0;
+            private int mCountOfIncomingSms = 0;
+            private int mCountOfOutgoingSms = 0;
+            private int mCountOfIncomingMms = 0;
+            private int mCountOfOutgoingMms = 0;
+
+            /**
+             * Sets carrierId value of {@link CarrierRoamingSatelliteSession} atom
+             * then returns Builder class
+             */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /**
+             * Sets isNtnRoamingInHomeCountry value of {@link CarrierRoamingSatelliteSession} atom
+             * then returns Builder class
+             */
+            public Builder setIsNtnRoamingInHomeCountry(boolean isNtnRoamingInHomeCountry) {
+                this.mIsNtnRoamingInHomeCountry = isNtnRoamingInHomeCountry;
+                return this;
+            }
+
+            /**
+             * Sets totalSatelliteModeTimeSec value of {@link CarrierRoamingSatelliteSession} atom
+             * then returns Builder class
+             */
+            public Builder setTotalSatelliteModeTimeSec(int totalSatelliteModeTimeSec) {
+                this.mTotalSatelliteModeTimeSec = totalSatelliteModeTimeSec;
+                return this;
+            }
+
+
+            /**
+             * Sets numberOfSatelliteConnections value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setNumberOfSatelliteConnections(int numberOfSatelliteConnections) {
+                this.mNumberOfSatelliteConnections = numberOfSatelliteConnections;
+                return this;
+            }
+
+            /**
+             * Sets avgDurationOfSatelliteConnectionSec value of
+             * {@link CarrierRoamingSatelliteSession} atom then returns Builder class
+             */
+            public Builder setAvgDurationOfSatelliteConnectionSec(
+                    int avgDurationOfSatelliteConnectionSec) {
+                this.mAvgDurationOfSatelliteConnectionSec = avgDurationOfSatelliteConnectionSec;
+                return this;
+            }
+
+            /**
+             * Sets satelliteConnectionGapMinSec value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setSatelliteConnectionGapMinSec(int satelliteConnectionGapMinSec) {
+                this.mSatelliteConnectionGapMinSec = satelliteConnectionGapMinSec;
+                return this;
+            }
+
+            /**
+             * Sets satelliteConnectionGapAvgSec value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setSatelliteConnectionGapAvgSec(int satelliteConnectionGapAvgSec) {
+                this.mSatelliteConnectionGapAvgSec = satelliteConnectionGapAvgSec;
+                return this;
+            }
+
+            /**
+             * Sets satelliteConnectionGapMaxSec value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setSatelliteConnectionGapMaxSec(int satelliteConnectionGapMaxSec) {
+                this.mSatelliteConnectionGapMaxSec = satelliteConnectionGapMaxSec;
+                return this;
+            }
+
+            /**
+             * Sets rsrpAvg value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setRsrpAvg(int rsrpAvg) {
+                this.mRsrpAvg = rsrpAvg;
+                return this;
+            }
+
+            /**
+             * Sets rsrpMedian value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setRsrpMedian(int rsrpMedian) {
+                this.mRsrpMedian = rsrpMedian;
+                return this;
+            }
+
+            /**
+             * Sets rssnrAvg value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setRssnrAvg(int rssnrAvg) {
+                this.mRssnrAvg = rssnrAvg;
+                return this;
+            }
+
+            /**
+             * Sets rssnrMedian value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setRssnrMedian(int rssnrMedian) {
+                this.mRssnrMedian = rssnrMedian;
+                return this;
+            }
+
+
+            /**
+             * Sets countOfIncomingSms value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setCountOfIncomingSms(int countOfIncomingSms) {
+                this.mCountOfIncomingSms = countOfIncomingSms;
+                return this;
+            }
+
+            /**
+             * Sets countOfOutgoingSms value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setCountOfOutgoingSms(int countOfOutgoingSms) {
+                this.mCountOfOutgoingSms = countOfOutgoingSms;
+                return this;
+            }
+
+            /**
+             * Sets countOfIncomingMms value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setCountOfIncomingMms(int countOfIncomingMms) {
+                this.mCountOfIncomingMms = countOfIncomingMms;
+                return this;
+            }
+
+            /**
+             * Sets countOfOutgoingMms value of {@link CarrierRoamingSatelliteSession}
+             * atom then returns Builder class
+             */
+            public Builder setCountOfOutgoingMms(int countOfOutgoingMms) {
+                this.mCountOfOutgoingMms = countOfOutgoingMms;
+                return this;
+            }
+
+            /**
+             * Returns CarrierRoamingSatelliteSessionParams, which contains whole component of
+             * {@link CarrierRoamingSatelliteSession} atom
+             */
+            public CarrierRoamingSatelliteSessionParams build() {
+                return new SatelliteStats()
+                        .new CarrierRoamingSatelliteSessionParams(Builder.this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "CarrierRoamingSatelliteSessionParams("
+                    + "carrierId=" + mCarrierId
+                    + ", isNtnRoamingInHomeCountry=" + mIsNtnRoamingInHomeCountry
+                    + ", totalSatelliteModeTimeSec=" + mTotalSatelliteModeTimeSec
+                    + ", numberOfSatelliteConnections=" + mNumberOfSatelliteConnections
+                    + ", avgDurationOfSatelliteConnectionSec="
+                    + mAvgDurationOfSatelliteConnectionSec
+                    + ", satelliteConnectionGapMinSec=" + mSatelliteConnectionGapMinSec
+                    + ", satelliteConnectionGapAvgSec=" + mSatelliteConnectionGapAvgSec
+                    + ", satelliteConnectionGapMaxSec=" + mSatelliteConnectionGapMaxSec
+                    + ", rsrpAvg=" + mRsrpAvg
+                    + ", rsrpMedian=" + mRsrpMedian
+                    + ", rssnrAvg=" + mRssnrAvg
+                    + ", rssnrMedian=" + mRssnrMedian
+                    + ", countOfIncomingSms=" + mCountOfIncomingSms
+                    + ", countOfOutgoingSms=" + mCountOfOutgoingSms
+                    + ", countOfIncomingMms=" + mCountOfIncomingMms
+                    + ", countOfOutgoingMms=" + mCountOfOutgoingMms
+                    + ")";
+        }
+    }
+
+    /**
+     * A data class to contain whole component of {@link CarrierRoamingSatelliteControllerStats}
+     * atom. Refer to {@link #onCarrierRoamingSatelliteControllerStatsMetrics(
+     * CarrierRoamingSatelliteControllerStatsParams)}.
+     */
+    public class CarrierRoamingSatelliteControllerStatsParams {
+        private final int mConfigDataSource;
+        private final int mCountOfEntitlementStatusQueryRequest;
+        private final int mCountOfSatelliteConfigUpdateRequest;
+        private final int mCountOfSatelliteNotificationDisplayed;
+        private final int mSatelliteSessionGapMinSec;
+        private final int mSatelliteSessionGapAvgSec;
+        private final int mSatelliteSessionGapMaxSec;
+
+        private CarrierRoamingSatelliteControllerStatsParams(Builder builder) {
+            this.mConfigDataSource = builder.mConfigDataSource;
+            this.mCountOfEntitlementStatusQueryRequest =
+                    builder.mCountOfEntitlementStatusQueryRequest;
+            this.mCountOfSatelliteConfigUpdateRequest =
+                    builder.mCountOfSatelliteConfigUpdateRequest;
+            this.mCountOfSatelliteNotificationDisplayed =
+                    builder.mCountOfSatelliteNotificationDisplayed;
+            this.mSatelliteSessionGapMinSec = builder.mSatelliteSessionGapMinSec;
+            this.mSatelliteSessionGapAvgSec = builder.mSatelliteSessionGapAvgSec;
+            this.mSatelliteSessionGapMaxSec = builder.mSatelliteSessionGapMaxSec;
+        }
+
+        public int getConfigDataSource() {
+            return mConfigDataSource;
+        }
+
+
+        public int getCountOfEntitlementStatusQueryRequest() {
+            return mCountOfEntitlementStatusQueryRequest;
+        }
+
+        public int getCountOfSatelliteConfigUpdateRequest() {
+            return mCountOfSatelliteConfigUpdateRequest;
+        }
+
+        public int getCountOfSatelliteNotificationDisplayed() {
+            return mCountOfSatelliteNotificationDisplayed;
+        }
+
+        public int getSatelliteSessionGapMinSec() {
+            return mSatelliteSessionGapMinSec;
+        }
+
+        public int getSatelliteSessionGapAvgSec() {
+            return mSatelliteSessionGapAvgSec;
+        }
+
+        public int getSatelliteSessionGapMaxSec() {
+            return mSatelliteSessionGapMaxSec;
+        }
+
+        /**
+         * A builder class to create {@link CarrierRoamingSatelliteControllerStatsParams}
+         * data structure class
+         */
+        public static class Builder {
+            private int mConfigDataSource = SatelliteConstants.CONFIG_DATA_SOURCE_UNKNOWN;
+            private int mCountOfEntitlementStatusQueryRequest = 0;
+            private int mCountOfSatelliteConfigUpdateRequest = 0;
+            private int mCountOfSatelliteNotificationDisplayed = 0;
+            private int mSatelliteSessionGapMinSec = 0;
+            private int mSatelliteSessionGapAvgSec = 0;
+            private int mSatelliteSessionGapMaxSec = 0;
+
+            /**
+             * Sets configDataSource value of {@link CarrierRoamingSatelliteControllerStats} atom
+             * then returns Builder class
+             */
+            public Builder setConfigDataSource(int configDataSource) {
+                this.mConfigDataSource = configDataSource;
+                return this;
+            }
+
+            /**
+             * Sets countOfEntitlementStatusQueryRequest value of
+             * {@link CarrierRoamingSatelliteControllerStats} atom then returns Builder class
+             */
+            public Builder setCountOfEntitlementStatusQueryRequest(
+                    int countOfEntitlementStatusQueryRequest) {
+                this.mCountOfEntitlementStatusQueryRequest = countOfEntitlementStatusQueryRequest;
+                return this;
+            }
+
+            /**
+             * Sets countOfSatelliteConfigUpdateRequest value of
+             * {@link CarrierRoamingSatelliteControllerStats} atom then returns Builder class
+             */
+            public Builder setCountOfSatelliteConfigUpdateRequest(
+                    int countOfSatelliteConfigUpdateRequest) {
+                this.mCountOfSatelliteConfigUpdateRequest = countOfSatelliteConfigUpdateRequest;
+                return this;
+            }
+
+            /**
+             * Sets countOfSatelliteNotificationDisplayed value of
+             * {@link CarrierRoamingSatelliteControllerStats} atom then returns Builder class
+             */
+            public Builder setCountOfSatelliteNotificationDisplayed(
+                    int countOfSatelliteNotificationDisplayed) {
+                this.mCountOfSatelliteNotificationDisplayed = countOfSatelliteNotificationDisplayed;
+                return this;
+            }
+
+            /**
+             * Sets satelliteSessionGapMinSec value of
+             * {@link CarrierRoamingSatelliteControllerStats} atom then returns Builder class
+             */
+            public Builder setSatelliteSessionGapMinSec(int satelliteSessionGapMinSec) {
+                this.mSatelliteSessionGapMinSec = satelliteSessionGapMinSec;
+                return this;
+            }
+
+            /**
+             * Sets satelliteSessionGapAvgSec value of
+             * {@link CarrierRoamingSatelliteControllerStats} atom then returns Builder class
+             */
+            public Builder setSatelliteSessionGapAvgSec(int satelliteSessionGapAvgSec) {
+                this.mSatelliteSessionGapAvgSec = satelliteSessionGapAvgSec;
+                return this;
+            }
+
+            /**
+             * Sets satelliteSessionGapMaxSec value of
+             * {@link CarrierRoamingSatelliteControllerStats} atom then returns Builder class
+             */
+            public Builder setSatelliteSessionGapMaxSec(int satelliteSessionGapMaxSec) {
+                this.mSatelliteSessionGapMaxSec = satelliteSessionGapMaxSec;
+                return this;
+            }
+
+            /**
+             * Returns CarrierRoamingSatelliteControllerStatsParams, which contains whole component
+             * of {@link CarrierRoamingSatelliteControllerStats} atom
+             */
+            public CarrierRoamingSatelliteControllerStatsParams build() {
+                return new SatelliteStats()
+                        .new CarrierRoamingSatelliteControllerStatsParams(Builder.this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "CarrierRoamingSatelliteControllerStatsParams("
+                    + "configDataSource=" + mConfigDataSource
+                    + ", countOfEntitlementStatusQueryRequest="
+                    + mCountOfEntitlementStatusQueryRequest
+                    + ", countOfSatelliteConfigUpdateRequest="
+                    + mCountOfSatelliteConfigUpdateRequest
+                    + ", countOfSatelliteNotificationDisplayed="
+                    + mCountOfSatelliteNotificationDisplayed
+                    + ", satelliteSessionGapMinSec=" + mSatelliteSessionGapMinSec
+                    + ", satelliteSessionGapAvgSec=" + mSatelliteSessionGapAvgSec
+                    + ", satelliteSessionGapMaxSec=" + mSatelliteSessionGapMaxSec
+                    + ")";
+        }
+    }
+
+    /**
+     * A data class to contain whole component of {@link SatelliteEntitlement} atom.
+     * Refer to {@link #onSatelliteEntitlementMetrics(SatelliteEntitlementParams)}.
+     */
+    public class SatelliteEntitlementParams {
+        private final int mCarrierId;
+        private final int mResult;
+        private final int mEntitlementStatus;
+        private final boolean mIsRetry;
+        private final int mCount;
+
+        private SatelliteEntitlementParams(Builder builder) {
+            this.mCarrierId = builder.mCarrierId;
+            this.mResult = builder.mResult;
+            this.mEntitlementStatus = builder.mEntitlementStatus;
+            this.mIsRetry = builder.mIsRetry;
+            this.mCount = builder.mCount;
+        }
+
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
+        public int getResult() {
+            return mResult;
+        }
+
+        public int getEntitlementStatus() {
+            return mEntitlementStatus;
+        }
+
+        public boolean getIsRetry() {
+            return mIsRetry;
+        }
+
+        public int getCount() {
+            return mCount;
+        }
+
+        /**
+         * A builder class to create {@link SatelliteEntitlementParams} data structure class
+         */
+        public static class Builder {
+            private int mCarrierId = -1;
+            private int mResult = -1;
+            private int mEntitlementStatus = -1;
+            private boolean mIsRetry = false;
+            private int mCount = -1;
+
+            /**
+             * Sets carrierId value of {@link SatelliteEntitlement} atom
+             * then returns Builder class
+             */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /**
+             * Sets result value of {@link SatelliteEntitlement} atom
+             * then returns Builder class
+             */
+            public Builder setResult(int result) {
+                this.mResult = result;
+                return this;
+            }
+
+            /**
+             * Sets entitlementStatus value of {@link SatelliteEntitlement} atom
+             * then returns Builder class
+             */
+            public Builder setEntitlementStatus(int entitlementStatus) {
+                this.mEntitlementStatus = entitlementStatus;
+                return this;
+            }
+
+            /**
+             * Sets isRetry value of {@link SatelliteEntitlement} atom
+             * then returns Builder class
+             */
+            public Builder setIsRetry(boolean isRetry) {
+                this.mIsRetry = isRetry;
+                return this;
+            }
+
+            /**
+             * Sets count value of {@link SatelliteEntitlement} atom
+             * then returns Builder class
+             */
+            public Builder setCount(int count) {
+                this.mCount = count;
+                return this;
+            }
+
+            /**
+             * Returns SatelliteEntitlementParams, which contains whole component of
+             * {@link SatelliteEntitlement} atom
+             */
+            public SatelliteEntitlementParams build() {
+                return new SatelliteStats()
+                        .new SatelliteEntitlementParams(Builder.this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SatelliteEntitlementParams("
+                    + "carrierId=" + mCarrierId
+                    + ", result=" + mResult
+                    + ", entitlementStatus=" + mEntitlementStatus
+                    + ", isRetry=" + mIsRetry
+                    + ", count=" + mCount + ")";
+        }
+    }
+
+    /**
+     * A data class to contain whole component of {@link SatelliteConfigUpdater} atom.
+     * Refer to {@link #onSatelliteConfigUpdaterMetrics(SatelliteConfigUpdaterParams)}.
+     */
+    public class SatelliteConfigUpdaterParams {
+        private final int mConfigVersion;
+        private final int mOemConfigResult;
+        private final int mCarrierConfigResult;
+        private final int mCount;
+
+        private SatelliteConfigUpdaterParams(Builder builder) {
+            this.mConfigVersion = builder.mConfigVersion;
+            this.mOemConfigResult = builder.mOemConfigResult;
+            this.mCarrierConfigResult = builder.mCarrierConfigResult;
+            this.mCount = builder.mCount;
+        }
+
+        public int getConfigVersion() {
+            return mConfigVersion;
+        }
+
+        public int getOemConfigResult() {
+            return mOemConfigResult;
+        }
+
+        public int getCarrierConfigResult() {
+            return mCarrierConfigResult;
+        }
+
+        public int getCount() {
+            return mCount;
+        }
+
+        /**
+         * A builder class to create {@link SatelliteConfigUpdaterParams} data structure class
+         */
+        public static class Builder {
+            private int mConfigVersion = -1;
+            private int mOemConfigResult = -1;
+            private int mCarrierConfigResult = -1;
+            private int mCount = -1;
+
+            /**
+             * Sets configVersion value of {@link SatelliteConfigUpdater} atom
+             * then returns Builder class
+             */
+            public Builder setConfigVersion(int configVersion) {
+                this.mConfigVersion = configVersion;
+                return this;
+            }
+
+            /**
+             * Sets oemConfigResult value of {@link SatelliteConfigUpdater} atom
+             * then returns Builder class
+             */
+            public Builder setOemConfigResult(int oemConfigResult) {
+                this.mOemConfigResult = oemConfigResult;
+                return this;
+            }
+
+            /**
+             * Sets carrierConfigResult value of {@link SatelliteConfigUpdater} atom
+             * then returns Builder class
+             */
+            public Builder setCarrierConfigResult(int carrierConfigResult) {
+                this.mCarrierConfigResult = carrierConfigResult;
+                return this;
+            }
+
+            /**
+             * Sets count value of {@link SatelliteConfigUpdater} atom
+             * then returns Builder class
+             */
+            public Builder setCount(int count) {
+                this.mCount = count;
+                return this;
+            }
+
+            /**
+             * Returns SatelliteConfigUpdaterParams, which contains whole component of
+             * {@link SatelliteConfigUpdater} atom
+             */
+            public SatelliteConfigUpdaterParams build() {
+                return new SatelliteStats()
+                        .new SatelliteConfigUpdaterParams(Builder.this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SatelliteConfigUpdaterParams("
+                    + "configVersion=" + mConfigVersion
+                    + ", oemConfigResult=" + mOemConfigResult
+                    + ", carrierConfigResult=" + mCarrierConfigResult
+                    + ", count=" + mCount + ")";
+        }
+    }
+
+    /**
+     * A data class to contain whole component of {@link SatelliteAccessControllerParams} atom.
+     * Refer to {@link #onSatelliteAccessControllerMetrics(SatelliteAccessControllerParams)}.
+     */
+    public class SatelliteAccessControllerParams {
+        private final @SatelliteConstants.AccessControlType int mAccessControlType;
+        private final long mLocationQueryTimeMillis;
+        private final long mOnDeviceLookupTimeMillis;
+        private final long mTotalCheckingTimeMillis;
+        private final boolean mIsAllowed;
+        private final boolean mIsEmergency;
+        private final @SatelliteManager.SatelliteResult int mResultCode;
+        private final String[] mCountryCodes;
+        private final @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+
+        private SatelliteAccessControllerParams(Builder builder) {
+            this.mAccessControlType = builder.mAccessControlType;
+            this.mLocationQueryTimeMillis = builder.mLocationQueryTimeMillis;
+            this.mOnDeviceLookupTimeMillis = builder.mOnDeviceLookupTimeMillis;
+            this.mTotalCheckingTimeMillis = builder.mTotalCheckingTimeMillis;
+            this.mIsAllowed = builder.mIsAllowed;
+            this.mIsEmergency = builder.mIsEmergency;
+            this.mResultCode = builder.mResultCode;
+            this.mCountryCodes = builder.mCountryCodes;
+            this.mConfigDataSource = builder.mConfigDataSource;
+        }
+
+        public @SatelliteConstants.AccessControlType int getAccessControlType() {
+            return mAccessControlType;
+        }
+
+        public long getLocationQueryTime() {
+            return mLocationQueryTimeMillis;
+        }
+
+        public long getOnDeviceLookupTime() {
+            return mOnDeviceLookupTimeMillis;
+        }
+
+        public long getTotalCheckingTime() {
+            return mTotalCheckingTimeMillis;
+        }
+
+        public boolean getIsAllowed() {
+            return mIsAllowed;
+        }
+
+        public boolean getIsEmergency() {
+            return mIsEmergency;
+        }
+
+        public @SatelliteManager.SatelliteResult int getResultCode() {
+            return mResultCode;
+        }
+
+        public String[] getCountryCodes() {
+            return mCountryCodes;
+        }
+
+        public @SatelliteConstants.ConfigDataSource int getConfigDataSource() {
+            return mConfigDataSource;
+        }
+
+        /**
+         * A builder class to create {@link SatelliteAccessControllerParams} data structure class
+         */
+        public static class Builder {
+            private @SatelliteConstants.AccessControlType int mAccessControlType;
+            private long mLocationQueryTimeMillis;
+            private long mOnDeviceLookupTimeMillis;
+            private long mTotalCheckingTimeMillis;
+            private boolean mIsAllowed;
+            private boolean mIsEmergency;
+            private @SatelliteManager.SatelliteResult int mResultCode;
+            private String[] mCountryCodes;
+            private @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+
+            /**
+             * Sets AccessControlType value of {@link #SatelliteAccessController}
+             * atom then returns Builder class
+             */
+            public Builder setAccessControlType(
+                    @SatelliteConstants.AccessControlType int accessControlType) {
+                this.mAccessControlType = accessControlType;
+                return this;
+            }
+
+            /** Sets the location query time for current satellite enablement. */
+            public Builder setLocationQueryTime(long locationQueryTimeMillis) {
+                this.mLocationQueryTimeMillis = locationQueryTimeMillis;
+                return this;
+            }
+
+            /** Sets the on device lookup time for current satellite enablement. */
+            public Builder setOnDeviceLookupTime(long onDeviceLookupTimeMillis) {
+                this.mOnDeviceLookupTimeMillis = onDeviceLookupTimeMillis;
+                return this;
+            }
+
+            /** Sets the total checking time for current satellite enablement. */
+            public Builder setTotalCheckingTime(long totalCheckingTimeMillis) {
+                this.mTotalCheckingTimeMillis = totalCheckingTimeMillis;
+                return this;
+            }
+
+            /** Sets whether the satellite communication is allowed from current location. */
+            public Builder setIsAllowed(boolean isAllowed) {
+                this.mIsAllowed = isAllowed;
+                return this;
+            }
+
+            /** Sets whether the current satellite enablement is for emergency or not. */
+            public Builder setIsEmergency(boolean isEmergency) {
+                this.mIsEmergency = isEmergency;
+                return this;
+            }
+
+            /** Sets the result code for checking whether satellite service is allowed from current
+             location. */
+            public Builder setResult(int result) {
+                this.mResultCode = result;
+                return this;
+            }
+
+            /** Sets the country code for current location while attempting satellite enablement. */
+            public Builder setCountryCodes(String[] countryCodes) {
+                this.mCountryCodes = Arrays.stream(countryCodes).toArray(String[]::new);
+                return this;
+            }
+
+            /** Sets the config data source for checking whether satellite service is allowed from
+             current location. */
+            public Builder setConfigDatasource(int configDatasource) {
+                this.mConfigDataSource = configDatasource;
+                return this;
+            }
+
+            /**
+             * Returns AccessControllerParams, which contains whole component of
+             * {@link #SatelliteAccessController} atom
+             */
+            public SatelliteAccessControllerParams build() {
+                return new SatelliteStats()
+                        .new SatelliteAccessControllerParams(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "AccessControllerParams("
+                    + ", AccessControlType=" + mAccessControlType
+                    + ", LocationQueryTime=" + mLocationQueryTimeMillis
+                    + ", OnDeviceLookupTime=" + mOnDeviceLookupTimeMillis
+                    + ", TotalCheckingTime=" + mTotalCheckingTimeMillis
+                    + ", IsAllowed=" + mIsAllowed
+                    + ", IsEmergency=" + mIsEmergency
+                    + ", ResultCode=" + mResultCode
+                    + ", CountryCodes=" + Arrays.toString(mCountryCodes)
+                    + ", ConfigDataSource=" + mConfigDataSource
+                    + ")";
+        }
+    }
+
     /**  Create a new atom or update an existing atom for SatelliteController metrics */
     public synchronized void onSatelliteControllerMetrics(SatelliteControllerParams param) {
         SatelliteController proto = new SatelliteController();
@@ -1261,6 +2194,7 @@
         proto.countOfIncomingDatagramSuccess = param.getCountOfIncomingDatagramSuccess();
         proto.countOfIncomingDatagramFailed = param.getCountOfOutgoingDatagramFailed();
         proto.isDemoMode = param.getIsDemoMode();
+        proto.maxNtnSignalStrengthLevel = param.getMaxNtnSignalStrengthLevel();
         mAtomsStorage.addSatelliteSessionStats(proto);
     }
 
@@ -1311,4 +2245,78 @@
         proto.count = 1;
         mAtomsStorage.addSatelliteSosMessageRecommenderStats(proto);
     }
+
+    /**  Create a new atom for CarrierRoamingSatelliteSession metrics */
+    public synchronized  void onCarrierRoamingSatelliteSessionMetrics(
+            CarrierRoamingSatelliteSessionParams param) {
+        CarrierRoamingSatelliteSession proto = new CarrierRoamingSatelliteSession();
+        proto.carrierId = param.getCarrierId();
+        proto.isNtnRoamingInHomeCountry = param.getIsNtnRoamingInHomeCountry();
+        proto.totalSatelliteModeTimeSec = param.getTotalSatelliteModeTimeSec();
+        proto.numberOfSatelliteConnections = param.getNumberOfSatelliteConnections();
+        proto.avgDurationOfSatelliteConnectionSec = param.getAvgDurationOfSatelliteConnectionSec();
+        proto.satelliteConnectionGapMinSec = param.mSatelliteConnectionGapMinSec;
+        proto.satelliteConnectionGapAvgSec = param.mSatelliteConnectionGapAvgSec;
+        proto.satelliteConnectionGapMaxSec = param.mSatelliteConnectionGapMaxSec;
+        proto.rsrpAvg = param.mRsrpAvg;
+        proto.rsrpMedian = param.mRsrpMedian;
+        proto.rssnrAvg = param.mRssnrAvg;
+        proto.rssnrMedian = param.mRssnrMedian;
+        proto.countOfIncomingSms = param.mCountOfIncomingSms;
+        proto.countOfOutgoingSms = param.mCountOfOutgoingSms;
+        proto.countOfIncomingMms = param.mCountOfIncomingMms;
+        proto.countOfOutgoingMms = param.mCountOfOutgoingMms;
+        mAtomsStorage.addCarrierRoamingSatelliteSessionStats(proto);
+    }
+
+    /**  Create a new atom for CarrierRoamingSatelliteSession metrics */
+    public synchronized  void onCarrierRoamingSatelliteControllerStatsMetrics(
+            CarrierRoamingSatelliteControllerStatsParams param) {
+        CarrierRoamingSatelliteControllerStats proto = new CarrierRoamingSatelliteControllerStats();
+        proto.configDataSource = param.mConfigDataSource;
+        proto.countOfEntitlementStatusQueryRequest = param.mCountOfEntitlementStatusQueryRequest;
+        proto.countOfSatelliteConfigUpdateRequest = param.mCountOfSatelliteConfigUpdateRequest;
+        proto.countOfSatelliteNotificationDisplayed = param.mCountOfSatelliteNotificationDisplayed;
+        proto.satelliteSessionGapMinSec = param.mSatelliteSessionGapMinSec;
+        proto.satelliteSessionGapAvgSec = param.mSatelliteSessionGapAvgSec;
+        proto.satelliteSessionGapMaxSec = param.mSatelliteSessionGapMaxSec;
+        mAtomsStorage.addCarrierRoamingSatelliteControllerStats(proto);
+    }
+
+    /**  Create a new atom for SatelliteEntitlement metrics */
+    public synchronized  void onSatelliteEntitlementMetrics(SatelliteEntitlementParams param) {
+        SatelliteEntitlement proto = new SatelliteEntitlement();
+        proto.carrierId = param.getCarrierId();
+        proto.result = param.getResult();
+        proto.entitlementStatus = param.getEntitlementStatus();
+        proto.isRetry = param.getIsRetry();
+        proto.count = param.getCount();
+        mAtomsStorage.addSatelliteEntitlementStats(proto);
+    }
+
+    /**  Create a new atom for SatelliteConfigUpdater metrics */
+    public synchronized  void onSatelliteConfigUpdaterMetrics(SatelliteConfigUpdaterParams param) {
+        SatelliteConfigUpdater proto = new SatelliteConfigUpdater();
+        proto.configVersion = param.getConfigVersion();
+        proto.oemConfigResult = param.getOemConfigResult();
+        proto.carrierConfigResult = param.getCarrierConfigResult();
+        proto.count = param.getCount();
+        mAtomsStorage.addSatelliteConfigUpdaterStats(proto);
+    }
+
+    /**  Create a new atom or update an existing atom for SatelliteAccessController metrics */
+    public synchronized void onSatelliteAccessControllerMetrics(
+            SatelliteAccessControllerParams param) {
+        SatelliteAccessController proto = new SatelliteAccessController();
+        proto.accessControlType = param.getAccessControlType();
+        proto.locationQueryTimeMillis = param.getLocationQueryTime();
+        proto.onDeviceLookupTimeMillis = param.getOnDeviceLookupTime();
+        proto.totalCheckingTimeMillis = param.getTotalCheckingTime();
+        proto.isAllowed = param.getIsAllowed();
+        proto.isEmergency = param.getIsEmergency();
+        proto.resultCode = param.getResultCode();
+        proto.countryCodes = param.getCountryCodes();
+        proto.configDataSource = param.getConfigDataSource();
+        mAtomsStorage.addSatelliteAccessControllerStats(proto);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
index f1ad9b2..3f24968 100644
--- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -67,14 +67,12 @@
     private boolean mExistAnyConnectedInternetPdn;
     private int mCurrentDataRat =
             TelephonyStatsLog.DATA_RAT_STATE_CHANGED__DATA_RAT__DATA_RAT_UNSPECIFIED;
-    private final SatelliteController mSatelliteController;
 
     public ServiceStateStats(Phone phone) {
         super(Runnable::run);
         mPhone = phone;
         mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
         mDeviceStateHelper = PhoneFactory.getMetricsCollector().getDeviceStateHelper();
-        mSatelliteController = SatelliteController.getInstance();
     }
 
     /** Finalizes the durations of the current service state segment. */
@@ -126,6 +124,7 @@
             // Finish the duration of last service state and mark modem off
             addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now);
         } else {
+            SatelliteController satelliteController = SatelliteController.getInstance();
             CellularServiceState newState = new CellularServiceState();
             newState.voiceRat = getVoiceRat(mPhone, serviceState);
             newState.dataRat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
@@ -143,8 +142,8 @@
             newState.overrideVoiceService = mOverrideVoiceService.get();
             newState.isDataEnabled = mPhone.getDataSettingsManager().isDataEnabled();
             newState.isIwlanCrossSim = isCrossSimCallingRegistered(mPhone);
-            newState.isNtn = mSatelliteController != null
-                    ? mSatelliteController.isInSatelliteModeForCarrierRoaming(mPhone) : false;
+            newState.isNtn = satelliteController != null
+                    && satelliteController.isInSatelliteModeForCarrierRoaming(mPhone);
             TimestampedServiceState prevState =
                     mLastState.getAndSet(new TimestampedServiceState(newState, now));
             addServiceStateAndSwitch(
diff --git a/src/java/com/android/internal/telephony/metrics/SmsStats.java b/src/java/com/android/internal/telephony/metrics/SmsStats.java
index 6f9a764..b62114c 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsStats.java
@@ -61,6 +61,7 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
+import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats;
 import com.android.telephony.Rlog;
 
 import java.util.Objects;
@@ -88,8 +89,9 @@
     }
 
     /** Create a new atom when multi-part incoming SMS is dropped due to missing parts. */
-    public void onDroppedIncomingMultipartSms(boolean is3gpp2, int receivedCount, int totalCount) {
-        IncomingSms proto = getIncomingDefaultProto(is3gpp2, SOURCE_NOT_INJECTED);
+    public void onDroppedIncomingMultipartSms(boolean is3gpp2, int receivedCount, int totalCount,
+            boolean isEmergency) {
+        IncomingSms proto = getIncomingDefaultProto(is3gpp2, SOURCE_NOT_INJECTED, isEmergency);
         // Keep SMS tech as unknown because it's possible that it changed overtime and is not
         // necessarily the current one. Similarly mark the RAT as unknown.
         proto.smsTech = INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
@@ -103,21 +105,21 @@
     /** Create a new atom when an SMS for the voicemail indicator is received. */
     public void onIncomingSmsVoicemail(boolean is3gpp2,
             @InboundSmsHandler.SmsSource int smsSource) {
-        IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+        IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource, false);
         proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION;
         mAtomsStorage.addIncomingSms(proto);
     }
 
     /** Create a new atom when an SMS of type zero is received. */
     public void onIncomingSmsTypeZero(@InboundSmsHandler.SmsSource int smsSource) {
-        IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource);
+        IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource, false);
         proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO;
         mAtomsStorage.addIncomingSms(proto);
     }
 
     /** Create a new atom when an SMS-PP for the SIM card is received. */
     public void onIncomingSmsPP(@InboundSmsHandler.SmsSource int smsSource, boolean success) {
-        IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource);
+        IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource, false);
         proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP;
         proto.error = getIncomingSmsError(success);
         mAtomsStorage.addIncomingSms(proto);
@@ -126,8 +128,8 @@
     /** Create a new atom when an SMS is received successfully. */
     public void onIncomingSmsSuccess(boolean is3gpp2,
             @InboundSmsHandler.SmsSource int smsSource, int messageCount,
-            boolean blocked, long messageId) {
-        IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+            boolean blocked, long messageId, boolean isEmergency) {
+        IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource, isEmergency);
         proto.totalParts = messageCount;
         proto.receivedParts = messageCount;
         proto.blocked = blocked;
@@ -137,16 +139,16 @@
 
     /** Create a new atom when an incoming SMS has an error. */
     public void onIncomingSmsError(boolean is3gpp2,
-            @InboundSmsHandler.SmsSource int smsSource, int result) {
-        IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+            @InboundSmsHandler.SmsSource int smsSource, int result, boolean isEmergency) {
+        IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource, isEmergency);
         proto.error = getIncomingSmsError(result);
         mAtomsStorage.addIncomingSms(proto);
     }
 
     /** Create a new atom when an incoming WAP_PUSH SMS is received. */
     public void onIncomingSmsWapPush(@InboundSmsHandler.SmsSource int smsSource,
-            int messageCount, int result, long messageId) {
-        IncomingSms proto = getIncomingDefaultProto(false, smsSource);
+            int messageCount, int result, long messageId, boolean isEmergency) {
+        IncomingSms proto = getIncomingDefaultProto(false, smsSource, isEmergency);
         proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH;
         proto.totalParts = messageCount;
         proto.receivedParts = messageCount;
@@ -202,6 +204,9 @@
         proto.networkErrorCode = networkErrorCode;
 
         mAtomsStorage.addOutgoingSms(proto);
+        CarrierRoamingSatelliteSessionStats sessionStats =
+                CarrierRoamingSatelliteSessionStats.getInstance(mPhone.getSubId());
+        sessionStats.onOutgoingSms(mPhone.getSubId());
     }
 
     /** Create a new atom when user attempted to send an outgoing short code sms. */
@@ -215,7 +220,7 @@
 
     /** Creates a proto for a normal single-part {@code IncomingSms} with default values. */
     private IncomingSms getIncomingDefaultProto(boolean is3gpp2,
-            @InboundSmsHandler.SmsSource int smsSource) {
+            @InboundSmsHandler.SmsSource int smsSource, boolean isEmergency) {
         IncomingSms proto = new IncomingSms();
         proto.smsFormat = getSmsFormat(is3gpp2);
         proto.smsTech = getSmsTech(smsSource, is3gpp2);
@@ -236,6 +241,7 @@
         proto.count = 1;
         proto.isManagedProfile = mPhone.isManagedProfile();
         proto.isNtn = isNonTerrestrialNetwork();
+        proto.isEmergency = isEmergency;
         return proto;
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index c4ad93a..911424e 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -41,16 +41,19 @@
 import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.os.PersistableBundle;
 import android.os.SystemClock;
 import android.telecom.VideoProfile;
 import android.telecom.VideoProfile.VideoState;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CallComposerStatus;
 import android.telephony.data.ApnSetting;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
@@ -168,10 +171,8 @@
     private final UiccController mUiccController = UiccController.getInstance();
     private final DeviceStateHelper mDeviceStateHelper =
             PhoneFactory.getMetricsCollector().getDeviceStateHelper();
-
     private final VonrHelper mVonrHelper =
             PhoneFactory.getMetricsCollector().getVonrHelper();
-
     private final SatelliteController mSatelliteController;
 
     public VoiceCallSessionStats(int phoneId, Phone phone, @NonNull FeatureFlags featureFlags) {
@@ -574,6 +575,10 @@
             proto.vonrEnabled = mVonrHelper.getVonrEnabled(mPhone.getSubId());
         }
 
+        proto.supportsBusinessCallComposer = isBusinessCallSupported();
+        // 0 is defined as UNKNOWN in Enum
+        proto.callComposerStatus = getCallComposerStatusForPhone() + 1;
+
         proto.isNtn = mSatelliteController != null
                 ? mSatelliteController.isInSatelliteModeForCarrierRoaming(mPhone) : false;
 
@@ -947,6 +952,36 @@
         return false;
     }
 
+    private @CallComposerStatus int getCallComposerStatusForPhone() {
+        TelephonyManager telephonyManager = mPhone.getContext()
+                .getSystemService(TelephonyManager.class);
+        if (telephonyManager == null) {
+            return TelephonyManager.CALL_COMPOSER_STATUS_OFF;
+        }
+        telephonyManager = telephonyManager.createForSubscriptionId(mPhone.getSubId());
+        return telephonyManager.getCallComposerStatus();
+    }
+
+    private boolean isBusinessCallSupported() {
+        CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (carrierConfigManager == null) {
+            return false;
+        }
+        int subId = mPhone.getSubId();
+        PersistableBundle b = null;
+        try {
+            b = carrierConfigManager.getConfigForSubId(subId,
+                    CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL);
+        } catch (RuntimeException e) {
+            loge("CarrierConfigLoader is not available.");
+        }
+        if (b == null || b.isEmpty()) {
+            return false;
+        }
+        return b.getBoolean(CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL);
+    }
+
     @VisibleForTesting
     protected long getTimeMillis() {
         return SystemClock.elapsedRealtime();
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index dbb586d..ff2ee9f 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -28,10 +28,14 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.Looper;
 import android.os.SystemProperties;
+import android.telephony.DropBoxManagerLoggerBackend;
+import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.SatelliteDatagram;
@@ -40,6 +44,7 @@
 import com.android.internal.R;
 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.List;
@@ -54,6 +59,7 @@
 
     @NonNull private static DatagramController sInstance;
     @NonNull private final Context mContext;
+    @NonNull private final FeatureFlags mFeatureFlags;
     @NonNull private final PointingAppController mPointingAppController;
     @NonNull private final DatagramDispatcher mDatagramDispatcher;
     @NonNull private final DatagramReceiver mDatagramReceiver;
@@ -69,6 +75,8 @@
     public static final int TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE = 3;
     /** This type is used by CTS to override the time to datagram delay in demo mode */
     public static final int TIMEOUT_TYPE_DATAGRAM_DELAY_IN_DEMO_MODE = 4;
+    /** This type is used by CTS to override wait for device alignment in demo datagram boolean */
+    public static final int BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM = 1;
     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
     private static final boolean DEBUG = !"user".equals(Build.TYPE);
 
@@ -101,9 +109,13 @@
     private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
     private long mDatagramWaitTimeForConnectedState;
     private long mModemImageSwitchingDuration;
+    private boolean mWaitForDeviceAlignmentInDemoDatagram;
+    private long mDatagramWaitTimeForConnectedStateForLastMessage;
     @GuardedBy("mLock")
     @SatelliteManager.SatelliteModemState
     private int mSatelltieModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
+    @Nullable
+    private PersistentLogger mPersistentLogger = null;
 
     /**
      * @return The singleton instance of DatagramController.
@@ -119,14 +131,17 @@
      * Create the DatagramController singleton instance.
      * @param context The Context to use to create the DatagramController.
      * @param looper The looper for the handler.
+     * @param featureFlags The telephony feature flags.
      * @param pointingAppController PointingAppController is used to update
      *                              PointingApp about datagram transfer state changes.
      * @return The singleton instance of DatagramController.
      */
     public static DatagramController make(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull PointingAppController pointingAppController) {
         if (sInstance == null) {
-            sInstance = new DatagramController(context, looper, pointingAppController);
+            sInstance = new DatagramController(
+                    context, looper, featureFlags, pointingAppController);
         }
         return sInstance;
     }
@@ -136,26 +151,40 @@
      *
      * @param context The Context for the DatagramController.
      * @param looper The looper for the handler
+     * @param featureFlags The telephony feature flags.
      * @param pointingAppController PointingAppController is used to update PointingApp
      *                              about datagram transfer state changes.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public DatagramController(@NonNull Context context, @NonNull Looper  looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull PointingAppController pointingAppController) {
         mContext = context;
+        mFeatureFlags = featureFlags;
         mPointingAppController = pointingAppController;
 
         // Create the DatagramDispatcher singleton,
         // which is used to send satellite datagrams.
-        mDatagramDispatcher = DatagramDispatcher.make(mContext, looper, this);
+        mDatagramDispatcher = DatagramDispatcher.make(
+                mContext, looper, mFeatureFlags, this);
 
         // Create the DatagramReceiver singleton,
         // which is used to receive satellite datagrams.
-        mDatagramReceiver = DatagramReceiver.make(mContext, looper, this);
+        mDatagramReceiver = DatagramReceiver.make(
+                mContext, looper, mFeatureFlags, this);
 
         mDatagramWaitTimeForConnectedState = getDatagramWaitForConnectedStateTimeoutMillis();
         mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis();
+        mWaitForDeviceAlignmentInDemoDatagram =
+                getWaitForDeviceAlignmentInDemoDatagramFromResources();
+        mDatagramWaitTimeForConnectedStateForLastMessage =
+                getDatagramWaitForConnectedStateForLastMessageTimeoutMillis();
         mDemoModeDatagramList = new ArrayList<>();
+
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
     }
 
     /**
@@ -196,7 +225,7 @@
      * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
     public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
-        logd("pollPendingSatelliteDatagrams");
+        plogd("pollPendingSatelliteDatagrams");
         mDatagramReceiver.pollPendingSatelliteDatagrams(subId, callback);
     }
 
@@ -238,13 +267,13 @@
             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
             int sendPendingCount, int errorCode) {
         synchronized (mLock) {
-            logd("updateSendStatus"
+            plogd("updateSendStatus"
                     + " subId: " + subId
                     + " datagramType: " + datagramType
                     + " datagramTransferState: " + datagramTransferState
                     + " sendPendingCount: " + sendPendingCount + " errorCode: " + errorCode);
             if (shouldSuppressDatagramTransferStateUpdate(datagramType)) {
-                logd("Ignore the request to update send status");
+                plogd("Ignore the request to update send status");
                 return;
             }
 
@@ -286,7 +315,7 @@
             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
             int receivePendingCount, int errorCode) {
         synchronized (mLock) {
-            logd("updateReceiveStatus"
+            plogd("updateReceiveStatus"
                     + " subId: " + subId
                     + " datagramTransferState: " + datagramTransferState
                     + " receivePendingCount: " + receivePendingCount + " errorCode: " + errorCode);
@@ -402,7 +431,7 @@
             }
             setDeviceAlignedWithSatellite(false);
         }
-        logd("setDemoMode: mIsDemoMode=" + mIsDemoMode);
+        plogd("setDemoMode: mIsDemoMode=" + mIsDemoMode);
     }
 
     /** Get the last sent datagram for demo mode */
@@ -413,7 +442,7 @@
         }
 
         synchronized (mLock) {
-            logd("popDemoModeDatagram");
+            plogd("popDemoModeDatagram");
             return mDemoModeDatagramList.size() > 0 ? mDemoModeDatagramList.remove(0) : null;
         }
     }
@@ -431,7 +460,7 @@
         if (mIsDemoMode && SatelliteServiceUtils.isSosMessage(datagramType)) {
             synchronized (mLock) {
                 mDemoModeDatagramList.add(datagram);
-                logd("pushDemoModeDatagram size=" + mDemoModeDatagramList.size());
+                plogd("pushDemoModeDatagram size=" + mDemoModeDatagramList.size());
             }
         }
     }
@@ -441,13 +470,17 @@
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public long getDatagramWaitTimeForConnectedState() {
+    public long getDatagramWaitTimeForConnectedState(boolean isLastSosMessage) {
         synchronized (mLock) {
+            long timeout = isLastSosMessage ? mDatagramWaitTimeForConnectedStateForLastMessage :
+                    mDatagramWaitTimeForConnectedState;
+            logd("getDatagramWaitTimeForConnectedState: isLastSosMessage=" + isLastSosMessage
+                    + ", timeout=" + timeout + ", modemState=" + mSatelltieModemState);
             if (mSatelltieModemState == SATELLITE_MODEM_STATE_OFF
                     || mSatelltieModemState == SATELLITE_MODEM_STATE_IDLE) {
-                return (mDatagramWaitTimeForConnectedState + mModemImageSwitchingDuration);
+                return (timeout + mModemImageSwitchingDuration);
             }
-            return mDatagramWaitTimeForConnectedState;
+            return timeout;
         }
     }
 
@@ -460,11 +493,11 @@
     boolean setDatagramControllerTimeoutDuration(
             boolean reset, int timeoutType, long timeoutMillis) {
         if (!isMockModemAllowed()) {
-            loge("Updating timeout duration is not allowed");
+            ploge("Updating timeout duration is not allowed");
             return false;
         }
 
-        logd("setDatagramControllerTimeoutDuration: timeoutMillis=" + timeoutMillis
+        plogd("setDatagramControllerTimeoutDuration: timeoutMillis=" + timeoutMillis
                 + ", reset=" + reset + ", timeoutType=" + timeoutType);
         if (timeoutType == TIMEOUT_TYPE_ALIGN) {
             if (reset) {
@@ -486,7 +519,37 @@
         } else if (timeoutType == TIMEOUT_TYPE_DATAGRAM_DELAY_IN_DEMO_MODE) {
             mDatagramDispatcher.setTimeoutDatagramDelayInDemoMode(reset, timeoutMillis);
         } else {
-            loge("Invalid timeout type " + timeoutType);
+            ploge("Invalid timeout type " + timeoutType);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * This API can be used by only CTS to override the boolean configs used by the
+     * DatagramController module.
+     *
+     * @param enable Whether to enable or disable boolean config.
+     * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+     */
+    boolean setDatagramControllerBooleanConfig(
+            boolean reset, int booleanType, boolean enable) {
+        if (!isMockModemAllowed()) {
+            loge("Updating boolean config is not allowed");
+            return false;
+        }
+
+        logd("setDatagramControllerTimeoutDuration: booleanType=" + booleanType
+                + ", reset=" + reset + ", enable=" + enable);
+        if (booleanType == BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM) {
+            if (reset) {
+                mWaitForDeviceAlignmentInDemoDatagram =
+                        getWaitForDeviceAlignmentInDemoDatagramFromResources();
+            } else {
+                mWaitForDeviceAlignmentInDemoDatagram = enable;
+            }
+        } else {
+            loge("Invalid boolean type " + booleanType);
             return false;
         }
         return true;
@@ -499,7 +562,7 @@
     private void notifyDatagramTransferStateChangedToSessionController() {
         SatelliteSessionController sessionController = SatelliteSessionController.getInstance();
         if (sessionController == null) {
-            loge("notifyDatagramTransferStateChangeToSessionController: SatelliteSessionController"
+            ploge("notifyDatagramTransferStateChangeToSessionController: SatelliteSessionController"
                     + " is not initialized yet");
         } else {
             sessionController.onDatagramTransferStateChanged(
@@ -517,6 +580,11 @@
                 R.integer.config_satellite_modem_image_switching_duration_millis);
     }
 
+    private long getDatagramWaitForConnectedStateForLastMessageTimeoutMillis() {
+        return mContext.getResources().getInteger(
+                R.integer.config_datagram_wait_for_connected_state_for_last_message_timeout_millis);
+    }
+
     /**
      * This API can be used by only CTS to override the cached value for the device overlay config
      * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
@@ -537,7 +605,7 @@
                     @Override
                     public void accept(Integer result) {
                         if (result != SATELLITE_RESULT_SUCCESS) {
-                            logd("retryPollPendingDatagramsInDemoMode result: " + result);
+                            plogd("retryPollPendingDatagramsInDemoMode result: " + result);
                         }
                     }
                 };
@@ -546,6 +614,38 @@
         }
     }
 
+    /**
+     * Get whether to wait for device alignment with satellite before sending datagrams.
+     *
+     * @param isAligned if the device is aligned with satellite or not
+     * @return {@code true} if device is not aligned to satellite,
+     * and it is required to wait for alignment else {@code false}
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean waitForAligningToSatellite(boolean isAligned) {
+        if (isAligned) {
+            return false;
+        }
+
+        return getWaitForDeviceAlignmentInDemoDatagram();
+    }
+
+    private boolean getWaitForDeviceAlignmentInDemoDatagram() {
+        return mWaitForDeviceAlignmentInDemoDatagram;
+    }
+
+    private boolean getWaitForDeviceAlignmentInDemoDatagramFromResources() {
+        boolean waitForDeviceAlignmentInDemoDatagram = false;
+        try {
+            waitForDeviceAlignmentInDemoDatagram = mContext.getResources().getBoolean(
+                    R.bool.config_wait_for_device_alignment_in_demo_datagram);
+        } catch (Resources.NotFoundException ex) {
+            loge("getWaitForDeviceAlignmentInDemoDatagram: ex=" + ex);
+        }
+
+        return waitForDeviceAlignmentInDemoDatagram;
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
@@ -553,4 +653,31 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index a5b3ca6..2c9463f 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -32,6 +32,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.telephony.DropBoxManagerLoggerBackend;
+import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.SatelliteDatagram;
@@ -41,6 +43,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.SatelliteStats;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
@@ -72,6 +75,7 @@
     @NonNull private final DatagramController mDatagramController;
     @NonNull private final ControllerMetricsStats mControllerMetricsStats;
     @NonNull private final SessionMetricsStats mSessionMetricsStats;
+    @NonNull private final FeatureFlags mFeatureFlags;
 
     private boolean mIsDemoMode = false;
     private boolean mIsAligned = false;
@@ -104,20 +108,34 @@
             mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>();
 
     private long mWaitTimeForDatagramSendingResponse;
+    private long mWaitTimeForDatagramSendingForLastMessageResponse;
     @SatelliteManager.DatagramType
     private int mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN;
+    @Nullable private PersistentLogger mPersistentLogger = null;
 
     /**
      * Create the DatagramDispatcher singleton instance.
      * @param context The Context to use to create the DatagramDispatcher.
      * @param looper The looper for the handler.
+     * @param featureFlags The telephony feature flags.
      * @param datagramController DatagramController which is used to update datagram transfer state.
      * @return The singleton instance of DatagramDispatcher.
      */
     public static DatagramDispatcher make(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull DatagramController datagramController) {
         if (sInstance == null) {
-            sInstance = new DatagramDispatcher(context, looper, datagramController);
+            sInstance = new DatagramDispatcher(context, looper, featureFlags, datagramController);
+        }
+        return sInstance;
+    }
+
+    /**
+     * @return The singleton instance of DatagramDispatcher.
+     */
+    public static DatagramDispatcher getInstance() {
+        if (sInstance == null) {
+            loge("DatagramDispatcher was not yet initialized.");
         }
         return sInstance;
     }
@@ -127,21 +145,30 @@
      *
      * @param context The Context for the DatagramDispatcher.
      * @param looper The looper for the handler.
+     * @param featureFlags The telephony feature flags.
      * @param datagramController DatagramController which is used to update datagram transfer state.
      */
     @VisibleForTesting
     protected DatagramDispatcher(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull DatagramController datagramController) {
         super(looper);
         mContext = context;
+        mFeatureFlags = featureFlags;
         mDatagramController = datagramController;
         mControllerMetricsStats = ControllerMetricsStats.getInstance();
         mSessionMetricsStats = SessionMetricsStats.getInstance();
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
 
         synchronized (mLock) {
             mSendingDatagramInProgress = false;
         }
         mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis();
+        mWaitTimeForDatagramSendingForLastMessageResponse =
+                getWaitForDatagramSendingResponseForLastMessageTimeoutMillis();
     }
 
     private static final class DatagramDispatcherHandlerRequest {
@@ -206,7 +233,7 @@
 
         switch(msg.what) {
             case CMD_SEND_SATELLITE_DATAGRAM: {
-                logd("CMD_SEND_SATELLITE_DATAGRAM mIsDemoMode=" + mIsDemoMode
+                plogd("CMD_SEND_SATELLITE_DATAGRAM mIsDemoMode=" + mIsDemoMode
                         + ", shouldSendDatagramToModemInDemoMode="
                         + shouldSendDatagramToModemInDemoMode());
                 request = (DatagramDispatcherHandlerRequest) msg.obj;
@@ -238,14 +265,15 @@
                 synchronized (mLock) {
                     if (mIsDemoMode && (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)) {
                         if (argument.skipCheckingSatelliteAligned) {
-                            logd("Satellite was already aligned. No need to check alignment again");
-                        } else if (!mIsAligned) {
-                            logd("Satellite is not aligned in demo mode, wait for the alignment.");
+                            plogd("Satellite was already aligned. "
+                                + "No need to check alignment again");
+                        } else if (mDatagramController.waitForAligningToSatellite(mIsAligned)) {
+                            plogd("Satellite is not aligned in demo mode, wait for the alignment.");
                             startSatelliteAlignedTimer(request);
                             break;
                         }
                     }
-                    logd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error
+                    plogd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error
                             + ", mIsDemoMode=" + mIsDemoMode);
 
                     /*
@@ -256,7 +284,7 @@
                      * 3) All pending send requests have been aborted due to some error.
                      */
                     if (!shouldProcessEventSendSatelliteDatagramDone(argument)) {
-                        logw("The message " + argument.datagramId + " was already processed");
+                        plogw("The message " + argument.datagramId + " was already processed");
                         break;
                     }
 
@@ -272,45 +300,31 @@
                         mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
                     }
 
-                    if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                    if (error == SATELLITE_RESULT_SUCCESS) {
                         // Update send status for current datagram
                         mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
                                 getPendingDatagramCount(), error);
-                        mControllerMetricsStats.reportOutgoingDatagramSuccessCount(
-                                argument.datagramType, mIsDemoMode);
-                        mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram();
                         startWaitForSimulatedPollDatagramsDelayTimer(request);
-                        if (getPendingDatagramCount() > 0) {
-                            // Send response for current datagram
-                            argument.callback.accept(error);
-                            // Send pending datagrams
-                            sendPendingDatagrams();
-                        } else {
-                            mDatagramController.updateSendStatus(argument.subId,
-                                    argument.datagramType,
-                                    SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0,
-                                    SatelliteManager.SATELLITE_RESULT_SUCCESS);
-                            // Send response for current datagram
-                            argument.callback.accept(error);
-                        }
                     } else {
                         // Update send status
                         mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
                                 getPendingDatagramCount(), error);
-                        mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
-                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                                0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                    }
+
+                    if (getPendingDatagramCount() > 0) {
                         // Send response for current datagram
-                        // after updating datagram transfer state internally.
                         argument.callback.accept(error);
-                        // Abort sending all the pending datagrams
-                        mControllerMetricsStats.reportOutgoingDatagramFailCount(
-                                argument.datagramType, mIsDemoMode);
-                        mSessionMetricsStats.addCountOfFailedOutgoingDatagram();
-                        abortSendingPendingDatagrams(argument.subId,
-                                SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+                        // Send pending datagrams
+                        sendPendingDatagrams();
+                    } else {
+                        mDatagramController.updateSendStatus(argument.subId,
+                                argument.datagramType,
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0,
+                                SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                        // Send response for current datagram
+                        argument.callback.accept(error);
                     }
                 }
                 break;
@@ -338,7 +352,7 @@
                 break;
 
             default:
-                logw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
+                plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
                 break;
         }
     }
@@ -380,7 +394,7 @@
             }
 
             if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) {
-                logd("sendDatagram: wait for satellite connected");
+                plogd("sendDatagram: wait for satellite connected");
                 mDatagramController.updateSendStatus(subId, datagramType,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
                         getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
@@ -395,7 +409,7 @@
                         getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
             } else {
-                logd("sendDatagram: mSendingDatagramInProgress="
+                plogd("sendDatagram: mSendingDatagramInProgress="
                         + mSendingDatagramInProgress + ", isPollingInIdleState="
                         + mDatagramController.isPollingInIdleState());
             }
@@ -415,7 +429,7 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected void setDemoMode(boolean isDemoMode) {
         mIsDemoMode = isDemoMode;
-        logd("setDemoMode: mIsDemoMode=" + mIsDemoMode);
+        plogd("setDemoMode: mIsDemoMode=" + mIsDemoMode);
     }
 
     /**
@@ -425,14 +439,14 @@
     public void setDeviceAlignedWithSatellite(boolean isAligned) {
         synchronized (mLock) {
             mIsAligned = isAligned;
-            logd("setDeviceAlignedWithSatellite: " + mIsAligned);
+            plogd("setDeviceAlignedWithSatellite: " + mIsAligned);
             if (isAligned && mIsDemoMode) handleEventSatelliteAligned();
         }
     }
 
     private void startSatelliteAlignedTimer(@NonNull DatagramDispatcherHandlerRequest request) {
         if (isSatelliteAlignedTimerStarted()) {
-            logd("Satellite aligned timer was already started");
+            plogd("Satellite aligned timer was already started");
             return;
         }
         mSendSatelliteDatagramRequest = request;
@@ -451,7 +465,7 @@
             stopSatelliteAlignedTimer();
 
             if (mSendSatelliteDatagramRequest == null) {
-                loge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null");
+                ploge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null");
             } else {
                 SendSatelliteDatagramArgument argument =
                         (SendSatelliteDatagramArgument) mSendSatelliteDatagramRequest.argument;
@@ -461,14 +475,14 @@
                 mSendSatelliteDatagramRequest = null;
                 AsyncResult.forMessage(message, null, null);
                 message.sendToTarget();
-                logd("handleEventSatelliteAligned: EVENT_SEND_SATELLITE_DATAGRAM_DONE");
+                plogd("handleEventSatelliteAligned: EVENT_SEND_SATELLITE_DATAGRAM_DONE");
             }
         }
     }
 
     private void handleEventSatelliteAlignedTimeout(
             @NonNull DatagramDispatcherHandlerRequest request) {
-        logd("handleEventSatelliteAlignedTimeout");
+        plogd("handleEventSatelliteAlignedTimeout");
         mSendSatelliteDatagramRequest = null;
         SatelliteManager.SatelliteException exception =
                 new SatelliteManager.SatelliteException(
@@ -492,15 +506,15 @@
      */
     @GuardedBy("mLock")
     private void sendPendingDatagrams() {
-        logd("sendPendingDatagrams()");
+        plogd("sendPendingDatagrams()");
         if (!mDatagramController.isPollingInIdleState()) {
             // Datagram should be sent to satellite modem when modem is free.
-            logd("sendPendingDatagrams: modem is receiving datagrams");
+            plogd("sendPendingDatagrams: modem is receiving datagrams");
             return;
         }
 
         if (getPendingDatagramCount() <= 0) {
-            logd("sendPendingDatagrams: no pending datagrams to send");
+            plogd("sendPendingDatagrams: no pending datagrams to send");
             return;
         }
 
@@ -516,7 +530,7 @@
             SendSatelliteDatagramArgument datagramArg =
                     pendingDatagram.iterator().next().getValue();
             if (mDatagramController.needsWaitingForSatelliteConnected(datagramArg.datagramType)) {
-                logd("sendPendingDatagrams: wait for satellite connected");
+                plogd("sendPendingDatagrams: wait for satellite connected");
                 return;
             }
 
@@ -543,15 +557,13 @@
         if (pendingDatagramsMap.size() == 0) {
             return;
         }
-        loge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources");
+        ploge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources");
 
         // Send error code to all the pending datagrams
         for (Entry<Long, SendSatelliteDatagramArgument> entry :
                 pendingDatagramsMap.entrySet()) {
             SendSatelliteDatagramArgument argument = entry.getValue();
             reportSendDatagramCompleted(argument, errorCode);
-            mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType,
-                    mIsDemoMode);
             argument.callback.accept(errorCode);
         }
 
@@ -568,7 +580,7 @@
     @GuardedBy("mLock")
     private void abortSendingPendingDatagrams(int subId,
             @SatelliteManager.SatelliteResult int errorCode) {
-        logd("abortSendingPendingDatagrams()");
+        plogd("abortSendingPendingDatagrams()");
         sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode);
         sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode);
     }
@@ -583,6 +595,22 @@
         }
     }
 
+    /** Return pending user messages count */
+    public int getPendingUserMessagesCount() {
+        synchronized (mLock) {
+            int pendingUserMessagesCount = 0;
+            for (Entry<Long, SendSatelliteDatagramArgument> entry :
+                    mPendingNonEmergencyDatagramsMap.entrySet()) {
+                SendSatelliteDatagramArgument argument = entry.getValue();
+                if (argument.datagramType != SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+                    pendingUserMessagesCount += 1;
+                }
+            }
+            pendingUserMessagesCount += mPendingEmergencyDatagramsMap.size();
+            return pendingUserMessagesCount;
+        }
+    }
+
     /**
      * Posts the specified command to be executed on the main thread and returns immediately.
      *
@@ -610,6 +638,16 @@
                                 ? (System.currentTimeMillis() - argument.datagramStartTime) : 0)
                         .setIsDemoMode(mIsDemoMode)
                         .build());
+        if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+            mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType,
+                    mIsDemoMode);
+            mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType);
+        } else {
+            mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType,
+                    mIsDemoMode);
+            mSessionMetricsStats.addCountOfFailedOutgoingDatagram(argument.datagramType,
+                    resultCode);
+        }
     }
 
     /**
@@ -630,7 +668,7 @@
         synchronized (mLock) {
             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
                     || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
-                logd("onSatelliteModemStateChanged: cleaning up resources");
+                plogd("onSatelliteModemStateChanged: cleaning up resources");
                 cleanUpResources();
             } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) {
                 sendPendingDatagrams();
@@ -646,7 +684,7 @@
 
     @GuardedBy("mLock")
     private void cleanUpResources() {
-        logd("cleanUpResources");
+        plogd("cleanUpResources");
         mSendingDatagramInProgress = false;
         if (getPendingDatagramCount() > 0) {
             mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
@@ -674,12 +712,13 @@
     private void startDatagramWaitForConnectedStateTimer(
             @NonNull SendSatelliteDatagramArgument datagramArgs) {
         if (isDatagramWaitForConnectedStateTimerStarted()) {
-            logd("DatagramWaitForConnectedStateTimer is already started");
+            plogd("DatagramWaitForConnectedStateTimer is already started");
             return;
         }
         sendMessageDelayed(obtainMessage(
                         EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT, datagramArgs),
-                mDatagramController.getDatagramWaitTimeForConnectedState());
+                mDatagramController.getDatagramWaitTimeForConnectedState(
+                        SatelliteServiceUtils.isLastSosMessage(datagramArgs.datagramType)));
     }
 
     private void stopDatagramWaitForConnectedStateTimer() {
@@ -705,12 +744,16 @@
     private void startWaitForDatagramSendingResponseTimer(
             @NonNull SendSatelliteDatagramArgument argument) {
         if (hasMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT)) {
-            logd("WaitForDatagramSendingResponseTimer was already started");
+            plogd("WaitForDatagramSendingResponseTimer was already started");
             return;
         }
+        long waitTime = SatelliteServiceUtils.isLastSosMessage(argument.datagramType)
+                ? mWaitTimeForDatagramSendingForLastMessageResponse
+                : mWaitTimeForDatagramSendingResponse;
+        logd("startWaitForDatagramSendingResponseTimer: datagramType=" + argument.datagramType
+                + ", waitTime=" + waitTime);
         sendMessageDelayed(obtainMessage(
-                EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT, argument),
-                mWaitTimeForDatagramSendingResponse);
+                EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT, argument), waitTime);
     }
 
     private void stopWaitForDatagramSendingResponseTimer() {
@@ -719,7 +762,7 @@
 
     private void handleEventDatagramWaitForConnectedStateTimedOut(
             @NonNull SendSatelliteDatagramArgument argument) {
-        logw("Timed out to wait for satellite connected before sending datagrams");
+        plogw("Timed out to wait for satellite connected before sending datagrams");
         synchronized (mLock) {
             // Update send status
             mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
@@ -733,9 +776,6 @@
                     0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
             abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                     SATELLITE_RESULT_NOT_REACHABLE);
-            mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType,
-                    mIsDemoMode);
-            mSessionMetricsStats.addCountOfFailedOutgoingDatagram();
         }
     }
 
@@ -751,7 +791,7 @@
             return mShouldSendDatagramToModemInDemoMode.get();
 
         } catch (Resources.NotFoundException ex) {
-            loge("shouldSendDatagramToModemInDemoMode: id= "
+            ploge("shouldSendDatagramToModemInDemoMode: id= "
                     + R.bool.config_send_satellite_datagram_to_modem_in_demo_mode + ", ex=" + ex);
             return false;
         }
@@ -762,6 +802,11 @@
                 R.integer.config_wait_for_datagram_sending_response_timeout_millis);
     }
 
+    private long getWaitForDatagramSendingResponseForLastMessageTimeoutMillis() {
+        return mContext.getResources().getInteger(R.integer
+                .config_wait_for_datagram_sending_response_for_last_message_timeout_millis);
+    }
+
     private boolean shouldProcessEventSendSatelliteDatagramDone(
             @NonNull SendSatelliteDatagramArgument argument) {
         synchronized (mLock) {
@@ -776,7 +821,7 @@
     private void handleEventWaitForDatagramSendingResponseTimedOut(
             @NonNull SendSatelliteDatagramArgument argument) {
         synchronized (mLock) {
-            logw("Timed out to wait for the response of the request to send the datagram "
+            plogw("Timed out to wait for the response of the request to send the datagram "
                     + argument.datagramId);
 
             // Ask vendor service to abort all datagram-sending requests
@@ -798,8 +843,6 @@
 
             // Log metrics about the outgoing datagram
             reportSendDatagramCompleted(argument, SATELLITE_RESULT_MODEM_TIMEOUT);
-            mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType,
-                    mIsDemoMode);
             // Remove current datagram from pending map.
             if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) {
                 mPendingEmergencyDatagramsMap.remove(argument.datagramId);
@@ -808,8 +851,7 @@
             }
 
             // Abort sending all the pending datagrams
-            abortSendingPendingDatagrams(argument.subId,
-                    SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+            abortSendingPendingDatagrams(argument.subId, SATELLITE_RESULT_MODEM_TIMEOUT);
         }
     }
 
@@ -824,7 +866,7 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected void setShouldSendDatagramToModemInDemoMode(
             @Nullable Boolean shouldSendToModemInDemoMode) {
-        logd("setShouldSendDatagramToModemInDemoMode(" + (shouldSendToModemInDemoMode == null
+        plogd("setShouldSendDatagramToModemInDemoMode(" + (shouldSendToModemInDemoMode == null
                 ? "null" : shouldSendToModemInDemoMode) + ")");
 
         if (shouldSendToModemInDemoMode == null) {
@@ -842,12 +884,12 @@
     private void startWaitForSimulatedPollDatagramsDelayTimer(
             @NonNull DatagramDispatcherHandlerRequest request) {
         if (mIsDemoMode) {
-            logd("startWaitForSimulatedPollDatagramsDelayTimer");
+            plogd("startWaitForSimulatedPollDatagramsDelayTimer");
             sendMessageDelayed(
                     obtainMessage(EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT, request),
                     getDemoTimeoutDuration());
         } else {
-            logd("Should not start WaitForSimulatedPollDatagramsDelayTimer in non-demo mode");
+            plogd("Should not start WaitForSimulatedPollDatagramsDelayTimer in non-demo mode");
         }
     }
 
@@ -858,17 +900,17 @@
     private void handleEventWaitForSimulatedPollDatagramsDelayTimedOut(
             @NonNull SendSatelliteDatagramArgument argument) {
         if (mIsDemoMode) {
-            logd("handleEventWaitForSimulatedPollDatagramsDelayTimedOut");
+            plogd("handleEventWaitForSimulatedPollDatagramsDelayTimedOut");
             mDatagramController.pushDemoModeDatagram(argument.datagramType, argument.datagram);
             Consumer<Integer> internalCallback = new Consumer<Integer>() {
                 @Override
                 public void accept(Integer result) {
-                    logd("pollPendingSatelliteDatagrams result: " + result);
+                    plogd("pollPendingSatelliteDatagrams result: " + result);
                 }
             };
             mDatagramController.pollPendingSatelliteDatagrams(argument.subId, internalCallback);
         } else {
-            logd("Unexpected EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT in "
+            plogd("Unexpected EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT in "
                     + "non-demo mode");
         }
     }
@@ -889,7 +931,7 @@
         } else {
             mDemoTimeoutDuration = timeoutMillis;
         }
-        logd("setTimeoutDatagramDelayInDemoMode " + mDemoTimeoutDuration + " reset=" + reset);
+        plogd("setTimeoutDatagramDelayInDemoMode " + mDemoTimeoutDuration + " reset=" + reset);
     }
 
     private static void logd(@NonNull String log) {
@@ -901,4 +943,38 @@
     }
 
     private static void logw(@NonNull String log) { Rlog.w(TAG, log); }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void plogw(@NonNull String log) {
+        Rlog.w(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.warn(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(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 145b017..ea75f03 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -37,6 +37,8 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Telephony;
+import android.telephony.DropBoxManagerLoggerBackend;
+import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteDatagramCallback;
@@ -50,6 +52,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.IVoidConsumer;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.SatelliteStats;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
@@ -83,6 +86,7 @@
     @NonNull private final ControllerMetricsStats mControllerMetricsStats;
     @NonNull private final SessionMetricsStats mSessionMetricsStats;
     @NonNull private final Looper mLooper;
+    @NonNull private final FeatureFlags mFeatureFlags;
 
     private long mDatagramTransferStartTime = 0;
     private boolean mIsDemoMode = false;
@@ -93,6 +97,8 @@
     @Nullable
     private DatagramReceiverHandlerRequest mPendingPollSatelliteDatagramsRequest = null;
     private final Object mLock = new Object();
+    @Nullable
+    private PersistentLogger mPersistentLogger = null;
 
     /**
      * Map key: subId, value: SatelliteDatagramListenerHandler to notify registrants.
@@ -112,13 +118,15 @@
      * Create the DatagramReceiver singleton instance.
      * @param context The Context to use to create the DatagramReceiver.
      * @param looper The looper for the handler.
+     * @param featureFlags The telephony feature flags.
      * @param datagramController DatagramController which is used to update datagram transfer state.
      * @return The singleton instance of DatagramReceiver.
      */
     public static DatagramReceiver make(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull DatagramController datagramController) {
         if (sInstance == null) {
-            sInstance = new DatagramReceiver(context, looper, datagramController);
+            sInstance = new DatagramReceiver(context, looper, featureFlags, datagramController);
         }
         return sInstance;
     }
@@ -129,25 +137,31 @@
      *
      * @param context The Context for the DatagramReceiver.
      * @param looper The looper for the handler.
+     * @param featureFlags The telephony feature flags.
      * @param datagramController DatagramController which is used to update datagram transfer state.
      */
     @VisibleForTesting
     protected DatagramReceiver(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull DatagramController datagramController) {
         super(looper);
         mContext = context;
         mLooper = looper;
+        mFeatureFlags = featureFlags;
         mContentResolver = context.getContentResolver();
         mDatagramController = datagramController;
         mControllerMetricsStats = ControllerMetricsStats.getInstance();
         mSessionMetricsStats = SessionMetricsStats.getInstance();
-
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
         try {
             mSharedPreferences =
                     mContext.getSharedPreferences(SatelliteController.SATELLITE_SHARED_PREF,
                             Context.MODE_PRIVATE);
         } catch (Exception e) {
-            loge("Cannot get default shared preferences: " + e);
+            ploge("Cannot get default shared preferences: " + e);
         }
     }
 
@@ -457,7 +471,7 @@
                     }
                 }
 
-                logd("EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE error: " + error);
+                plogd("EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE error: " + error);
                 if (error != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     mDatagramController.updateReceiveStatus(request.subId,
                             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
@@ -485,7 +499,7 @@
                 break;
 
             default:
-                logw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
+                plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
                 break;
         }
     }
@@ -558,7 +572,7 @@
     public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
         if (!mDatagramController.isPollingInIdleState()) {
             // Poll request should be sent to satellite modem only when it is free.
-            logd("pollPendingSatelliteDatagrams: satellite modem is busy receiving datagrams.");
+            plogd("pollPendingSatelliteDatagrams: satellite modem is busy receiving datagrams.");
             callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
             return;
         }
@@ -570,7 +584,7 @@
             if (isDatagramWaitForConnectedStateTimerStarted()) {
                 stopDatagramWaitForConnectedStateTimer();
                 if (mPendingPollSatelliteDatagramsRequest == null) {
-                    loge("handleSatelliteConnectedEvent: mPendingPollSatelliteDatagramsRequest is"
+                    ploge("handleSatelliteConnectedEvent: mPendingPollSatelliteDatagramsRequest is"
                             + " null");
                     return;
                 }
@@ -588,7 +602,7 @@
             @NonNull Consumer<Integer> callback) {
         if (!mDatagramController.isSendingInIdleState()) {
             // Poll request should be sent to satellite modem only when it is free.
-            logd("pollPendingSatelliteDatagramsInternal: satellite modem is busy sending "
+            plogd("pollPendingSatelliteDatagramsInternal: satellite modem is busy sending "
                     + "datagrams.");
             callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
             return;
@@ -596,7 +610,7 @@
 
         if (mDatagramController.needsWaitingForSatelliteConnected(
                 SatelliteManager.DATAGRAM_TYPE_UNKNOWN)) {
-            logd("pollPendingSatelliteDatagramsInternal: wait for satellite connected");
+            plogd("pollPendingSatelliteDatagramsInternal: wait for satellite connected");
             synchronized (mLock) {
                 mPendingPollSatelliteDatagramsRequest = new DatagramReceiverHandlerRequest(
                         callback, SatelliteServiceUtils.getPhone(), subId);
@@ -620,7 +634,7 @@
             DatagramReceiverHandlerRequest request = new DatagramReceiverHandlerRequest(
                     callback, phone, subId);
             synchronized (mLock) {
-                if (mIsAligned) {
+                if (!mDatagramController.waitForAligningToSatellite(mIsAligned)) {
                     Message msg = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
                             request);
                     AsyncResult.forMessage(msg, null, null);
@@ -644,7 +658,7 @@
         synchronized (mLock) {
             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
                     || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
-                logd("onSatelliteModemStateChanged: cleaning up resources");
+                plogd("onSatelliteModemStateChanged: cleaning up resources");
                 cleanUpResources();
             } else if (state == SATELLITE_MODEM_STATE_CONNECTED) {
                 handleSatelliteConnectedEvent();
@@ -657,7 +671,7 @@
         if (isSatelliteAlignedTimerStarted()) {
             stopSatelliteAlignedTimer();
             if (mDemoPollPendingSatelliteDatagramsRequest == null) {
-                loge("Satellite aligned timer was started "
+                ploge("Satellite aligned timer was started "
                         + "but mDemoPollPendingSatelliteDatagramsRequest is null");
             } else {
                 Consumer<Integer> callback =
@@ -755,14 +769,14 @@
     public void setDeviceAlignedWithSatellite(boolean isAligned) {
         synchronized (mLock) {
             mIsAligned = isAligned;
-            logd("setDeviceAlignedWithSatellite: " + mIsAligned);
+            plogd("setDeviceAlignedWithSatellite: " + mIsAligned);
             if (isAligned && mIsDemoMode) handleEventSatelliteAligned();
         }
     }
 
     private void startSatelliteAlignedTimer(DatagramReceiverHandlerRequest request) {
         if (isSatelliteAlignedTimerStarted()) {
-            logd("Satellite aligned timer was already started");
+            plogd("Satellite aligned timer was already started");
             return;
         }
         mDemoPollPendingSatelliteDatagramsRequest = request;
@@ -781,7 +795,7 @@
             stopSatelliteAlignedTimer();
 
             if (mDemoPollPendingSatelliteDatagramsRequest == null) {
-                loge("handleSatelliteAlignedTimer: mDemoPollPendingSatelliteDatagramsRequest "
+                ploge("handleSatelliteAlignedTimer: mDemoPollPendingSatelliteDatagramsRequest "
                         + "is null");
             } else {
                 Message message = obtainMessage(
@@ -813,12 +827,12 @@
 
     private void startDatagramWaitForConnectedStateTimer() {
         if (isDatagramWaitForConnectedStateTimerStarted()) {
-            logd("DatagramWaitForConnectedStateTimer is already started");
+            plogd("DatagramWaitForConnectedStateTimer is already started");
             return;
         }
         sendMessageDelayed(obtainMessage(
                         EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT),
-                mDatagramController.getDatagramWaitTimeForConnectedState());
+                mDatagramController.getDatagramWaitTimeForConnectedState(false));
     }
 
     private void stopDatagramWaitForConnectedStateTimer() {
@@ -833,12 +847,12 @@
     private void handleEventDatagramWaitForConnectedStateTimedOut() {
         synchronized (mLock) {
             if (mPendingPollSatelliteDatagramsRequest == null) {
-                logw("handleEventDatagramWaitForConnectedStateTimedOut: "
+                plogw("handleEventDatagramWaitForConnectedStateTimedOut: "
                         + "mPendingPollSatelliteDatagramsRequest is null");
                 return;
             }
 
-            logw("Timed out to wait for satellite connected before polling datagrams");
+            plogw("Timed out to wait for satellite connected before polling datagrams");
             mDatagramController.updateReceiveStatus(mPendingPollSatelliteDatagramsRequest.subId,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
                     mDatagramController.getReceivePendingCount(),
@@ -877,4 +891,38 @@
     private static void logw(@NonNull String log) {
         Rlog.w(TAG, log);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void plogw(@NonNull String log) {
+        Rlog.w(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.warn(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
new file mode 100644
index 0000000..3c31ae8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
@@ -0,0 +1,329 @@
+/*
+ * 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.satellite;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.IIntegerConsumer;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NtnSignalStrength;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.telephony.satellite.stub.SatelliteResult;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+public class DemoSimulator extends StateMachine {
+    private static final String TAG = "DemoSimulator";
+    private static final boolean DBG = true;
+
+    private static final int EVENT_SATELLITE_MODE_ON = 1;
+    private static final int EVENT_SATELLITE_MODE_OFF = 2;
+    private static final int EVENT_DEVICE_ALIGNED_WITH_SATELLITE = 3;
+    protected static final int EVENT_DEVICE_ALIGNED = 4;
+    protected static final int EVENT_DEVICE_NOT_ALIGNED = 5;
+
+    @NonNull private static DemoSimulator sInstance;
+
+    @NonNull private final Context mContext;
+    @NonNull private final SatelliteController mSatelliteController;
+    @NonNull private final PowerOffState mPowerOffState = new PowerOffState();
+    @NonNull private final NotConnectedState mNotConnectedState = new NotConnectedState();
+    @NonNull private final ConnectedState mConnectedState = new ConnectedState();
+    @NonNull private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private boolean mIsAligned = false;
+    private ISatelliteListener mISatelliteListener;
+
+    /**
+     * @return The singleton instance of DemoSimulator.
+     */
+    public static DemoSimulator getInstance() {
+        if (sInstance == null) {
+            Log.e(TAG, "DemoSimulator was not yet initialized.");
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create the DemoSimulator singleton instance.
+     *
+     * @param context The Context for the DemoSimulator.
+     * @return The singleton instance of DemoSimulator.
+     */
+    public static DemoSimulator make(@NonNull Context context,
+            @NonNull SatelliteController satelliteController) {
+        if (sInstance == null) {
+            sInstance = new DemoSimulator(context, Looper.getMainLooper(), satelliteController);
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create a DemoSimulator.
+     *
+     * @param context The Context for the DemoSimulator.
+     * @param looper The looper associated with the handler of this class.
+     */
+    protected DemoSimulator(@NonNull Context context, @NonNull Looper looper,
+            @NonNull SatelliteController satelliteController) {
+        super(TAG, looper);
+
+        mContext = context;
+        mSatelliteController = satelliteController;
+        addState(mPowerOffState);
+        addState(mNotConnectedState);
+        addState(mConnectedState);
+        setInitialState(mPowerOffState);
+        start();
+    }
+
+    private class PowerOffState extends State {
+        @Override
+        public void enter() {
+            logd("Entering PowerOffState");
+        }
+
+        @Override
+        public void exit() {
+            logd("Exiting PowerOffState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("PowerOffState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODE_ON:
+                    transitionTo(mNotConnectedState);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+    }
+
+    private class NotConnectedState extends State {
+        @Override
+        public void enter() {
+            logd("Entering NotConnectedState");
+
+            try {
+                NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
+                ntnSignalStrength.signalStrengthLevel = 0;
+                mISatelliteListener.onSatelliteModemStateChanged(
+                        SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+                mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
+
+                synchronized (mLock) {
+                    if (mIsAligned) {
+                        handleEventDeviceAlignedWithSatellite(true);
+                    }
+                }
+            } catch (RemoteException e) {
+                loge("NotConnectedState: RemoteException " + e);
+            }
+        }
+
+        @Override
+        public void exit() {
+            logd("Exiting NotConnectedState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("NotConnectedState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODE_OFF:
+                    transitionTo(mPowerOffState);
+                    break;
+                case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+                    handleEventDeviceAlignedWithSatellite((boolean) msg.obj);
+                    break;
+                case EVENT_DEVICE_ALIGNED:
+                    transitionTo(mConnectedState);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+
+        private void handleEventDeviceAlignedWithSatellite(boolean isAligned) {
+            if (isAligned && !hasMessages(EVENT_DEVICE_ALIGNED)) {
+                long durationMillis = mSatelliteController.getDemoPointingAlignedDurationMillis();
+                logd("NotConnectedState: handleEventAlignedWithSatellite isAligned=true."
+                        + " Send delayed EVENT_DEVICE_ALIGNED message in"
+                        + " durationMillis=" + durationMillis);
+                sendMessageDelayed(EVENT_DEVICE_ALIGNED, durationMillis);
+            } else if (!isAligned && hasMessages(EVENT_DEVICE_ALIGNED)) {
+                logd("NotConnectedState: handleEventAlignedWithSatellite isAligned=false."
+                        + " Remove EVENT_DEVICE_ALIGNED message.");
+                removeMessages(EVENT_DEVICE_ALIGNED);
+            }
+        }
+    }
+
+    private class ConnectedState extends State {
+        @Override
+        public void enter() {
+            logd("Entering ConnectedState");
+
+            try {
+                NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
+                ntnSignalStrength.signalStrengthLevel = 2;
+                mISatelliteListener.onSatelliteModemStateChanged(
+                        SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+                mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
+
+                synchronized (mLock) {
+                    if (!mIsAligned) {
+                        handleEventDeviceAlignedWithSatellite(false);
+                    }
+                }
+            } catch (RemoteException e) {
+                loge("ConnectedState: RemoteException " + e);
+            }
+        }
+
+        @Override
+        public void exit() {
+            logd("Exiting ConnectedState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("ConnectedState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODE_OFF:
+                    transitionTo(mPowerOffState);
+                    break;
+                case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+                    handleEventDeviceAlignedWithSatellite((boolean) msg.obj);
+                    break;
+                case EVENT_DEVICE_NOT_ALIGNED:
+                    transitionTo(mNotConnectedState);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+
+        private void handleEventDeviceAlignedWithSatellite(boolean isAligned) {
+            if (!isAligned && !hasMessages(EVENT_DEVICE_NOT_ALIGNED)) {
+                long durationMillis =
+                        mSatelliteController.getDemoPointingNotAlignedDurationMillis();
+                logd("ConnectedState: handleEventAlignedWithSatellite isAligned=false."
+                        + " Send delayed EVENT_DEVICE_NOT_ALIGNED message"
+                        + " in durationMillis=" + durationMillis);
+                sendMessageDelayed(EVENT_DEVICE_NOT_ALIGNED, durationMillis);
+            } else if (isAligned && hasMessages(EVENT_DEVICE_NOT_ALIGNED)) {
+                logd("ConnectedState: handleEventAlignedWithSatellite isAligned=true."
+                        + " Remove EVENT_DEVICE_NOT_ALIGNED message.");
+                removeMessages(EVENT_DEVICE_NOT_ALIGNED);
+            }
+        }
+    }
+
+    /**
+     * @return the string for msg.what
+     */
+    @Override
+    protected String getWhatToString(int what) {
+        String whatString;
+        switch (what) {
+            case EVENT_SATELLITE_MODE_ON:
+                whatString = "EVENT_SATELLITE_MODE_ON";
+                break;
+            case EVENT_SATELLITE_MODE_OFF:
+                whatString = "EVENT_SATELLITE_MODE_OFF";
+                break;
+            case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+                whatString = "EVENT_DEVICE_ALIGNED_WITH_SATELLITE";
+                break;
+            case EVENT_DEVICE_ALIGNED:
+                whatString = "EVENT_DEVICE_ALIGNED";
+                break;
+            case EVENT_DEVICE_NOT_ALIGNED:
+                whatString = "EVENT_DEVICE_NOT_ALIGNED";
+                break;
+            default:
+                whatString = "UNKNOWN EVENT " + what;
+        }
+        return whatString;
+    }
+
+    /**
+     * Register the callback interface with satellite service.
+     *
+     * @param listener The callback interface to handle satellite service indications.
+     */
+    public void setSatelliteListener(@NonNull ISatelliteListener listener) {
+        mISatelliteListener = listener;
+    }
+
+    /**
+     * Allow cellular modem scanning while satellite mode is on.
+     *
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     *                             and {@code false} to disable
+     * @param errorCallback The callback to receive the error code result of the operation.
+     */
+    public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+            @NonNull IIntegerConsumer errorCallback) {
+        try {
+            errorCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS);
+        } catch (RemoteException e) {
+            loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
+        }
+    }
+
+    /**
+     * This function is used by {@link SatelliteSessionController} to notify {@link DemoSimulator}
+     * that satellite mode is ON.
+     */
+    public void onSatelliteModeOn() {
+        if (mSatelliteController.isDemoModeEnabled()) {
+            sendMessage(EVENT_SATELLITE_MODE_ON);
+        }
+    }
+
+    /**
+     * This function is used by {@link SatelliteSessionController} to notify {@link DemoSimulator}
+     * that satellite mode is OFF.
+     */
+    public void onSatelliteModeOff() {
+        sendMessage(EVENT_SATELLITE_MODE_OFF);
+    }
+
+    /**
+     * Set whether the device is aligned with the satellite.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void setDeviceAlignedWithSatellite(boolean isAligned) {
+        synchronized (mLock) {
+            if (mSatelliteController.isDemoModeEnabled()) {
+                mIsAligned = isAligned;
+                sendMessage(EVENT_DEVICE_ALIGNED_WITH_SATELLITE, isAligned);
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
index 65a25ea..dfc7919 100644
--- a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
+++ b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
@@ -47,7 +47,8 @@
         SatelliteController satelliteController = SatelliteController.getInstance();
         List<String> satellitePlmnList = satelliteController.getSatellitePlmnsForCarrier(subId);
         for (String satellitePlmn : satellitePlmnList) {
-            if (TextUtils.equals(satellitePlmn, registeredPlmn)) {
+            if (TextUtils.equals(satellitePlmn, registeredPlmn)
+                    && networkRegistrationInfo.isInService()) {
                 logd("Registered to satellite PLMN " + satellitePlmn);
                 networkRegistrationInfo.setIsNonTerrestrialNetwork(true);
                 networkRegistrationInfo.setAvailableServices(
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
index d6b40ca..06281c7 100644
--- a/src/java/com/android/internal/telephony/satellite/PointingAppController.java
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -34,6 +34,8 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.telephony.DropBoxManagerLoggerBackend;
+import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.PointingInfo;
@@ -42,6 +44,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -60,6 +63,7 @@
     @NonNull
     private static PointingAppController sInstance;
     @NonNull private final Context mContext;
+    @NonNull private final FeatureFlags mFeatureFlags;
     private boolean mStartedSatelliteTransmissionUpdates;
     private boolean mLastNeedFullScreenPointingUI;
     private boolean mLastIsDemoMode;
@@ -74,6 +78,7 @@
      */
     private final ConcurrentHashMap<Integer, SatelliteTransmissionUpdateHandler>
             mSatelliteTransmissionUpdateHandlers = new ConcurrentHashMap<>();
+    @Nullable private PersistentLogger mPersistentLogger = null;
 
     /**
      * @return The singleton instance of PointingAppController.
@@ -88,11 +93,13 @@
     /**
      * Create the PointingAppController singleton instance.
      * @param context The Context to use to create the PointingAppController.
+     * @param featureFlags The telephony feature flags.
      * @return The singleton instance of PointingAppController.
      */
-    public static PointingAppController make(@NonNull Context context) {
+    public static PointingAppController make(@NonNull Context context,
+            @NonNull FeatureFlags featureFlags) {
         if (sInstance == null) {
-            sInstance = new PointingAppController(context);
+            sInstance = new PointingAppController(context, featureFlags);
         }
         return sInstance;
     }
@@ -103,14 +110,20 @@
      * @param context The Context for the PointingUIController.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public PointingAppController(@NonNull Context context) {
+    public PointingAppController(@NonNull Context context,
+            @NonNull FeatureFlags featureFlags) {
         mContext = context;
+        mFeatureFlags = featureFlags;
         mStartedSatelliteTransmissionUpdates = false;
         mLastNeedFullScreenPointingUI = false;
         mLastIsDemoMode = false;
         mLastIsEmergency = false;
         mListenerForPointingUIRegistered = false;
         mActivityManager = mContext.getSystemService(ActivityManager.class);
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
     }
 
     /**
@@ -147,7 +160,7 @@
 
             if (callerPackages != null) {
                 if (Arrays.stream(callerPackages).anyMatch(pointingUiPackage::contains)) {
-                    logd("Restarting pointingUI");
+                    plogd("Restarting pointingUI");
                     startPointingUI(mLastNeedFullScreenPointingUI, mLastIsDemoMode,
                             mLastIsEmergency);
                 }
@@ -331,7 +344,7 @@
      */
     public void startSatelliteTransmissionUpdates(@NonNull Message message) {
         if (mStartedSatelliteTransmissionUpdates) {
-            logd("startSatelliteTransmissionUpdates: already started");
+            plogd("startSatelliteTransmissionUpdates: already started");
             AsyncResult.forMessage(message, null, new SatelliteManager.SatelliteException(
                     SatelliteManager.SATELLITE_RESULT_SUCCESS));
             message.sendToTarget();
@@ -359,7 +372,7 @@
             boolean isEmergency) {
         String packageName = getPointingUiPackageName();
         if (TextUtils.isEmpty(packageName)) {
-            logd("startPointingUI: config_pointing_ui_package is not set. Ignore the request");
+            plogd("startPointingUI: config_pointing_ui_package is not set. Ignore the request");
             return;
         }
 
@@ -373,10 +386,10 @@
             launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
         }
         if (launchIntent == null) {
-            loge("startPointingUI: launchIntent is null");
+            ploge("startPointingUI: launchIntent is null");
             return;
         }
-        logd("startPointingUI: needFullScreenPointingUI: " + needFullScreenPointingUI
+        plogd("startPointingUI: needFullScreenPointingUI: " + needFullScreenPointingUI
                 + ", isDemoMode: " + isDemoMode + ", isEmergency: " + isEmergency);
         launchIntent.putExtra("needFullScreen", needFullScreenPointingUI);
         launchIntent.putExtra("isDemoMode", isDemoMode);
@@ -393,7 +406,7 @@
             mLastIsEmergency = isEmergency;
             mContext.startActivity(launchIntent);
         } catch (ActivityNotFoundException ex) {
-            loge("startPointingUI: Pointing UI app activity is not found, ex=" + ex);
+            ploge("startPointingUI: Pointing UI app activity is not found, ex=" + ex);
         }
     }
 
@@ -422,7 +435,7 @@
                     request);
             msg.sendToTarget();
         } else {
-            loge("SatelliteTransmissionUpdateHandler not found for subId: " + subId);
+            ploge("SatelliteTransmissionUpdateHandler not found for subId: " + subId);
         }
     }
 
@@ -441,7 +454,7 @@
                     request);
             msg.sendToTarget();
         } else {
-            loge(" SatelliteTransmissionUpdateHandler not found for subId: " + subId);
+            ploge(" SatelliteTransmissionUpdateHandler not found for subId: " + subId);
         }
     }
 
@@ -456,12 +469,12 @@
     boolean setSatellitePointingUiClassName(
             @Nullable String packageName, @Nullable String className) {
         if (!isMockModemAllowed()) {
-            loge("setSatellitePointingUiClassName: modifying satellite pointing UI package and "
+            ploge("setSatellitePointingUiClassName: modifying satellite pointing UI package and "
                     + "class name is not allowed");
             return false;
         }
 
-        logd("setSatellitePointingUiClassName: config_pointing_ui_package is updated, new "
+        plogd("setSatellitePointingUiClassName: config_pointing_ui_package is updated, new "
                 + "packageName=" + packageName
                 + ", config_pointing_ui_class new className=" + className);
 
@@ -507,6 +520,33 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
     /**
      * TODO: The following needs to be added in this class:
      * - check if pointingUI crashes - then restart it
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java b/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
new file mode 100644
index 0000000..384dfa5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
@@ -0,0 +1,107 @@
+/*
+ * 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.satellite;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class SatelliteConstants {
+    public static final int CONFIG_DATA_SOURCE_UNKNOWN = 0;
+    public static final int CONFIG_DATA_SOURCE_ENTITLEMENT = 1;
+    public static final int CONFIG_DATA_SOURCE_CONFIG_UPDATER = 2;
+    public static final int CONFIG_DATA_SOURCE_CARRIER_CONFIG = 3;
+    public static final int CONFIG_DATA_SOURCE_DEVICE_CONFIG = 4;
+
+    @IntDef(prefix = {"CONFIG_DATA_SOURCE_"}, value = {
+            CONFIG_DATA_SOURCE_UNKNOWN,
+            CONFIG_DATA_SOURCE_ENTITLEMENT,
+            CONFIG_DATA_SOURCE_CONFIG_UPDATER,
+            CONFIG_DATA_SOURCE_CARRIER_CONFIG,
+            CONFIG_DATA_SOURCE_DEVICE_CONFIG
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConfigDataSource {}
+
+    public static final int SATELLITE_ENTITLEMENT_STATUS_UNKNOWN = 0;
+    public static final int SATELLITE_ENTITLEMENT_STATUS_DISABLED = 1;
+    public static final int SATELLITE_ENTITLEMENT_STATUS_ENABLED = 2;
+    public static final int SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE = 3;
+    public static final int SATELLITE_ENTITLEMENT_STATUS_PROVISIONING = 4;
+
+    @IntDef(prefix = {"SATELLITE_ENTITLEMENT_STATUS_"}, value = {
+            SATELLITE_ENTITLEMENT_STATUS_UNKNOWN,
+            SATELLITE_ENTITLEMENT_STATUS_DISABLED,
+            SATELLITE_ENTITLEMENT_STATUS_ENABLED,
+            SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE,
+            SATELLITE_ENTITLEMENT_STATUS_PROVISIONING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteEntitlementStatus {}
+
+    public static final int CONFIG_UPDATE_RESULT_UNKNOWN = 0;
+    public static final int CONFIG_UPDATE_RESULT_SUCCESS = 1;
+    public static final int CONFIG_UPDATE_RESULT_INVALID_DOMAIN = 2;
+    public static final int CONFIG_UPDATE_RESULT_INVALID_VERSION = 3;
+    public static final int CONFIG_UPDATE_RESULT_NO_DATA = 4;
+    public static final int CONFIG_UPDATE_RESULT_NO_SATELLITE_DATA = 5;
+    public static final int CONFIG_UPDATE_RESULT_PARSE_ERROR = 6;
+    public static final int CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_PLMN = 7;
+    public static final int CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_SUPPORTED_SERVICES = 8;
+    public static final int CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_COUNTRY_CODE = 9;
+    public static final int CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE = 10;
+    public static final int CONFIG_UPDATE_RESULT_IO_ERROR = 11;
+
+    @IntDef(prefix = {"CONFIG_UPDATE_RESULT_"}, value = {
+            CONFIG_UPDATE_RESULT_UNKNOWN,
+            CONFIG_UPDATE_RESULT_SUCCESS,
+            CONFIG_UPDATE_RESULT_INVALID_DOMAIN,
+            CONFIG_UPDATE_RESULT_INVALID_VERSION,
+            CONFIG_UPDATE_RESULT_NO_DATA,
+            CONFIG_UPDATE_RESULT_NO_SATELLITE_DATA,
+            CONFIG_UPDATE_RESULT_PARSE_ERROR,
+            CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_PLMN,
+            CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_SUPPORTED_SERVICES,
+            CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_COUNTRY_CODE,
+            CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE,
+            CONFIG_UPDATE_RESULT_IO_ERROR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConfigUpdateResult {}
+
+    // Access control type is unknown
+    public static final int ACCESS_CONTROL_TYPE_UNKNOWN = 0;
+    // Network country code is used for satellite access decision
+    public static final int ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE = 1;
+    // Device's current location is used for satellite access decision
+    public static final int ACCESS_CONTROL_TYPE_CURRENT_LOCATION = 2;
+    // Device's last known location is used for satellite access decision
+    public static final int ACCESS_CONTROL_TYPE_LAST_KNOWN_LOCATION = 3;
+    // Cached country codes are used for satellite access decision
+    public static final int ACCESS_CONTROL_TYPE_CACHED_COUNTRY_CODE = 4;
+
+    @IntDef(prefix = {"ACCESS_CONTROL_TYPE_"}, value = {
+            ACCESS_CONTROL_TYPE_UNKNOWN,
+            ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE,
+            ACCESS_CONTROL_TYPE_CURRENT_LOCATION,
+            ACCESS_CONTROL_TYPE_LAST_KNOWN_LOCATION,
+            ACCESS_CONTROL_TYPE_CACHED_COUNTRY_CODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AccessControlType {}
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 05051fa..f826f0b 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -82,8 +82,11 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
+import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -117,6 +120,8 @@
 import com.android.internal.telephony.configupdate.ConfigProviderAdaptor;
 import com.android.internal.telephony.configupdate.TelephonyConfigUpdateInstallReceiver;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
@@ -153,6 +158,7 @@
     private static final boolean DEBUG = !"user".equals(Build.TYPE);
     /** File used to store shared preferences related to satellite. */
     public static final String SATELLITE_SHARED_PREF = "satellite_shared_pref";
+    public static final String SATELLITE_SUBSCRIPTION_ID = "satellite_subscription_id";
     /** Value to pass for the setting key SATELLITE_MODE_ENABLED, enabled = 1, disabled = 0 */
     public static final int SATELLITE_MODE_ENABLED_TRUE = 1;
     public static final int SATELLITE_MODE_ENABLED_FALSE = 0;
@@ -162,6 +168,10 @@
      * to enable satellite.
      */
     public static final int TIMEOUT_TYPE_WAIT_FOR_SATELLITE_ENABLING_RESPONSE = 1;
+    /** This is used by CTS to override demo pointing aligned duration. */
+    public static final int TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS = 2;
+    /** This is used by CTS to override demo pointing not aligned duration. */
+    public static final int TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS = 3;
 
     /** Key used to read/write OEM-enabled satellite provision status in shared preferences. */
     private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY =
@@ -214,12 +224,14 @@
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
     @NonNull private final SatelliteModemInterface mSatelliteModemInterface;
-    @NonNull private SatelliteSessionController mSatelliteSessionController;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @NonNull protected SatelliteSessionController mSatelliteSessionController;
     @NonNull private final PointingAppController mPointingAppController;
     @NonNull private final DatagramController mDatagramController;
     @NonNull private final ControllerMetricsStats mControllerMetricsStats;
     @NonNull private final ProvisionMetricsStats mProvisionMetricsStats;
     @NonNull private SessionMetricsStats mSessionMetricsStats;
+    @NonNull private CarrierRoamingSatelliteControllerStats mCarrierRoamingSatelliteControllerStats;
     @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
     private final CommandsInterface mCi;
     private ContentResolver mContentResolver;
@@ -266,6 +278,8 @@
             new AtomicBoolean(false);
     private final AtomicBoolean mIsModemEnabledReportingNtnSignalStrength =
             new AtomicBoolean(false);
+    private final AtomicBoolean mLatestRequestedStateForNtnSignalStrengthReport =
+            new AtomicBoolean(false);
     private final AtomicBoolean mRegisteredForSatelliteSupportedStateChanged =
             new AtomicBoolean(false);
     /**
@@ -304,6 +318,8 @@
     private final Object mIsSatelliteEnabledLock = new Object();
     @GuardedBy("mIsSatelliteEnabledLock")
     private Boolean mIsSatelliteEnabled = null;
+    private final Object mIsRadioOnLock = new Object();
+    @GuardedBy("mIsRadioOnLock")
     private boolean mIsRadioOn = false;
     private final Object mSatelliteViaOemProvisionLock = new Object();
     @GuardedBy("mSatelliteViaOemProvisionLock")
@@ -364,6 +380,10 @@
     @GuardedBy("mSatelliteConnectedLock")
     @NonNull private final SparseBooleanArray mInitialized = new SparseBooleanArray();
 
+    @GuardedBy("mSatelliteConnectedLock")
+    @NonNull private final Map<Integer, CarrierRoamingSatelliteSessionStats>
+            mCarrierRoamingSatelliteSessionStatsMap = new HashMap<>();
+
     /**
      * Key: Subscription ID; Value: set of
      * {@link android.telephony.NetworkRegistrationInfo.ServiceType}
@@ -382,6 +402,8 @@
     private int mDelayInSendingEventDisplayEmergencyMessage = 0;
     @NonNull private SharedPreferences mSharedPreferences = null;
 
+    @Nullable private PersistentLogger mPersistentLogger = null;
+
     /**
      * Key : Subscription ID, Value: {@code true} if the EntitlementStatus is enabled,
      * {@code false} otherwise.
@@ -401,6 +423,13 @@
     private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
     private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0);
     private long mWaitTimeForSatelliteEnablingResponse;
+    private long mDemoPointingAlignedDurationMillis;
+    private long mDemoPointingNotAlignedDurationMillis;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private long mLastEmergencyCallTime;
+    private long mSatelliteEmergencyModeDurationMillis;
+    private static final int DEFAULT_SATELLITE_EMERGENCY_MODE_DURATION_SECONDS = 300;
 
     /** Key used to read/write satellite system notification done in shared preferences. */
     private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
@@ -419,6 +448,9 @@
     private long mSessionStartTimeStamp;
     private long mSessionProcessingTimeStamp;
 
+    // Variable for backup and restore device's screen rotation settings.
+    private String mDeviceRotationLockToBackupAndRestore = null;
+
     /**
      * @return The singleton instance of SatelliteController.
      */
@@ -455,6 +487,11 @@
             @NonNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags) {
         super(looper);
 
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
+
         mContext = context;
         mFeatureFlags = featureFlags;
         Phone phone = SatelliteServiceUtils.getPhone();
@@ -462,25 +499,32 @@
         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);
+        mSatelliteModemInterface = SatelliteModemInterface.make(
+                mContext, this, mFeatureFlags);
 
         // Create the PointingUIController singleton,
         // which is used to manage interactions with PointingUI app.
-        mPointingAppController = PointingAppController.make(mContext);
+        mPointingAppController = PointingAppController.make(mContext, mFeatureFlags);
 
         // Create the SatelliteControllerMetrics to report controller metrics
         // should be called before making DatagramController
         mControllerMetricsStats = ControllerMetricsStats.make(mContext);
         mProvisionMetricsStats = ProvisionMetricsStats.getOrCreateInstance();
         mSessionMetricsStats = SessionMetricsStats.getInstance();
+        mCarrierRoamingSatelliteControllerStats =
+                CarrierRoamingSatelliteControllerStats.getOrCreateInstance();
         mSubscriptionManagerService = SubscriptionManagerService.getInstance();
 
         // Create the DatagramController singleton,
         // which is used to send and receive satellite datagrams.
-        mDatagramController = DatagramController.make(mContext, looper, mPointingAppController);
+        mDatagramController = DatagramController.make(
+                mContext, looper, mFeatureFlags, mPointingAppController);
 
         mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
-        mIsRadioOn = phone.isRadioOn();
+        synchronized (mIsRadioOnLock) {
+            mIsRadioOn = phone.isRadioOn();
+        }
+
         registerForSatelliteProvisionStateChanged();
         registerForPendingDatagramCount();
         registerForSatelliteModemStateChanged();
@@ -528,6 +572,11 @@
                 null);
         loadSatelliteSharedPreferences();
         mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis();
+        mDemoPointingAlignedDurationMillis = getDemoPointingAlignedDurationMillisFromResources();
+        mDemoPointingNotAlignedDurationMillis =
+                getDemoPointingNotAlignedDurationMillisFromResources();
+        mSatelliteEmergencyModeDurationMillis =
+                getSatelliteEmergencyModeDurationFromOverlayConfig(context);
     }
 
     /**
@@ -591,10 +640,10 @@
                 String satelliteModeRadios = Settings.Global.getString(mContentResolver,
                         Settings.Global.SATELLITE_MODE_RADIOS);
                 if (satelliteModeRadios == null) {
-                    loge("initializeSatelliteModeRadios: satelliteModeRadios is null");
+                    ploge("initializeSatelliteModeRadios: satelliteModeRadios is null");
                     return;
                 }
-                logd("Radios To be checked when satellite is on: " + satelliteModeRadios);
+                plogd("Radios To be checked when satellite is on: " + satelliteModeRadios);
 
                 if (satelliteModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
                     BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -631,7 +680,7 @@
                     // Unregister receiver before registering it.
                     mContext.unregisterReceiver(mBTWifiNFCSateReceiver);
                 } catch (IllegalArgumentException e) {
-                    logd("initializeSatelliteModeRadios: unregisterReceiver, e=" + e);
+                    plogd("initializeSatelliteModeRadios: unregisterReceiver, e=" + e);
                 }
                 mContext.registerReceiver(mBTWifiNFCSateReceiver, radioStateIntentFilter);
 
@@ -652,12 +701,12 @@
                     }
                 }
 
-                logd("mDisableBTOnSatelliteEnabled: " + mDisableBTOnSatelliteEnabled
+                plogd("mDisableBTOnSatelliteEnabled: " + mDisableBTOnSatelliteEnabled
                         + " mDisableNFCOnSatelliteEnabled: " + mDisableNFCOnSatelliteEnabled
                         + " mDisableWifiOnSatelliteEnabled: " + mDisableWifiOnSatelliteEnabled
                         + " mDisableUWBOnSatelliteEnabled: " + mDisableUWBOnSatelliteEnabled);
 
-                logd("mBTStateEnabled: " + mBTStateEnabled
+                plogd("mBTStateEnabled: " + mBTStateEnabled
                         + " mNfcStateEnabled: " + mNfcStateEnabled
                         + " mWifiStateEnabled: " + mWifiStateEnabled
                         + " mUwbStateEnabled: " + mUwbStateEnabled);
@@ -685,8 +734,8 @@
 
         @Override
         public void onStateChanged(int state, int reason) {
-            logd("UwbAdapterStateCallback#onStateChanged() called, state = " + toString(state));
-            logd("Adapter state changed reason " + String.valueOf(reason));
+            plogd("UwbAdapterStateCallback#onStateChanged() called, state = " + toString(state));
+            plogd("Adapter state changed reason " + String.valueOf(reason));
             synchronized (mRadioStateLock) {
                 if (state == UwbManager.AdapterStateCallback.STATE_DISABLED) {
                     mUwbStateEnabled = false;
@@ -694,7 +743,7 @@
                 } else {
                     mUwbStateEnabled = true;
                 }
-                logd("mUwbStateEnabled: " + mUwbStateEnabled);
+                plogd("mUwbStateEnabled: " + mUwbStateEnabled);
             }
         }
     }
@@ -704,7 +753,7 @@
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             if (action == null) {
-                logd("BTWifiNFCStateReceiver NULL action for intent " + intent);
+                plogd("BTWifiNFCStateReceiver NULL action for intent " + intent);
                 return;
             }
 
@@ -721,7 +770,7 @@
                             mBTStateEnabled = true;
                         }
                         if (currentBTStateEnabled != mBTStateEnabled) {
-                            logd("mBTStateEnabled=" + mBTStateEnabled);
+                            plogd("mBTStateEnabled=" + mBTStateEnabled);
                         }
                     }
                     break;
@@ -737,7 +786,7 @@
                             evaluateToSendSatelliteEnabledSuccess();
                         }
                         if (currentNfcStateEnabled != mNfcStateEnabled) {
-                            logd("mNfcStateEnabled=" + mNfcStateEnabled);
+                            plogd("mNfcStateEnabled=" + mNfcStateEnabled);
                         }
                     }
                     break;
@@ -754,7 +803,7 @@
                             evaluateToSendSatelliteEnabledSuccess();
                         }
                         if (currentWifiStateEnabled != mWifiStateEnabled) {
-                            logd("mWifiStateEnabled=" + mWifiStateEnabled);
+                            plogd("mWifiStateEnabled=" + mWifiStateEnabled);
                         }
                     }
                     break;
@@ -943,14 +992,14 @@
                 RequestSatelliteEnabledArgument argument =
                         (RequestSatelliteEnabledArgument) request.argument;
                 int error =  SatelliteServiceUtils.getSatelliteError(ar, "setSatelliteEnabled");
-                logd("EVENT_SET_SATELLITE_ENABLED_DONE = " + error);
+                plogd("EVENT_SET_SATELLITE_ENABLED_DONE = " + error);
 
                 /*
                  * The timer to wait for EVENT_SET_SATELLITE_ENABLED_DONE might have expired and
                  * thus the request resources might have been cleaned up.
                  */
                 if (!shouldProcessEventSetSatelliteEnabledDone(argument)) {
-                    logw("The request ID=" + argument.requestId + ", enableSatellite="
+                    plogw("The request ID=" + argument.requestId + ", enableSatellite="
                             + argument.enableSatellite + " was already processed");
                     return;
                 }
@@ -962,6 +1011,7 @@
                             mWaitingForRadioDisabled = true;
                         }
                         setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_TRUE);
+                        setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_TRUE);
                         evaluateToSendSatelliteEnabledSuccess();
                     } else {
                         /**
@@ -988,13 +1038,14 @@
                                         SATELLITE_RESULT_SUCCESS,
                                         argument.callback);
                             } else {
-                                logd("Wait for satellite modem off before updating satellite"
+                                plogd("Wait for satellite modem off before updating satellite"
                                         + " modem state");
                             }
                             mWaitingForDisableSatelliteModemResponse = false;
                         }
                     }
                     // Request Ntn signal strength report when satellite enabled or disabled done.
+                    mLatestRequestedStateForNtnSignalStrengthReport.set(argument.enableSatellite);
                     updateNtnSignalStrengthReporting(argument.enableSatellite);
                 } else {
                     synchronized (mSatelliteEnabledRequestLock) {
@@ -1041,6 +1092,8 @@
 
                     mControllerMetricsStats.onSatelliteDisabled();
 
+                    handlePersistentLoggingOnSessionEnd(mIsEmergency);
+
                     synchronized (mIsSatelliteEnabledLock) {
                         mWaitingForDisableSatelliteModemResponse = false;
                     }
@@ -1068,11 +1121,11 @@
                 Bundle bundle = new Bundle();
                 if (error == SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
-                        loge("isSatelliteEnabled: result is null");
+                        ploge("isSatelliteEnabled: result is null");
                         error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean enabled = ((int[]) ar.result)[0] == 1;
-                        if (DBG) logd("isSatelliteEnabled: " + enabled);
+                        if (DBG) plogd("isSatelliteEnabled: " + enabled);
                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, enabled);
                         updateSatelliteEnabledState(enabled, "EVENT_IS_SATELLITE_ENABLED_DONE");
                     }
@@ -1097,11 +1150,11 @@
                 Bundle bundle = new Bundle();
                 if (error == SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
-                        loge("isSatelliteSupported: result is null");
+                        ploge("isSatelliteSupported: result is null");
                         error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean supported = (boolean) ar.result;
-                        logd("isSatelliteSupported: " + supported);
+                        plogd("isSatelliteSupported: " + supported);
                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, supported);
                         updateSatelliteSupportedStateWhenSatelliteServiceConnected(supported);
                     }
@@ -1125,14 +1178,14 @@
                 Bundle bundle = new Bundle();
                 if (error == SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
-                        loge("getSatelliteCapabilities: result is null");
+                        ploge("getSatelliteCapabilities: result is null");
                         error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         SatelliteCapabilities capabilities = (SatelliteCapabilities) ar.result;
                         synchronized (mNeedsSatellitePointingLock) {
                             mNeedsSatellitePointing = capabilities.isPointingRequired();
                         }
-                        if (DBG) logd("getSatelliteCapabilities: " + capabilities);
+                        if (DBG) plogd("getSatelliteCapabilities: " + capabilities);
                         bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
                                 capabilities);
                         synchronized (mSatelliteCapabilitiesLock) {
@@ -1160,13 +1213,13 @@
                 Bundle bundle = new Bundle();
                 if (error == SATELLITE_RESULT_SUCCESS) {
                     if (ar.result == null) {
-                        loge("requestTimeForNextSatelliteVisibility: result is null");
+                        ploge("requestTimeForNextSatelliteVisibility: result is null");
                         error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         int nextVisibilityDuration = ((int[]) ar.result)[0];
                         if (DBG) {
-                            logd("requestTimeForNextSatelliteVisibility: " +
-                                    nextVisibilityDuration);
+                            plogd("requestTimeForNextSatelliteVisibility: "
+                                    + nextVisibilityDuration);
                         }
                         bundle.putInt(SatelliteManager.KEY_SATELLITE_NEXT_VISIBILITY,
                                 nextVisibilityDuration);
@@ -1177,11 +1230,13 @@
             }
 
             case EVENT_RADIO_STATE_CHANGED: {
-                if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
-                    mIsRadioOn = true;
-                } else if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
-                    mIsRadioOn = false;
-                    resetCarrierRoamingSatelliteModeParams();
+                synchronized (mIsRadioOnLock) {
+                    logd("EVENT_RADIO_STATE_CHANGED: radioState=" + mCi.getRadioState());
+                    if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
+                        mIsRadioOn = true;
+                    } else if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
+                        resetCarrierRoamingSatelliteModeParams();
+                    }
                 }
 
                 if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
@@ -1192,7 +1247,7 @@
                                     @Override
                                     protected void onReceiveResult(
                                             int resultCode, Bundle resultData) {
-                                        logd("onRadioStateChanged.requestIsSatelliteSupported: "
+                                        plogd("onRadioStateChanged.requestIsSatelliteSupported: "
                                                 + "resultCode=" + resultCode
                                                 + ", resultData=" + resultData);
                                     }
@@ -1220,18 +1275,18 @@
             case EVENT_SATELLITE_PROVISION_STATE_CHANGED:
                 ar = (AsyncResult) msg.obj;
                 if (ar.result == null) {
-                    loge("EVENT_SATELLITE_PROVISION_STATE_CHANGED: result is null");
+                    ploge("EVENT_SATELLITE_PROVISION_STATE_CHANGED: result is null");
                 } else {
                     handleEventSatelliteProvisionStateChanged((boolean) ar.result);
                 }
                 break;
 
             case EVENT_PENDING_DATAGRAMS:
-                logd("Received EVENT_PENDING_DATAGRAMS");
+                plogd("Received EVENT_PENDING_DATAGRAMS");
                 IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
                     @Override
                     public void accept(int result) {
-                        logd("pollPendingSatelliteDatagram result: " + result);
+                        plogd("pollPendingSatelliteDatagram result: " + result);
                     }
                 };
                 pollPendingDatagrams(
@@ -1241,7 +1296,7 @@
             case EVENT_SATELLITE_MODEM_STATE_CHANGED:
                 ar = (AsyncResult) msg.obj;
                 if (ar.result == null) {
-                    loge("EVENT_SATELLITE_MODEM_STATE_CHANGED: result is null");
+                    ploge("EVENT_SATELLITE_MODEM_STATE_CHANGED: result is null");
                 } else {
                     handleEventSatelliteModemStateChanged((int) ar.result);
                 }
@@ -1252,7 +1307,7 @@
                 break;
 
             case CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE: {
-                logd("CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE");
+                plogd("CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE");
                 request = (SatelliteControllerHandlerRequest) msg.obj;
                 handleRequestSatelliteAttachRestrictionForCarrierCmd(request);
                 break;
@@ -1283,7 +1338,7 @@
             }
 
             case CMD_REQUEST_NTN_SIGNAL_STRENGTH: {
-                logd("CMD_REQUEST_NTN_SIGNAL_STRENGTH");
+                plogd("CMD_REQUEST_NTN_SIGNAL_STRENGTH");
                 request = (SatelliteControllerHandlerRequest) msg.obj;
                 onCompleted = obtainMessage(EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE, request);
                 mSatelliteModemInterface.requestNtnSignalStrength(onCompleted);
@@ -1312,7 +1367,7 @@
                                         NTN_SIGNAL_STRENGTH_NONE);
                             }
                         }
-                        loge("EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE: ntnSignalStrength is null");
+                        ploge("EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE: ntnSignalStrength is null");
                         result.send(SatelliteManager.SATELLITE_RESULT_REQUEST_FAILED, null);
                     }
                 } else {
@@ -1329,7 +1384,7 @@
             case EVENT_NTN_SIGNAL_STRENGTH_CHANGED: {
                 ar = (AsyncResult) msg.obj;
                 if (ar.result == null) {
-                    loge("EVENT_NTN_SIGNAL_STRENGTH_CHANGED: result is null");
+                    ploge("EVENT_NTN_SIGNAL_STRENGTH_CHANGED: result is null");
                 } else {
                     handleEventNtnSignalStrengthChanged((NtnSignalStrength) ar.result);
                 }
@@ -1340,7 +1395,7 @@
                 ar = (AsyncResult) msg.obj;
                 boolean shouldReport = (boolean) ar.result;
                 if (DBG) {
-                    logd("CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING: shouldReport=" + shouldReport);
+                    plogd("CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING: shouldReport=" + shouldReport);
                 }
                 handleCmdUpdateNtnSignalStrengthReporting(shouldReport);
                 break;
@@ -1355,6 +1410,13 @@
                                 + shouldReport);
                 if (errorCode == SATELLITE_RESULT_SUCCESS) {
                     mIsModemEnabledReportingNtnSignalStrength.set(shouldReport);
+                    if (mLatestRequestedStateForNtnSignalStrengthReport.get()
+                            != mIsModemEnabledReportingNtnSignalStrength.get()) {
+                        logd("mLatestRequestedStateForNtnSignalStrengthReport does not match with "
+                                + "mIsModemEnabledReportingNtnSignalStrength");
+                        updateNtnSignalStrengthReporting(
+                                mLatestRequestedStateForNtnSignalStrengthReport.get());
+                    }
                 } else {
                     loge(((boolean) request.argument ? "startSendingNtnSignalStrength"
                             : "stopSendingNtnSignalStrength") + "returns " + errorCode);
@@ -1370,7 +1432,7 @@
             case EVENT_SATELLITE_CAPABILITIES_CHANGED: {
                 ar = (AsyncResult) msg.obj;
                 if (ar.result == null) {
-                    loge("EVENT_SATELLITE_CAPABILITIES_CHANGED: result is null");
+                    ploge("EVENT_SATELLITE_CAPABILITIES_CHANGED: result is null");
                 } else {
                     handleEventSatelliteCapabilitiesChanged((SatelliteCapabilities) ar.result);
                 }
@@ -1380,7 +1442,7 @@
             case EVENT_SATELLITE_SUPPORTED_STATE_CHANGED: {
                 ar = (AsyncResult) msg.obj;
                 if (ar.result == null) {
-                    loge("EVENT_SATELLITE_SUPPORTED_STATE_CHANGED: result is null");
+                    ploge("EVENT_SATELLITE_SUPPORTED_STATE_CHANGED: result is null");
                 } else {
                     handleEventSatelliteSupportedStateChanged((boolean) ar.result);
                 }
@@ -1415,7 +1477,7 @@
                 processNewCarrierConfigData(subId);
             }
         } else {
-            loge("updateSupportedSatelliteServicesForActiveSubscriptions: "
+            ploge("updateSupportedSatelliteServicesForActiveSubscriptions: "
                     + "activeSubIds is null");
         }
     }
@@ -1440,7 +1502,7 @@
      */
     public void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
             boolean isEmergency, @NonNull IIntegerConsumer callback) {
-        logd("requestSatelliteEnabled subId: " + subId + " enableSatellite: " + enableSatellite
+        plogd("requestSatelliteEnabled subId: " + subId + " enableSatellite: " + enableSatellite
                 + " enableDemoMode: " + enableDemoMode + " isEmergency: " + isEmergency);
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         int error = evaluateOemSatelliteRequestAllowed(true);
@@ -1450,11 +1512,13 @@
         }
 
         if (enableSatellite) {
-            if (!mIsRadioOn) {
-                loge("Radio is not on, can not enable satellite");
-                sendErrorAndReportSessionMetrics(
-                        SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE, result);
-                return;
+            synchronized (mIsRadioOnLock) {
+                if (!mIsRadioOn) {
+                    ploge("Radio is not on, can not enable satellite");
+                    sendErrorAndReportSessionMetrics(
+                            SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE, result);
+                    return;
+                }
             }
         } else {
             /* if disable satellite, always assume demo is also disabled */
@@ -1465,13 +1529,13 @@
             if (mIsSatelliteEnabled != null) {
                 if (mIsSatelliteEnabled == enableSatellite) {
                     if (enableDemoMode != mIsDemoModeEnabled) {
-                        loge("Received invalid demo mode while satellite session is enabled"
+                        ploge("Received invalid demo mode while satellite session is enabled"
                                 + " enableDemoMode = " + enableDemoMode);
                         sendErrorAndReportSessionMetrics(
                                 SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS, result);
                         return;
                     } else {
-                        logd("Enable request matches with current state"
+                        plogd("Enable request matches with current state"
                                 + " enableSatellite = " + enableSatellite);
                         sendErrorAndReportSessionMetrics(
                                 SatelliteManager.SATELLITE_RESULT_SUCCESS, result);
@@ -1498,14 +1562,14 @@
             if (mSatelliteEnabledRequest == null) {
                 mSatelliteEnabledRequest = request;
             } else if (mSatelliteEnabledRequest.enableSatellite == request.enableSatellite) {
-                logd("requestSatelliteEnabled enableSatellite: " + enableSatellite
+                plogd("requestSatelliteEnabled enableSatellite: " + enableSatellite
                         + " is already in progress.");
                 sendErrorAndReportSessionMetrics(
                         SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS, result);
                 return;
             } else if (mSatelliteEnabledRequest.enableSatellite == false
                     && request.enableSatellite == true) {
-                logd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
+                plogd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
                         + "processed. Disable satellite is already in progress.");
                 sendErrorAndReportSessionMetrics(
                         SatelliteManager.SATELLITE_RESULT_ERROR, result);
@@ -1551,7 +1615,7 @@
      */
     public boolean isSatelliteEnabled() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("isSatelliteEnabled: oemEnabledSatelliteFlag is disabled");
+            plogd("isSatelliteEnabled: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         if (mIsSatelliteEnabled == null) return false;
@@ -1565,7 +1629,7 @@
      */
     public boolean isSatelliteBeingEnabled() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("isSatelliteBeingEnabled: oemEnabledSatelliteFlag is disabled");
+            plogd("isSatelliteBeingEnabled: oemEnabledSatelliteFlag is disabled");
             return false;
         }
 
@@ -1602,7 +1666,7 @@
      */
     public boolean isDemoModeEnabled() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("isDemoModeEnabled: oemEnabledSatelliteFlag is disabled");
+            plogd("isDemoModeEnabled: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         return mIsDemoModeEnabled;
@@ -1617,7 +1681,7 @@
      */
     public void requestIsSatelliteSupported(int subId, @NonNull ResultReceiver result) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("requestIsSatelliteSupported: oemEnabledSatelliteFlag is disabled");
+            plogd("requestIsSatelliteSupported: oemEnabledSatelliteFlag is disabled");
             result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
@@ -1626,6 +1690,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);
                 result.send(SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
@@ -1829,7 +1894,7 @@
     public void unregisterForSatelliteProvisionStateChanged(
             int subId, @NonNull ISatelliteProvisionStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("unregisterForSatelliteProvisionStateChanged: "
+            plogd("unregisterForSatelliteProvisionStateChanged: "
                     + "oemEnabledSatelliteFlag is disabled");
             return;
         }
@@ -1875,13 +1940,13 @@
     @SatelliteManager.SatelliteResult public int registerForSatelliteModemStateChanged(int subId,
             @NonNull ISatelliteModemStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForSatelliteModemStateChanged: oemEnabledSatelliteFlag is disabled");
+            plogd("registerForSatelliteModemStateChanged: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.registerForSatelliteModemStateChanged(callback);
         } else {
-            loge("registerForSatelliteModemStateChanged: mSatelliteSessionController"
+            ploge("registerForSatelliteModemStateChanged: mSatelliteSessionController"
                     + " is not initialized yet");
             return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         }
@@ -1899,13 +1964,13 @@
     public void unregisterForModemStateChanged(int subId,
             @NonNull ISatelliteModemStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("unregisterForModemStateChanged: oemEnabledSatelliteFlag is disabled");
+            plogd("unregisterForModemStateChanged: oemEnabledSatelliteFlag is disabled");
             return;
         }
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.unregisterForSatelliteModemStateChanged(callback);
         } else {
-            loge("unregisterForModemStateChanged: mSatelliteSessionController"
+            ploge("unregisterForModemStateChanged: mSatelliteSessionController"
                     + " is not initialized yet");
         }
     }
@@ -1921,13 +1986,13 @@
     @SatelliteManager.SatelliteResult public int registerForIncomingDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
+            plogd("registerForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
-        logd("registerForIncomingDatagram: callback=" + callback);
+        plogd("registerForIncomingDatagram: callback=" + callback);
         return mDatagramController.registerForSatelliteDatagram(subId, callback);
     }
 
@@ -1942,13 +2007,13 @@
     public void unregisterForIncomingDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("unregisterForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
+            plogd("unregisterForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
             return;
         }
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return;
         }
-        logd("unregisterForIncomingDatagram: callback=" + callback);
+        plogd("unregisterForIncomingDatagram: callback=" + callback);
         mDatagramController.unregisterForSatelliteDatagram(subId, callback);
     }
 
@@ -1994,7 +2059,7 @@
     public void sendDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
             SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull IIntegerConsumer callback) {
-        logd("sendSatelliteDatagram: subId: " + subId + " datagramType: " + datagramType
+        plogd("sendSatelliteDatagram: subId: " + subId + " datagramType: " + datagramType
                 + " needFullScreenPointingUI: " + needFullScreenPointingUI);
 
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
@@ -2043,9 +2108,11 @@
      */
     public void setDeviceAlignedWithSatellite(@NonNull int subId, @NonNull boolean isAligned) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setDeviceAlignedWithSatellite: oemEnabledSatelliteFlag is disabled");
+            plogd("setDeviceAlignedWithSatellite: oemEnabledSatelliteFlag is disabled");
             return;
         }
+
+        DemoSimulator.getInstance().setDeviceAlignedWithSatellite(isAligned);
         mDatagramController.setDeviceAlignedWithSatellite(isAligned);
     }
 
@@ -2158,7 +2225,7 @@
      * strength of the satellite connection.
      */
     public void requestNtnSignalStrength(int subId, @NonNull ResultReceiver result) {
-        if (DBG) logd("requestNtnSignalStrength()");
+        if (DBG) plogd("requestNtnSignalStrength()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) {
@@ -2197,7 +2264,7 @@
      */
     public void registerForNtnSignalStrengthChanged(int subId,
             @NonNull INtnSignalStrengthCallback callback) throws RemoteException {
-        if (DBG) logd("registerForNtnSignalStrengthChanged()");
+        if (DBG) plogd("registerForNtnSignalStrengthChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error == SATELLITE_RESULT_SUCCESS) {
@@ -2218,7 +2285,7 @@
      */
     public void unregisterForNtnSignalStrengthChanged(
             int subId, @NonNull INtnSignalStrengthCallback callback) {
-        if (DBG) logd("unregisterForNtnSignalStrengthChanged()");
+        if (DBG) plogd("unregisterForNtnSignalStrengthChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error == SATELLITE_RESULT_SUCCESS) {
@@ -2236,7 +2303,7 @@
      */
     @SatelliteManager.SatelliteResult public int registerForCapabilitiesChanged(
             int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
-        if (DBG) logd("registerForCapabilitiesChanged()");
+        if (DBG) plogd("registerForCapabilitiesChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) return error;
@@ -2256,7 +2323,7 @@
      */
     public void unregisterForCapabilitiesChanged(
             int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
-        if (DBG) logd("unregisterForCapabilitiesChanged()");
+        if (DBG) plogd("unregisterForCapabilitiesChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error == SATELLITE_RESULT_SUCCESS) {
@@ -2275,7 +2342,7 @@
     @SatelliteManager.SatelliteResult public int registerForSatelliteSupportedStateChanged(
             int subId, @NonNull ISatelliteSupportedStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForSatelliteSupportedStateChanged: oemEnabledSatelliteFlag is disabled");
+            plogd("registerForSatelliteSupportedStateChanged: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
         }
 
@@ -2294,7 +2361,7 @@
     public void unregisterForSatelliteSupportedStateChanged(
             int subId, @NonNull ISatelliteSupportedStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("unregisterForSatelliteSupportedStateChanged: "
+            plogd("unregisterForSatelliteSupportedStateChanged: "
                     + "oemEnabledSatelliteFlag is disabled");
             return;
         }
@@ -2310,16 +2377,16 @@
      */
     public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setSatelliteServicePackageName: oemEnabledSatelliteFlag is disabled");
+            plogd("setSatelliteServicePackageName: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         if (!isMockModemAllowed()) {
-            logd("setSatelliteServicePackageName: mock modem not allowed");
+            plogd("setSatelliteServicePackageName: mock modem not allowed");
             return false;
         }
 
         // Cached states need to be cleared whenever switching satellite vendor services.
-        logd("setSatelliteServicePackageName: Resetting cached states");
+        plogd("setSatelliteServicePackageName: Resetting cached states");
         synchronized (mIsSatelliteSupportedLock) {
             mIsSatelliteSupported = null;
         }
@@ -2346,11 +2413,11 @@
      */
     public boolean setSatelliteListeningTimeoutDuration(long timeoutMillis) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setSatelliteListeningTimeoutDuration: oemEnabledSatelliteFlag is disabled");
+            plogd("setSatelliteListeningTimeoutDuration: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         if (mSatelliteSessionController == null) {
-            loge("mSatelliteSessionController is not initialized yet");
+            ploge("mSatelliteSessionController is not initialized yet");
             return false;
         }
         return mSatelliteSessionController.setSatelliteListeningTimeoutDuration(timeoutMillis);
@@ -2366,16 +2433,35 @@
     public boolean setDatagramControllerTimeoutDuration(
             boolean reset, int timeoutType, long timeoutMillis) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setDatagramControllerTimeoutDuration: oemEnabledSatelliteFlag is disabled");
+            plogd("setDatagramControllerTimeoutDuration: oemEnabledSatelliteFlag is disabled");
             return false;
         }
-        logd("setDatagramControllerTimeoutDuration: reset=" + reset + ", timeoutType="
+        plogd("setDatagramControllerTimeoutDuration: reset=" + reset + ", timeoutType="
                 + timeoutType + ", timeoutMillis=" + timeoutMillis);
         return mDatagramController.setDatagramControllerTimeoutDuration(
                 reset, timeoutType, timeoutMillis);
     }
 
     /**
+     * This API can be used by only CTS to override the boolean configs used by the
+     * DatagramController module.
+     *
+     * @param enable Whether to enable or disable boolean config.
+     * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+     */
+    public boolean setDatagramControllerBooleanConfig(
+            boolean reset, int booleanType, boolean enable) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("setDatagramControllerBooleanConfig: oemEnabledSatelliteFlag is disabled");
+            return false;
+        }
+        logd("setDatagramControllerBooleanConfig: reset=" + reset + ", booleanType="
+                + booleanType + ", enable=" + enable);
+        return mDatagramController.setDatagramControllerBooleanConfig(
+                reset, booleanType, enable);
+    }
+
+    /**
      * This API can be used by only CTS to override timeout durations used by SatelliteController
      * module.
      *
@@ -2385,14 +2471,14 @@
     public boolean setSatelliteControllerTimeoutDuration(
             boolean reset, int timeoutType, long timeoutMillis) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setSatelliteControllerTimeoutDuration: oemEnabledSatelliteFlag is disabled");
+            plogd("setSatelliteControllerTimeoutDuration: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         if (!isMockModemAllowed()) {
-            logd("setSatelliteControllerTimeoutDuration: mock modem is not allowed");
+            plogd("setSatelliteControllerTimeoutDuration: mock modem is not allowed");
             return false;
         }
-        logd("setSatelliteControllerTimeoutDuration: reset=" + reset + ", timeoutType="
+        plogd("setSatelliteControllerTimeoutDuration: reset=" + reset + ", timeoutType="
                 + timeoutType + ", timeoutMillis=" + timeoutMillis);
         if (timeoutType == TIMEOUT_TYPE_WAIT_FOR_SATELLITE_ENABLING_RESPONSE) {
             if (reset) {
@@ -2401,9 +2487,23 @@
             } else {
                 mWaitTimeForSatelliteEnablingResponse = timeoutMillis;
             }
-            logd("mWaitTimeForSatelliteEnablingResponse=" + mWaitTimeForSatelliteEnablingResponse);
+            plogd("mWaitTimeForSatelliteEnablingResponse=" + mWaitTimeForSatelliteEnablingResponse);
+        } else if (timeoutType == TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS) {
+            if (reset) {
+                mDemoPointingAlignedDurationMillis =
+                        getDemoPointingAlignedDurationMillisFromResources();
+            } else {
+                mDemoPointingAlignedDurationMillis = timeoutMillis;
+            }
+        } else if (timeoutType == TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS) {
+            if (reset) {
+                mDemoPointingNotAlignedDurationMillis =
+                        getDemoPointingNotAlignedDurationMillisFromResources();
+            } else {
+                mDemoPointingNotAlignedDurationMillis = timeoutMillis;
+            }
         } else {
-            logw("Invalid timeoutType=" + timeoutType);
+            plogw("Invalid timeoutType=" + timeoutType);
             return false;
         }
         return true;
@@ -2418,11 +2518,11 @@
      */
     public boolean setSatelliteGatewayServicePackageName(@Nullable String servicePackageName) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setSatelliteGatewayServicePackageName: oemEnabledSatelliteFlag is disabled");
+            plogd("setSatelliteGatewayServicePackageName: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         if (mSatelliteSessionController == null) {
-            loge("mSatelliteSessionController is not initialized yet");
+            ploge("mSatelliteSessionController is not initialized yet");
             return false;
         }
         return mSatelliteSessionController.setSatelliteGatewayServicePackageName(
@@ -2440,7 +2540,7 @@
     public boolean setSatellitePointingUiClassName(
             @Nullable String packageName, @Nullable String className) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setSatellitePointingUiClassName: oemEnabledSatelliteFlag is disabled");
+            plogd("setSatellitePointingUiClassName: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         return mPointingAppController.setSatellitePointingUiClassName(packageName, className);
@@ -2461,7 +2561,7 @@
      */
     public boolean setEmergencyCallToSatelliteHandoverType(int handoverType, int delaySeconds) {
         if (!isMockModemAllowed()) {
-            loge("setEmergencyCallToSatelliteHandoverType: mock modem not allowed");
+            ploge("setEmergencyCallToSatelliteHandoverType: mock modem not allowed");
             return false;
         }
         if (isHandoverTypeValid(handoverType)) {
@@ -2485,7 +2585,7 @@
      */
     public boolean setOemEnabledSatelliteProvisionStatus(boolean reset, boolean isProvisioned) {
         if (!isMockModemAllowed()) {
-            loge("setOemEnabledSatelliteProvisionStatus: mock modem not allowed");
+            ploge("setOemEnabledSatelliteProvisionStatus: mock modem not allowed");
             return false;
         }
         synchronized (mSatelliteViaOemProvisionLock) {
@@ -2530,7 +2630,7 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public void onSatelliteServiceConnected() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("onSatelliteServiceConnected: oemEnabledSatelliteFlag is disabled");
+            plogd("onSatelliteServiceConnected: oemEnabledSatelliteFlag is disabled");
             return;
         }
         if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
@@ -2540,7 +2640,7 @@
                         @Override
                         protected void onReceiveResult(
                                 int resultCode, Bundle resultData) {
-                            logd("onSatelliteServiceConnected.requestIsSatelliteSupported:"
+                            plogd("onSatelliteServiceConnected.requestIsSatelliteSupported:"
                                     + " resultCode=" + resultCode);
                         }
                     };
@@ -2549,7 +2649,7 @@
                 }
             }
         } else {
-            logd("onSatelliteServiceConnected: Satellite vendor service is not supported."
+            plogd("onSatelliteServiceConnected: Satellite vendor service is not supported."
                     + " Ignored the event");
         }
     }
@@ -2560,18 +2660,21 @@
      * modem. {@link SatelliteController} will then power off the satellite modem.
      */
     public void onCellularRadioPowerOffRequested() {
+        logd("onCellularRadioPowerOffRequested()");
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("onCellularRadioPowerOffRequested: oemEnabledSatelliteFlag is disabled");
+            plogd("onCellularRadioPowerOffRequested: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
-        mIsRadioOn = false;
+        synchronized (mIsRadioOnLock) {
+            mIsRadioOn = false;
+        }
         requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                 false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */,
                 new IIntegerConsumer.Stub() {
                     @Override
                     public void accept(int result) {
-                        logd("onRadioPowerOffRequested: requestSatelliteEnabled result=" + result);
+                        plogd("onRadioPowerOffRequested: requestSatelliteEnabled result=" + result);
                     }
                 });
     }
@@ -2582,7 +2685,7 @@
      */
     public boolean isSatelliteSupportedViaOem() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("isSatelliteSupported: oemEnabledSatelliteFlag is disabled");
+            plogd("isSatelliteSupported: oemEnabledSatelliteFlag is disabled");
             return false;
         }
         Boolean supported = isSatelliteSupportedViaOemInternal();
@@ -2663,13 +2766,13 @@
      */
     public boolean isSatelliteAttachRequired() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("isSatelliteAttachRequired: oemEnabledSatelliteFlag is disabled");
+            plogd("isSatelliteAttachRequired: oemEnabledSatelliteFlag is disabled");
             return false;
         }
 
         synchronized (mSatelliteCapabilitiesLock) {
             if (mSatelliteCapabilities == null) {
-                loge("isSatelliteAttachRequired: mSatelliteCapabilities is null");
+                ploge("isSatelliteAttachRequired: mSatelliteCapabilities is null");
                 return false;
             }
             if (mSatelliteCapabilities.getSupportedRadioTechnologies().contains(
@@ -2798,7 +2901,7 @@
             return true;
         }
 
-        if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
+        if (getWwanIsInService(serviceState)) {
             // Device is connected to terrestrial network which has coverage
             resetCarrierRoamingSatelliteModeParams(subId);
             return false;
@@ -2846,6 +2949,20 @@
     }
 
     /**
+     * Request to get the {@link SatelliteSessionStats} of the satellite service.
+     *
+     * @param subId The subId of the subscription to the satellite session stats for.
+     * @param result The result receiver that returns the {@link SatelliteSessionStats}
+     *               if the request is successful or an error code if the request failed.
+     */
+    public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            return;
+        }
+        mSessionMetricsStats.requestSatelliteSessionStats(subId, result);
+    }
+
+    /**
      * Get the carrier-enabled emergency call wait for connection timeout millis
      */
     public long getCarrierEmergencyCallWaitForConnectionTimeoutMillis() {
@@ -2883,6 +3000,29 @@
     }
 
     /**
+     * Register the handler for SIM Refresh notifications.
+     * @param handler Handler for notification message.
+     * @param what User-defined message code.
+     */
+    public void registerIccRefresh(Handler handler, int what) {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            CommandsInterface ci = phone.mCi;
+            ci.registerForIccRefresh(handler, what, null);
+        }
+    }
+
+    /**
+     * Unregister the handler for SIM Refresh notifications.
+     * @param handler Handler for notification message.
+     */
+    public void unRegisterIccRefresh(Handler handler) {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            CommandsInterface ci = phone.mCi;
+            ci.unregisterForIccRefresh(handler);
+        }
+    }
+
+    /**
      * To use the satellite service, update the EntitlementStatus and the PlmnAllowedList after
      * receiving the satellite configuration from the entitlement server. If satellite
      * entitlement is enabled, enable satellite for the carrier. Otherwise, disable satellite.
@@ -2961,7 +3101,7 @@
     private boolean isValidPlmnList(@NonNull List<String> plmnList) {
         for (String plmn : plmnList) {
             if (!TelephonyUtils.isValidPlmn(plmn)) {
-                loge("Invalid PLMN = " + plmn);
+                ploge("Invalid PLMN = " + plmn);
                 return false;
             }
         }
@@ -2987,7 +3127,7 @@
                 new ResultReceiver(this) {
                     @Override
                     protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        logd("isSatelliteSupportedViaOemInternal.requestIsSatelliteSupported:"
+                        plogd("isSatelliteSupportedViaOemInternal.requestIsSatelliteSupported:"
                                 + " resultCode=" + resultCode);
                     }
                 });
@@ -2997,12 +3137,12 @@
     private void handleEventProvisionSatelliteServiceDone(
             @NonNull ProvisionSatelliteServiceArgument arg,
             @SatelliteManager.SatelliteResult int result) {
-        logd("handleEventProvisionSatelliteServiceDone: result="
+        plogd("handleEventProvisionSatelliteServiceDone: result="
                 + result + ", subId=" + arg.subId);
 
         Consumer<Integer> callback = mSatelliteProvisionCallbacks.remove(arg.subId);
         if (callback == null) {
-            loge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
+            ploge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
                     + arg.subId);
             mProvisionMetricsStats
                     .setResultCode(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)
@@ -3033,10 +3173,10 @@
             @NonNull ProvisionSatelliteServiceArgument arg,
             @SatelliteManager.SatelliteResult int result) {
         if (arg == null) {
-            loge("handleEventDeprovisionSatelliteServiceDone: arg is null");
+            ploge("handleEventDeprovisionSatelliteServiceDone: arg is null");
             return;
         }
-        logd("handleEventDeprovisionSatelliteServiceDone: result="
+        plogd("handleEventDeprovisionSatelliteServiceDone: result="
                 + result + ", subId=" + arg.subId);
 
         if (result == SATELLITE_RESULT_SUCCESS
@@ -3144,7 +3284,7 @@
                 new ResultReceiver(this) {
                     @Override
                     protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        logd("isSatelliteViaOemProvisioned: resultCode=" + resultCode);
+                        plogd("isSatelliteViaOemProvisioned: resultCode=" + resultCode);
                     }
                 });
         return null;
@@ -3153,10 +3293,11 @@
     private void handleSatelliteEnabled(SatelliteControllerHandlerRequest request) {
         RequestSatelliteEnabledArgument argument =
                 (RequestSatelliteEnabledArgument) request.argument;
+        handlePersistentLoggingOnSessionStart(argument);
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.onSatelliteEnablementStarted(argument.enableSatellite);
         } else {
-            loge("handleSatelliteEnabled: mSatelliteSessionController is not initialized yet");
+            ploge("handleSatelliteEnabled: mSatelliteSessionController is not initialized yet");
         }
 
         if (!argument.enableSatellite && mSatelliteModemInterface.isSatelliteServiceSupported()) {
@@ -3197,27 +3338,29 @@
             mIsSatelliteSupported = supported;
         }
         mSatelliteSessionController = SatelliteSessionController.make(
-                mContext, getLooper(), supported);
+                mContext, getLooper(), mFeatureFlags, supported);
+        plogd("create a new SatelliteSessionController due to isSatelliteSupported state has "
+                + "changed to " + supported);
+
         if (supported) {
             registerForSatelliteProvisionStateChanged();
             registerForPendingDatagramCount();
             registerForSatelliteModemStateChanged();
             registerForNtnSignalStrengthChanged();
             registerForCapabilitiesChanged();
-            registerForSatelliteSupportedStateChanged();
 
             requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
-                            logd("requestIsSatelliteProvisioned: resultCode=" + resultCode
+                            plogd("requestIsSatelliteProvisioned: resultCode=" + resultCode
                                     + ", resultData=" + resultData);
                             requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                                     false, false, false,
                                     new IIntegerConsumer.Stub() {
                                         @Override
                                         public void accept(int result) {
-                                            logd("requestSatelliteEnabled: result=" + result);
+                                            plogd("requestSatelliteEnabled: result=" + result);
                                         }
                                     });
                         }
@@ -3226,11 +3369,12 @@
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
-                            logd("requestSatelliteCapabilities: resultCode=" + resultCode
+                            plogd("requestSatelliteCapabilities: resultCode=" + resultCode
                                     + ", resultData=" + resultData);
                         }
                     });
         }
+        registerForSatelliteSupportedStateChanged();
     }
 
     private void updateSatelliteEnabledState(boolean enabled, String caller) {
@@ -3241,7 +3385,7 @@
             mSatelliteSessionController.onSatelliteEnabledStateChanged(enabled);
             mSatelliteSessionController.setDemoMode(mIsDemoModeEnabled);
         } else {
-            loge(caller + ": mSatelliteSessionController is not initialized yet");
+            ploge(caller + ": mSatelliteSessionController is not initialized yet");
         }
         if (!enabled) {
             mIsModemEnabledReportingNtnSignalStrength.set(false);
@@ -3280,7 +3424,7 @@
 
     private void registerForNtnSignalStrengthChanged() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForNtnSignalStrengthChanged: oemEnabledSatelliteFlag is disabled");
+            plogd("registerForNtnSignalStrengthChanged: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
@@ -3295,7 +3439,7 @@
 
     private void registerForCapabilitiesChanged() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForCapabilitiesChanged: oemEnabledSatelliteFlag is disabled");
+            plogd("registerForCapabilitiesChanged: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
@@ -3319,7 +3463,7 @@
     }
 
     private void handleEventSatelliteProvisionStateChanged(boolean provisioned) {
-        logd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
+        plogd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
 
         synchronized (mSatelliteViaOemProvisionLock) {
             persistOemEnabledSatelliteProvisionStatus(provisioned);
@@ -3331,7 +3475,7 @@
             try {
                 listener.onSatelliteProvisionStateChanged(provisioned);
             } catch (RemoteException e) {
-                logd("handleSatelliteProvisionStateChangedEvent RemoteException: " + e);
+                plogd("handleSatelliteProvisionStateChangedEvent RemoteException: " + e);
                 deadCallersList.add(listener);
             }
         });
@@ -3342,7 +3486,7 @@
 
     private void handleEventSatelliteModemStateChanged(
             @SatelliteManager.SatelliteModemState int state) {
-        logd("handleEventSatelliteModemStateChanged: state=" + state);
+        plogd("handleEventSatelliteModemStateChanged: state=" + state);
         if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
                 || state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
             synchronized (mIsSatelliteEnabledLock) {
@@ -3360,8 +3504,8 @@
                     }
                     moveSatelliteToOffStateAndCleanUpResources(error, callback);
                 } else {
-                    logd("Either waiting for the response of disabling satellite modem or the event"
-                            + " should be ignored because isSatelliteEnabled="
+                    plogd("Either waiting for the response of disabling satellite modem or the"
+                            + " event should be ignored because isSatelliteEnabled="
                             + isSatelliteEnabled()
                             + ", mIsSatelliteEnabled=" + mIsSatelliteEnabled);
                 }
@@ -3371,7 +3515,7 @@
             if (mSatelliteSessionController != null) {
                 mSatelliteSessionController.onSatelliteModemStateChanged(state);
             } else {
-                loge("handleEventSatelliteModemStateChanged: mSatelliteSessionController is null");
+                ploge("handleEventSatelliteModemStateChanged: mSatelliteSessionController is null");
             }
         }
     }
@@ -3386,13 +3530,14 @@
         synchronized (mNtnSignalsStrengthLock) {
             mNtnSignalStrength = ntnSignalStrength;
         }
+        mSessionMetricsStats.updateMaxNtnSignalStrengthLevel(ntnSignalStrength.getLevel());
 
         List<INtnSignalStrengthCallback> deadCallersList = new ArrayList<>();
         mNtnSignalStrengthChangedListeners.values().forEach(listener -> {
             try {
                 listener.onNtnSignalStrengthChanged(ntnSignalStrength);
             } catch (RemoteException e) {
-                logd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
+                plogd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
                 deadCallersList.add(listener);
             }
         });
@@ -3402,9 +3547,9 @@
     }
 
     private void handleEventSatelliteCapabilitiesChanged(SatelliteCapabilities capabilities) {
-        logd("handleEventSatelliteCapabilitiesChanged()");
+        plogd("handleEventSatelliteCapabilitiesChanged()");
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("handleEventSatelliteCapabilitiesChanged: oemEnabledSatelliteFlag is disabled");
+            plogd("handleEventSatelliteCapabilitiesChanged: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
@@ -3417,7 +3562,7 @@
             try {
                 listener.onSatelliteCapabilitiesChanged(capabilities);
             } catch (RemoteException e) {
-                logd("handleEventSatelliteCapabilitiesChanged RemoteException: " + e);
+                plogd("handleEventSatelliteCapabilitiesChanged RemoteException: " + e);
                 deadCallersList.add(listener);
             }
         });
@@ -3427,21 +3572,24 @@
     }
 
     private void handleEventSatelliteSupportedStateChanged(boolean supported) {
-        logd("handleSatelliteSupportedStateChangedEvent: supported=" + supported);
+        plogd("handleSatelliteSupportedStateChangedEvent: supported=" + supported);
 
         synchronized (mIsSatelliteSupportedLock) {
             if (mIsSatelliteSupported != null && mIsSatelliteSupported == supported) {
                 if (DBG) {
-                    logd("current satellite support state and new supported state are matched,"
+                    plogd("current satellite support state and new supported state are matched,"
                             + " ignore update.");
                 }
                 return;
             }
+
+            updateSatelliteSupportedStateWhenSatelliteServiceConnected(supported);
+
             /* In case satellite has been reported as not support from modem, but satellite is
                enabled, request disable satellite. */
             synchronized (mIsSatelliteEnabledLock) {
                 if (!supported && mIsSatelliteEnabled != null && mIsSatelliteEnabled) {
-                    logd("Invoke requestSatelliteEnabled(), supported=false, "
+                    plogd("Invoke requestSatelliteEnabled(), supported=false, "
                             + "mIsSatelliteEnabled=true");
                     requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                             false /* enableSatellite */, false /* enableDemoMode */,
@@ -3449,7 +3597,7 @@
                             new IIntegerConsumer.Stub() {
                                 @Override
                                 public void accept(int result) {
-                                    logd("handleSatelliteSupportedStateChangedEvent: request "
+                                    plogd("handleSatelliteSupportedStateChangedEvent: request "
                                             + "satellite disable, result="
                                             + result);
                                 }
@@ -3465,7 +3613,7 @@
             try {
                 listener.onSatelliteSupportedStateChanged(supported);
             } catch (RemoteException e) {
-                logd("handleSatelliteSupportedStateChangedEvent RemoteException: " + e);
+                plogd("handleSatelliteSupportedStateChangedEvent RemoteException: " + e);
                 deadCallersList.add(listener);
             }
         });
@@ -3476,11 +3624,114 @@
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected void setSettingsKeyForSatelliteMode(int val) {
-        logd("setSettingsKeyForSatelliteMode val: " + val);
+        plogd("setSettingsKeyForSatelliteMode val: " + val);
         Settings.Global.putInt(mContext.getContentResolver(),
                     Settings.Global.SATELLITE_MODE_ENABLED, val);
     }
 
+    /**
+     * Allow screen rotation temporary in rotation locked foldable device.
+     * <p>
+     * Temporarily allow screen rotation user to catch satellite signals properly by UI guide in
+     * emergency situations. Unlock the setting value so that the screen rotation is not locked, and
+     * return it to the original value when the satellite service is finished.
+     * <p>
+     * Note that, only the unfolded screen will be temporarily allowed screen rotation.
+     *
+     * @param val {@link SATELLITE_MODE_ENABLED_TRUE} if satellite mode is enabled,
+     *     {@link SATELLITE_MODE_ENABLED_FALSE} satellite mode is not enabled.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected void setSettingsKeyToAllowDeviceRotation(int val) {
+        // Only allows on a foldable device type.
+        if (!isFoldable(mContext)) {
+            return;
+        }
+
+        switch (val) {
+            case SATELLITE_MODE_ENABLED_TRUE:
+                mDeviceRotationLockToBackupAndRestore =
+                        Settings.Secure.getString(mContentResolver,
+                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK);
+                String unlockedRotationSettings = replaceDeviceRotationValue(
+                        mDeviceRotationLockToBackupAndRestore == null
+                                ? "" : mDeviceRotationLockToBackupAndRestore,
+                        Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+                Settings.Secure.putString(mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK, unlockedRotationSettings);
+                logd("setSettingsKeyToAllowDeviceRotation(TRUE), RotationSettings is changed"
+                        + " from " + mDeviceRotationLockToBackupAndRestore
+                        + " to " + unlockedRotationSettings);
+                break;
+            case SATELLITE_MODE_ENABLED_FALSE:
+                if (mDeviceRotationLockToBackupAndRestore == null) {
+                    break;
+                }
+                Settings.Secure.putString(mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        mDeviceRotationLockToBackupAndRestore);
+                logd("setSettingsKeyToAllowDeviceRotation(FALSE), RotationSettings is restored to"
+                        + mDeviceRotationLockToBackupAndRestore);
+                mDeviceRotationLockToBackupAndRestore = "";
+                break;
+            default:
+                loge("setSettingsKeyToAllowDeviceRotation(" + val + "), never reach here.");
+                break;
+        }
+    }
+
+    /**
+     * If the device type is foldable.
+     *
+     * @param context context
+     * @return {@code true} if device type is foldable. {@code false} for otherwise.
+     */
+    private boolean isFoldable(Context context) {
+        return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0;
+    }
+
+    /**
+     * Replaces a value of given a target key with a new value in a string of key-value pairs.
+     * <p>
+     * Replaces the value corresponding to the target key with a new value. If the key value is not
+     * found in the device rotation information, it is not replaced.
+     *
+     * @param deviceRotationValue Device rotation key values separated by colon(':').
+     * @param targetKey The key of the new item caller wants to add.
+     * @param newValue  The value of the new item caller want to add.
+     * @return A new string where all the key-value pairs.
+     */
+    private static String replaceDeviceRotationValue(
+            @NonNull String deviceRotationValue, int targetKey, int newValue) {
+        // Use list of Key-Value pair
+        List<Pair<Integer, Integer>> keyValuePairs = new ArrayList<>();
+
+        String[] pairs = deviceRotationValue.split(":");
+        if (pairs.length % 2 != 0) {
+            // Return without modifying. The key-value may be incorrect if length is an odd number.
+            loge("The length of key-value pair do not match. Return without modification.");
+            return deviceRotationValue;
+        }
+
+        // collect into keyValuePairs
+        for (int i = 0; i < pairs.length; i += 2) {
+            try {
+                int key = Integer.parseInt(pairs[i]);
+                int value = Integer.parseInt(pairs[i + 1]);
+                keyValuePairs.add(new Pair<>(key, key == targetKey ? newValue : value));
+            } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+                // Return without modifying if got exception.
+                loge("got error while parsing key-value. Return without modification. e:" + e);
+                return deviceRotationValue;
+            }
+        }
+
+        return keyValuePairs.stream()
+                .map(pair -> pair.first + ":" + pair.second) // Convert to "key:value" format
+                .collect(Collectors.joining(":")); // Join pairs with colons
+    }
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected boolean areAllRadiosDisabled() {
         synchronized (mRadioStateLock) {
@@ -3488,20 +3739,20 @@
                     || (mDisableNFCOnSatelliteEnabled && mNfcStateEnabled)
                     || (mDisableWifiOnSatelliteEnabled && mWifiStateEnabled)
                     || (mDisableUWBOnSatelliteEnabled && mUwbStateEnabled)) {
-                logd("All radios are not disabled yet.");
+                plogd("All radios are not disabled yet.");
                 return false;
             }
-            logd("All radios are disabled.");
+            plogd("All radios are disabled.");
             return true;
         }
     }
 
     private void evaluateToSendSatelliteEnabledSuccess() {
-        logd("evaluateToSendSatelliteEnabledSuccess");
+        plogd("evaluateToSendSatelliteEnabledSuccess");
         synchronized (mSatelliteEnabledRequestLock) {
             if (areAllRadiosDisabled() && (mSatelliteEnabledRequest != null)
                     && mWaitingForRadioDisabled) {
-                logd("Sending success to callback that sent enable satellite request");
+                plogd("Sending success to callback that sent enable satellite request");
                 setDemoModeEnabled(mSatelliteEnabledRequest.enableDemoMode);
                 mIsEmergency = mSatelliteEnabledRequest.isEmergency;
                 synchronized (mIsSatelliteEnabledLock) {
@@ -3518,7 +3769,7 @@
     }
 
     private void resetSatelliteEnabledRequest() {
-        logd("resetSatelliteEnabledRequest");
+        plogd("resetSatelliteEnabledRequest");
         synchronized (mSatelliteEnabledRequestLock) {
             mSatelliteEnabledRequest = null;
             mWaitingForRadioDisabled = false;
@@ -3527,13 +3778,15 @@
 
     private void moveSatelliteToOffStateAndCleanUpResources(
             @SatelliteManager.SatelliteResult int error, @Nullable Consumer<Integer> callback) {
-        logd("moveSatelliteToOffStateAndCleanUpResources");
+        plogd("moveSatelliteToOffStateAndCleanUpResources");
         synchronized (mIsSatelliteEnabledLock) {
             resetSatelliteEnabledRequest();
             setDemoModeEnabled(false);
+            handlePersistentLoggingOnSessionEnd(mIsEmergency);
             mIsEmergency = false;
             mIsSatelliteEnabled = false;
             setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
+            setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_FALSE);
             if (callback != null) callback.accept(error);
             updateSatelliteEnabledState(
                     false, "moveSatelliteToOffStateAndCleanUpResources");
@@ -3543,7 +3796,7 @@
     private void setDemoModeEnabled(boolean enabled) {
         mIsDemoModeEnabled = enabled;
         mDatagramController.setDemoMode(mIsDemoModeEnabled);
-        logd("setDemoModeEnabled: mIsDemoModeEnabled=" + mIsDemoModeEnabled);
+        plogd("setDemoModeEnabled: mIsDemoModeEnabled=" + mIsDemoModeEnabled);
     }
 
     private boolean isMockModemAllowed() {
@@ -3601,19 +3854,21 @@
      * Otherwise, If the carrierPlmnList exist then used it.
      */
     private void updatePlmnListPerCarrier(int subId) {
-        logd("updatePlmnListPerCarrier: subId=" + subId);
+        plogd("updatePlmnListPerCarrier: subId=" + subId);
         synchronized (mSupportedSatelliteServicesLock) {
             List<String> carrierPlmnList, entitlementPlmnList;
             if (getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
                     false)) {
                 entitlementPlmnList = mEntitlementPlmnListPerCarrier.get(subId,
                         new ArrayList<>()).stream().toList();
-                logd("updatePlmnListPerCarrier: entitlementPlmnList="
+                plogd("updatePlmnListPerCarrier: entitlementPlmnList="
                         + String.join(",", entitlementPlmnList)
                         + " size=" + entitlementPlmnList.size());
                 if (!entitlementPlmnList.isEmpty()) {
                     mMergedPlmnListPerCarrier.put(subId, entitlementPlmnList);
-                    logd("mMergedPlmnListPerCarrier is updated by Entitlement");
+                    plogd("mMergedPlmnListPerCarrier is updated by Entitlement");
+                    mCarrierRoamingSatelliteControllerStats.reportConfigDataSource(
+                            SatelliteConstants.CONFIG_DATA_SOURCE_ENTITLEMENT);
                     return;
                 }
             }
@@ -3624,9 +3879,11 @@
                 int carrierId = tm.createForSubscriptionId(subId).getSimCarrierId();
                 List<String> plmnList = satelliteConfig.getAllSatellitePlmnsForCarrier(carrierId);
                 if (!plmnList.isEmpty()) {
-                    logd("mMergedPlmnListPerCarrier is updated by ConfigUpdater : "
+                    plogd("mMergedPlmnListPerCarrier is updated by ConfigUpdater : "
                             + String.join(",", plmnList));
                     mMergedPlmnListPerCarrier.put(subId, plmnList);
+                    mCarrierRoamingSatelliteControllerStats.reportConfigDataSource(
+                            SatelliteConstants.CONFIG_DATA_SOURCE_CONFIG_UPDATER);
                     return;
                 }
             }
@@ -3635,18 +3892,20 @@
                     && mSatelliteServicesSupportedByCarriers.get(subId) != null) {
                 carrierPlmnList =
                         mSatelliteServicesSupportedByCarriers.get(subId).keySet().stream().toList();
-                logd("mMergedPlmnListPerCarrier is updated by carrier config: "
+                plogd("mMergedPlmnListPerCarrier is updated by carrier config: "
                         + String.join(",", carrierPlmnList));
+                mCarrierRoamingSatelliteControllerStats.reportConfigDataSource(
+                        SatelliteConstants.CONFIG_DATA_SOURCE_CARRIER_CONFIG);
             } else {
                 carrierPlmnList = new ArrayList<>();
-                logd("Empty mMergedPlmnListPerCarrier");
+                plogd("Empty mMergedPlmnListPerCarrier");
             }
             mMergedPlmnListPerCarrier.put(subId, carrierPlmnList);
         }
     }
 
     private void updateSupportedSatelliteServices(int subId) {
-        logd("updateSupportedSatelliteServices with subId " + subId);
+        plogd("updateSupportedSatelliteServices with subId " + subId);
         synchronized (mSupportedSatelliteServicesLock) {
             SatelliteConfig satelliteConfig = getSatelliteConfig();
 
@@ -3658,19 +3917,19 @@
                         satelliteConfig.getSupportedSatelliteServices(carrierId);
                 if (!supportedServicesPerPlmn.isEmpty()) {
                     mSatelliteServicesSupportedByCarriers.put(subId, supportedServicesPerPlmn);
-                    logd("updateSupportedSatelliteServices using ConfigUpdater, "
+                    plogd("updateSupportedSatelliteServices using ConfigUpdater, "
                             + "supportedServicesPerPlmn = " + supportedServicesPerPlmn.size());
                     updatePlmnListPerCarrier(subId);
                     return;
                 } else {
-                    logd("supportedServicesPerPlmn is empty");
+                    plogd("supportedServicesPerPlmn is empty");
                 }
             }
 
             mSatelliteServicesSupportedByCarriers.put(
                     subId, readSupportedSatelliteServicesFromCarrierConfig(subId));
             updatePlmnListPerCarrier(subId);
-            logd("updateSupportedSatelliteServices using carrier config");
+            plogd("updateSupportedSatelliteServices using carrier config");
         }
     }
 
@@ -3711,7 +3970,7 @@
 
     private void handleCarrierConfigChanged(int slotIndex, int subId, int carrierId,
             int specificCarrierId) {
-        logd("handleCarrierConfigChanged(): slotIndex(" + slotIndex + "), subId("
+        plogd("handleCarrierConfigChanged(): slotIndex(" + slotIndex + "), subId("
                 + subId + "), carrierId(" + carrierId + "), specificCarrierId("
                 + specificCarrierId + ")");
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -3727,11 +3986,6 @@
 
     private void processNewCarrierConfigData(int subId) {
         configureSatellitePlmnForCarrier(subId);
-        synchronized (mIsSatelliteEnabledLock) {
-            mSatelliteAttachRestrictionForCarrierArray.clear();
-            mIsSatelliteAttachEnabledForCarrierArrayPerSub.clear();
-        }
-
         setSatelliteAttachEnabledForCarrierOnSimLoaded(subId);
         updateRestrictReasonForEntitlementPerCarrier(subId);
     }
@@ -3749,21 +4003,21 @@
      */
     private void updateEntitlementPlmnListPerCarrier(int subId) {
         if (!getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false)) {
-            logd("don't support entitlement");
+            plogd("don't support entitlement");
             return;
         }
 
         synchronized (mSupportedSatelliteServicesLock) {
             if (mEntitlementPlmnListPerCarrier.indexOfKey(subId) < 0) {
-                logd("updateEntitlementPlmnListPerCarrier: no correspondent cache, load from "
+                plogd("updateEntitlementPlmnListPerCarrier: no correspondent cache, load from "
                         + "persist storage");
                 List<String> entitlementPlmnList =
                         mSubscriptionManagerService.getSatelliteEntitlementPlmnList(subId);
                 if (entitlementPlmnList.isEmpty()) {
-                    logd("updateEntitlementPlmnListPerCarrier: read empty list");
+                    plogd("updateEntitlementPlmnListPerCarrier: read empty list");
                     return;
                 }
-                logd("updateEntitlementPlmnListPerCarrier: entitlementPlmnList="
+                plogd("updateEntitlementPlmnListPerCarrier: entitlementPlmnList="
                         + String.join(",", entitlementPlmnList));
                 mEntitlementPlmnListPerCarrier.put(subId, entitlementPlmnList);
             }
@@ -3793,7 +4047,7 @@
         try {
             strArray = mContext.getResources().getStringArray(id);
         } catch (Resources.NotFoundException ex) {
-            loge("readStringArrayFromOverlayConfig: id= " + id + ", ex=" + ex);
+            ploge("readStringArrayFromOverlayConfig: id= " + id + ", ex=" + ex);
         }
         if (strArray == null) {
             strArray = new String[0];
@@ -3823,7 +4077,7 @@
                 return !cachedRestrictionSet.contains(
                         SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER);
             } else {
-                logd("isSatelliteAttachEnabledForCarrierByUser() no correspondent cache, "
+                plogd("isSatelliteAttachEnabledForCarrierByUser() no correspondent cache, "
                         + "load from persist storage");
                 try {
                     String enabled =
@@ -3832,13 +4086,13 @@
                                     mContext.getOpPackageName(), mContext.getAttributionTag());
 
                     if (enabled == null) {
-                        loge("isSatelliteAttachEnabledForCarrierByUser: invalid subId, subId="
+                        ploge("isSatelliteAttachEnabledForCarrierByUser: invalid subId, subId="
                                 + subId);
                         return false;
                     }
 
                     if (enabled.isEmpty()) {
-                        loge("isSatelliteAttachEnabledForCarrierByUser: no data for subId(" + subId
+                        ploge("isSatelliteAttachEnabledForCarrierByUser: no data for subId(" + subId
                                 + ")");
                         return false;
                     }
@@ -3853,7 +4107,7 @@
                         return result;
                     }
                 } catch (IllegalArgumentException | SecurityException ex) {
-                    loge("isSatelliteAttachEnabledForCarrierByUser: ex=" + ex);
+                    ploge("isSatelliteAttachEnabledForCarrierByUser: ex=" + ex);
                     return false;
                 }
             }
@@ -3876,19 +4130,19 @@
 
     private void updateRestrictReasonForEntitlementPerCarrier(int subId) {
         if (!getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false)) {
-            logd("don't support entitlement");
+            plogd("don't support entitlement");
             return;
         }
 
         IIntegerConsumer callback = new IIntegerConsumer.Stub() {
             @Override
             public void accept(int result) {
-                logd("updateRestrictReasonForEntitlementPerCarrier:" + result);
+                plogd("updateRestrictReasonForEntitlementPerCarrier:" + result);
             }
         };
         synchronized (mSupportedSatelliteServicesLock) {
             if (mSatelliteEntitlementStatusPerCarrier.indexOfKey(subId) < 0) {
-                logd("updateRestrictReasonForEntitlementPerCarrier: no correspondent cache, "
+                plogd("updateRestrictReasonForEntitlementPerCarrier: no correspondent cache, "
                         + "load from persist storage");
                 String entitlementStatus = null;
                 try {
@@ -3897,17 +4151,17 @@
                                     SATELLITE_ENTITLEMENT_STATUS, mContext.getOpPackageName(),
                                     mContext.getAttributionTag());
                 } catch (IllegalArgumentException | SecurityException e) {
-                    loge("updateRestrictReasonForEntitlementPerCarrier, e=" + e);
+                    ploge("updateRestrictReasonForEntitlementPerCarrier, e=" + e);
                 }
 
                 if (entitlementStatus == null) {
-                    loge("updateRestrictReasonForEntitlementPerCarrier: invalid subId, subId="
+                    ploge("updateRestrictReasonForEntitlementPerCarrier: invalid subId, subId="
                             + subId + " set to default value");
                     entitlementStatus = "0";
                 }
 
                 if (entitlementStatus.isEmpty()) {
-                    loge("updateRestrictReasonForEntitlementPerCarrier: no data for subId(" + subId
+                    ploge("updateRestrictReasonForEntitlementPerCarrier: no data for subId(" + subId
                             + "). set to default value");
                     entitlementStatus = "0";
                 }
@@ -3932,9 +4186,9 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected boolean persistSatelliteAttachEnabledForCarrierSetting(int subId) {
-        logd("persistSatelliteAttachEnabledForCarrierSetting");
+        plogd("persistSatelliteAttachEnabledForCarrierSetting");
         if (!isValidSubscriptionId(subId)) {
-            loge("persistSatelliteAttachEnabledForCarrierSetting: subId is not valid,"
+            ploge("persistSatelliteAttachEnabledForCarrierSetting: subId is not valid,"
                     + " subId=" + subId);
             return false;
         }
@@ -3947,7 +4201,7 @@
                                 .contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)
                                 ? "0" : "1");
             } catch (IllegalArgumentException | SecurityException ex) {
-                loge("persistSatelliteAttachEnabledForCarrierSetting, ex=" + ex);
+                ploge("persistSatelliteAttachEnabledForCarrierSetting, ex=" + ex);
                 return false;
             }
         }
@@ -3996,12 +4250,12 @@
     private void evaluateEnablingSatelliteForCarrier(int subId, int reason,
             @Nullable Consumer<Integer> callback) {
         if (callback == null) {
-            callback = errorCode -> logd("evaluateEnablingSatelliteForCarrier: "
+            callback = errorCode -> plogd("evaluateEnablingSatelliteForCarrier: "
                     + "SetSatelliteAttachEnableForCarrier error code =" + errorCode);
         }
 
         if (!isSatelliteSupportedViaCarrier(subId)) {
-            logd("Satellite for carrier is not supported. Only user setting is stored");
+            plogd("Satellite for carrier is not supported. Only user setting is stored");
             callback.accept(SATELLITE_RESULT_SUCCESS);
             return;
         }
@@ -4033,17 +4287,17 @@
     @SatelliteManager.SatelliteResult private int evaluateOemSatelliteRequestAllowed(
             boolean isProvisionRequired) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("oemEnabledSatelliteFlag is disabled");
+            plogd("oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
         }
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
-            logd("evaluateOemSatelliteRequestAllowed: satellite service is not supported");
+            plogd("evaluateOemSatelliteRequestAllowed: satellite service is not supported");
             return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
         }
 
         Boolean satelliteSupported = isSatelliteSupportedViaOemInternal();
         if (satelliteSupported == null) {
-            logd("evaluateOemSatelliteRequestAllowed: satelliteSupported is null");
+            plogd("evaluateOemSatelliteRequestAllowed: satelliteSupported is null");
             return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         }
         if (!satelliteSupported) {
@@ -4053,7 +4307,7 @@
         if (isProvisionRequired) {
             Boolean satelliteProvisioned = isSatelliteViaOemProvisioned();
             if (satelliteProvisioned == null) {
-                logd("evaluateOemSatelliteRequestAllowed: satelliteProvisioned is null");
+                plogd("evaluateOemSatelliteRequestAllowed: satelliteProvisioned is null");
                 return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
             }
             if (!satelliteProvisioned) {
@@ -4113,7 +4367,18 @@
             }
 
             synchronized (mSatelliteConnectedLock) {
+                CarrierRoamingSatelliteSessionStats sessionStats =
+                        mCarrierRoamingSatelliteSessionStatsMap.get(subId);
+
                 if (serviceState.isUsingNonTerrestrialNetwork()) {
+                    if (sessionStats != null) {
+                        sessionStats.onSignalStrength(phone);
+                        if (!mWasSatelliteConnectedViaCarrier.get(subId)) {
+                            // Log satellite connection start
+                            sessionStats.onConnectionStart(phone);
+                        }
+                    }
+
                     resetCarrierRoamingSatelliteModeParams(subId);
                     mWasSatelliteConnectedViaCarrier.put(subId, true);
 
@@ -4126,19 +4391,24 @@
                     }
                 } else {
                     Boolean connected = mWasSatelliteConnectedViaCarrier.get(subId);
-                    if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
+                    if (getWwanIsInService(serviceState)) {
                         resetCarrierRoamingSatelliteModeParams(subId);
                     } else if (connected != null && connected) {
                         // The device just got disconnected from a satellite network
                         // and is not connected to any terrestrial network that  has coverage
                         mLastSatelliteDisconnectedTimesMillis.put(subId, getElapsedRealtime());
 
-                        logd("sendMessageDelayed subId:" + subId
+                        plogd("sendMessageDelayed subId:" + subId
                                 + " phoneId:" + phone.getPhoneId()
                                 + " time:" + getSatelliteConnectionHysteresisTimeMillis(subId));
                         sendMessageDelayed(obtainMessage(EVENT_NOTIFY_NTN_HYSTERESIS_TIMED_OUT,
                                         phone.getPhoneId()),
                                 getSatelliteConnectionHysteresisTimeMillis(subId));
+
+                        if (sessionStats != null) {
+                            // Log satellite connection end
+                            sessionStats.onConnectionEnd();
+                        }
                     }
                     mWasSatelliteConnectedViaCarrier.put(subId, false);
                 }
@@ -4163,6 +4433,27 @@
                 if (!initialized) mInitialized.put(subId, true);
                 mLastNotifiedNtnMode.put(subId, currNtnMode);
                 phone.notifyCarrierRoamingNtnModeChanged(currNtnMode);
+                logCarrierRoamingSatelliteSessionStats(phone, lastNotifiedNtnMode, currNtnMode);
+            }
+        }
+    }
+
+    private void logCarrierRoamingSatelliteSessionStats(@NonNull Phone phone,
+            boolean lastNotifiedNtnMode, boolean currNtnMode) {
+        synchronized (mSatelliteConnectedLock) {
+            int subId = phone.getSubId();
+            if (!lastNotifiedNtnMode && currNtnMode) {
+                // Log satellite session start
+                CarrierRoamingSatelliteSessionStats sessionStats =
+                        CarrierRoamingSatelliteSessionStats.getInstance(subId);
+                sessionStats.onSessionStart(phone.getCarrierId(), phone);
+                mCarrierRoamingSatelliteSessionStatsMap.put(subId, sessionStats);
+            } else if (lastNotifiedNtnMode && !currNtnMode) {
+                // Log satellite session end
+                CarrierRoamingSatelliteSessionStats sessionStats =
+                        mCarrierRoamingSatelliteSessionStatsMap.get(subId);
+                sessionStats.onSessionEnd();
+                mCarrierRoamingSatelliteSessionStatsMap.remove(subId);
             }
         }
     }
@@ -4175,12 +4466,12 @@
 
     private void persistOemEnabledSatelliteProvisionStatus(boolean isProvisioned) {
         synchronized (mSatelliteViaOemProvisionLock) {
-            logd("persistOemEnabledSatelliteProvisionStatus: isProvisioned=" + isProvisioned);
+            plogd("persistOemEnabledSatelliteProvisionStatus: isProvisioned=" + isProvisioned);
 
             if (!loadSatelliteSharedPreferences()) return;
 
             if (mSharedPreferences == null) {
-                loge("persistOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
+                ploge("persistOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
             } else {
                 mSharedPreferences.edit().putBoolean(
                         OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, isProvisioned).apply();
@@ -4193,7 +4484,7 @@
             if (!loadSatelliteSharedPreferences()) return false;
 
             if (mSharedPreferences == null) {
-                loge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
+                ploge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
                 return false;
             } else {
                 return mSharedPreferences.getBoolean(
@@ -4209,7 +4500,7 @@
                         mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
                                 Context.MODE_PRIVATE);
             } catch (Exception e) {
-                loge("loadSatelliteSharedPreferences: Cannot get default "
+                ploge("loadSatelliteSharedPreferences: Cannot get default "
                         + "shared preferences, e=" + e);
                 return false;
             }
@@ -4224,18 +4515,18 @@
         boolean isSatelliteProvisionedInModem = false;
         if (error == SATELLITE_RESULT_SUCCESS) {
             if (ar.result == null) {
-                loge("handleIsSatelliteProvisionedDoneEvent: result is 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) {
-            logd("handleIsSatelliteProvisionedDoneEvent: Modem does not support this request");
+            plogd("handleIsSatelliteProvisionedDoneEvent: Modem does not support this request");
             isSatelliteProvisionedInModem = true;
         }
         boolean isSatelliteViaOemProvisioned =
                 isSatelliteProvisionedInModem && getPersistedOemEnabledSatelliteProvisionStatus();
-        logd("isSatelliteProvisionedInModem=" + isSatelliteProvisionedInModem
+        plogd("isSatelliteProvisionedInModem=" + isSatelliteProvisionedInModem
                 + ", isSatelliteViaOemProvisioned=" + isSatelliteViaOemProvisioned);
         Bundle bundle = new Bundle();
         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, isSatelliteViaOemProvisioned);
@@ -4254,11 +4545,11 @@
             @NonNull RequestSatelliteEnabledArgument argument) {
         synchronized (mSatelliteEnabledRequestLock) {
             if (hasMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT, argument)) {
-                logd("WaitForSatelliteEnablingResponseTimer of request ID "
+                plogd("WaitForSatelliteEnablingResponseTimer of request ID "
                         + argument.requestId + " was already started");
                 return;
             }
-            logd("Start timer to wait for response of the satellite enabling request ID="
+            plogd("Start timer to wait for response of the satellite enabling request ID="
                     + argument.requestId + ", enableSatellite=" + argument.enableSatellite
                     + ", mWaitTimeForSatelliteEnablingResponse="
                     + mWaitTimeForSatelliteEnablingResponse);
@@ -4270,7 +4561,7 @@
     private void stopWaitForSatelliteEnablingResponseTimer(
             @NonNull RequestSatelliteEnabledArgument argument) {
         synchronized (mSatelliteEnabledRequestLock) {
-            logd("Stop timer to wait for response of the satellite enabling request ID="
+            plogd("Stop timer to wait for response of the satellite enabling request ID="
                     + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
             removeMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT, argument);
         }
@@ -4288,7 +4579,7 @@
 
     private void handleEventWaitForSatelliteEnablingResponseTimedOut(
             @NonNull RequestSatelliteEnabledArgument argument) {
-        logw("Timed out to wait for response of the satellite enabling request ID="
+        plogw("Timed out to wait for response of the satellite enabling request ID="
                 + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
 
         synchronized (mSatelliteEnabledRequestLock) {
@@ -4313,7 +4604,7 @@
                     IIntegerConsumer callback = new IIntegerConsumer.Stub() {
                         @Override
                         public void accept(int result) {
-                            logd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
+                            plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
                                     + "disable satellite result=" + result);
                         }
                     };
@@ -4363,19 +4654,20 @@
 
     private void handleCmdUpdateNtnSignalStrengthReporting(boolean shouldReport) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("handleCmdUpdateNtnSignalStrengthReporting: oemEnabledSatelliteFlag is "
+            plogd("handleCmdUpdateNtnSignalStrengthReporting: oemEnabledSatelliteFlag is "
                     + "disabled");
             return;
         }
 
         if (!isSatelliteEnabled()) {
-            logd("handleCmdUpdateNtnSignalStrengthReporting: ignore request, satellite is "
+            plogd("handleCmdUpdateNtnSignalStrengthReporting: ignore request, satellite is "
                     + "disabled");
             return;
         }
 
+        mLatestRequestedStateForNtnSignalStrengthReport.set(shouldReport);
         if (mIsModemEnabledReportingNtnSignalStrength.get() == shouldReport) {
-            logd("handleCmdUpdateNtnSignalStrengthReporting: ignore request. "
+            plogd("handleCmdUpdateNtnSignalStrengthReporting: ignore request. "
                     + "mIsModemEnabledReportingNtnSignalStrength="
                     + mIsModemEnabledReportingNtnSignalStrength.get());
             return;
@@ -4386,7 +4678,7 @@
 
     private void updateNtnSignalStrengthReporting(boolean shouldReport) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("updateNtnSignalStrengthReporting: oemEnabledSatelliteFlag is "
+            plogd("updateNtnSignalStrengthReporting: oemEnabledSatelliteFlag is "
                     + "disabled");
             return;
         }
@@ -4396,10 +4688,10 @@
         Message onCompleted = obtainMessage(EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE,
                 request);
         if (shouldReport) {
-            logd("updateNtnSignalStrengthReporting: startSendingNtnSignalStrength");
+            plogd("updateNtnSignalStrengthReporting: startSendingNtnSignalStrength");
             mSatelliteModemInterface.startSendingNtnSignalStrength(onCompleted);
         } else {
-            logd("updateNtnSignalStrengthReporting: stopSendingNtnSignalStrength");
+            plogd("updateNtnSignalStrengthReporting: stopSendingNtnSignalStrength");
             mSatelliteModemInterface.stopSendingNtnSignalStrength(onCompleted);
         }
     }
@@ -4416,12 +4708,12 @@
      */
     public boolean setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setShouldSendDatagramToModemInDemoMode: oemEnabledSatelliteFlag is disabled");
+            plogd("setShouldSendDatagramToModemInDemoMode: oemEnabledSatelliteFlag is disabled");
             return false;
         }
 
         if (!isMockModemAllowed()) {
-            logd("setShouldSendDatagramToModemInDemoMode: mock modem not allowed.");
+            plogd("setShouldSendDatagramToModemInDemoMode: mock modem not allowed.");
             return false;
         }
 
@@ -4458,7 +4750,7 @@
     }
 
     private void showSatelliteSystemNotification(int subId) {
-        logd("showSatelliteSystemNotification");
+        plogd("showSatelliteSystemNotification");
         final NotificationChannel notificationChannel = new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID,
                 NOTIFICATION_CHANNEL,
@@ -4496,7 +4788,7 @@
             intent.addCategory(Intent.CATEGORY_HOME);
             return intent;
         }).orElseGet(() -> {
-            loge("showSatelliteSystemNotification: no default sms package name, Invoke "
+            ploge("showSatelliteSystemNotification: no default sms package name, Invoke "
                     + "default sms compose window instead");
             Intent newIntent = new Intent(Intent.ACTION_VIEW);
             newIntent.setData(Uri.parse("sms:"));
@@ -4523,6 +4815,8 @@
 
         notificationManager.notifyAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
                 notificationBuilder.build(), UserHandle.ALL);
+
+        mCarrierRoamingSatelliteControllerStats.reportCountOfSatelliteNotificationDisplayed();
     }
 
     private void resetCarrierRoamingSatelliteModeParams() {
@@ -4536,7 +4830,6 @@
     private void resetCarrierRoamingSatelliteModeParams(int subId) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) return;
 
-        logd("resetCarrierRoamingSatelliteModeParams subId:" + subId);
         synchronized (mSatelliteConnectedLock) {
             mLastSatelliteDisconnectedTimesMillis.put(subId, null);
             mSatModeCapabilitiesForCarrierRoaming.remove(subId);
@@ -4568,11 +4861,64 @@
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.onSatelliteEnablementFailed();
         } else {
-            loge("notifyEnablementFailedToSatelliteSessionController: mSatelliteSessionController"
+            ploge("notifyEnablementFailedToSatelliteSessionController: mSatelliteSessionController"
                     + " is not initialized yet");
         }
     }
 
+    private long getDemoPointingAlignedDurationMillisFromResources() {
+        long durationMillis = 15000L;
+        try {
+            durationMillis = mContext.getResources().getInteger(
+                    R.integer.config_demo_pointing_aligned_duration_millis);
+        } catch (Resources.NotFoundException ex) {
+            loge("getPointingAlignedDurationMillis: ex=" + ex);
+        }
+
+        return durationMillis;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public long getDemoPointingAlignedDurationMillis() {
+        return mDemoPointingAlignedDurationMillis;
+    }
+
+    private long getDemoPointingNotAlignedDurationMillisFromResources() {
+        long durationMillis = 30000L;
+        try {
+            durationMillis = mContext.getResources().getInteger(
+                    R.integer.config_demo_pointing_not_aligned_duration_millis);
+        } catch (Resources.NotFoundException ex) {
+            loge("getPointingNotAlignedDurationMillis: ex=" + ex);
+        }
+
+        return durationMillis;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public long getDemoPointingNotAlignedDurationMillis() {
+        return mDemoPointingNotAlignedDurationMillis;
+    }
+
+    private boolean getWwanIsInService(ServiceState serviceState) {
+        List<NetworkRegistrationInfo> nriList = serviceState
+                .getNetworkRegistrationInfoListForTransportType(
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        for (NetworkRegistrationInfo nri : nriList) {
+            if (nri.isInService()) {
+                logv("getWwanIsInService: return true");
+                return true;
+            }
+        }
+
+        logv("getWwanIsInService: return false");
+        return false;
+    }
+
+    private static void logv(@NonNull String log) {
+        Rlog.v(TAG, log);
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
@@ -4584,4 +4930,98 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void plogw(@NonNull String log) {
+        Rlog.w(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.warn(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
+
+    private void handlePersistentLoggingOnSessionStart(RequestSatelliteEnabledArgument argument) {
+        if (mPersistentLogger == null) {
+            return;
+        }
+        if (argument.isEmergency) {
+            DropBoxManagerLoggerBackend.getInstance(mContext).setLoggingEnabled(true);
+        }
+    }
+
+    private void handlePersistentLoggingOnSessionEnd(boolean isEmergency) {
+        if (mPersistentLogger == null) {
+            return;
+        }
+        DropBoxManagerLoggerBackend loggerBackend =
+                DropBoxManagerLoggerBackend.getInstance(mContext);
+        // Flush persistent satellite logs on eSOS session end
+        if (isEmergency) {
+            loggerBackend.flushAsync();
+        }
+        // Also turn off persisted logging until new session is started
+        loggerBackend.setLoggingEnabled(false);
+    }
+
+    /**
+     * Set last emergency call time to the current time.
+     */
+    public void setLastEmergencyCallTime() {
+        synchronized (mLock) {
+            mLastEmergencyCallTime = getElapsedRealtime();
+            plogd("mLastEmergencyCallTime=" + mLastEmergencyCallTime);
+        }
+    }
+
+    /**
+     * Check if satellite is in emergency mode.
+     */
+    public boolean isInEmergencyMode() {
+        synchronized (mLock) {
+            if (mLastEmergencyCallTime == 0) return false;
+
+            long currentTime = getElapsedRealtime();
+            if ((currentTime - mLastEmergencyCallTime) <= mSatelliteEmergencyModeDurationMillis) {
+                plogd("Satellite is in emergency mode");
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private long getSatelliteEmergencyModeDurationFromOverlayConfig(@NonNull Context context) {
+        Integer duration = DEFAULT_SATELLITE_EMERGENCY_MODE_DURATION_SECONDS;
+        try {
+            duration = context.getResources().getInteger(com.android.internal.R.integer
+                    .config_satellite_emergency_mode_duration);
+        } catch (Resources.NotFoundException ex) {
+            ploge("getSatelliteEmergencyModeDurationFromOverlayConfig: got ex=" + ex);
+        }
+        return TimeUnit.SECONDS.toMillis(duration);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 2e99ae6..da4c69b 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -30,8 +30,10 @@
 import android.os.Message;
 import android.os.RegistrantList;
 import android.os.RemoteException;
+import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.IBooleanConsumer;
 import android.telephony.IIntegerConsumer;
+import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
@@ -42,6 +44,7 @@
 import android.telephony.satellite.stub.ISatellite;
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
 import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.SatelliteModemState;
 import android.telephony.satellite.stub.SatelliteService;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -49,6 +52,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import java.util.Arrays;
 import java.util.List;
@@ -64,6 +68,9 @@
 
     @NonNull private static SatelliteModemInterface sInstance;
     @NonNull private final Context mContext;
+    @NonNull private final DemoSimulator mDemoSimulator;
+    @NonNull private final SatelliteListener mVendorListener;
+    @NonNull private final SatelliteListener mDemoListener;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     @NonNull protected final ExponentialBackoff mExponentialBackoff;
     @NonNull private final Object mLock = new Object();
@@ -77,6 +84,7 @@
     @NonNull private String mVendorSatellitePackageName = "";
     private boolean mIsBound;
     private boolean mIsBinding;
+    @Nullable private PersistentLogger mPersistentLogger = null;
 
     @NonNull private final RegistrantList mSatelliteProvisionStateChangedRegistrants =
             new RegistrantList();
@@ -96,7 +104,14 @@
     @NonNull private final RegistrantList mSatelliteSupportedStateChangedRegistrants =
             new RegistrantList();
 
-    @NonNull private final ISatelliteListener mListener = new ISatelliteListener.Stub() {
+    private class SatelliteListener extends ISatelliteListener.Stub {
+
+        private final boolean mIsDemoListener;
+
+        SatelliteListener(boolean isDemoListener) {
+            mIsDemoListener = isDemoListener;
+        }
+
         @Override
         public void onSatelliteProvisionStateChanged(boolean provisioned) {
             mSatelliteProvisionStateChangedRegistrants.notifyResult(provisioned);
@@ -105,15 +120,19 @@
         @Override
         public void onSatelliteDatagramReceived(
                 android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
-            logd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
-            mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
-                    SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+            if (notifyResultIfExpectedListener()) {
+                plogd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
+                mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
+                        SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+            }
         }
 
         @Override
         public void onPendingDatagrams() {
-            logd("onPendingDatagrams");
-            mPendingDatagramsRegistrants.notifyResult(null);
+            if (notifyResultIfExpectedListener()) {
+                plogd("onPendingDatagrams");
+                mPendingDatagramsRegistrants.notifyResult(null);
+            }
         }
 
         @Override
@@ -125,33 +144,39 @@
 
         @Override
         public void onSatelliteModemStateChanged(int state) {
-            mSatelliteModemStateChangedRegistrants.notifyResult(
-                    SatelliteServiceUtils.fromSatelliteModemState(state));
-            int datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
-            switch (state) {
-                case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
-                    datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
-                    break;
-                case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
-                    datagramTransferState =
-                            SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
-                    break;
-                case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
-                    datagramTransferState =
-                            SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
-                    break;
-                case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
-                    // keep previous state as this could be retrying sending or receiving
-                    break;
+            if (notifyModemStateChanged(state)) {
+                mSatelliteModemStateChangedRegistrants.notifyResult(
+                        SatelliteServiceUtils.fromSatelliteModemState(state));
+                int datagramTransferState =
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
+                switch (state) {
+                    case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
+                        datagramTransferState =
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+                        break;
+                    case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
+                        datagramTransferState =
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
+                        break;
+                    case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
+                        datagramTransferState =
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
+                        break;
+                    case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
+                        // keep previous state as this could be retrying sending or receiving
+                        break;
+                }
+                mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
             }
-            mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
         }
 
         @Override
         public void onNtnSignalStrengthChanged(
                 android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) {
-            mNtnSignalStrengthChangedRegistrants.notifyResult(
-                    SatelliteServiceUtils.fromNtnSignalStrength(ntnSignalStrength));
+            if (notifyResultIfExpectedListener()) {
+                mNtnSignalStrengthChangedRegistrants.notifyResult(
+                        SatelliteServiceUtils.fromNtnSignalStrength(ntnSignalStrength));
+            }
         }
 
         @Override
@@ -165,7 +190,27 @@
         public void onSatelliteSupportedStateChanged(boolean supported) {
             mSatelliteSupportedStateChangedRegistrants.notifyResult(supported);
         }
-    };
+
+        @Override
+        public void onRegistrationFailure(int causeCode) {
+            // TO-DO notify registrants
+        }
+
+        private boolean notifyResultIfExpectedListener() {
+            // Demo listener should notify results only during demo mode
+            // Vendor listener should notify result only during real mode
+            return mIsDemoListener == mSatelliteController.isDemoModeEnabled();
+        }
+
+        private boolean notifyModemStateChanged(int state) {
+            if (notifyResultIfExpectedListener()) {
+                return true;
+            }
+
+            return state == SatelliteModemState.SATELLITE_MODEM_STATE_OFF
+                    || state == SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE;
+        }
+    }
 
     /**
      * @return The singleton instance of SatelliteModemInterface.
@@ -181,13 +226,15 @@
      * Create the SatelliteModemInterface singleton instance.
      * @param context The Context to use to create the SatelliteModemInterface.
      * @param satelliteController The singleton instance of SatelliteController.
+     * @param featureFlags The telephony feature flags.
      * @return The singleton instance of SatelliteModemInterface.
      */
     public static SatelliteModemInterface make(@NonNull Context context,
-            SatelliteController satelliteController) {
+            SatelliteController satelliteController,
+            @NonNull FeatureFlags featureFlags) {
         if (sInstance == null) {
             sInstance = new SatelliteModemInterface(
-                    context, satelliteController, Looper.getMainLooper());
+                    context, satelliteController, Looper.getMainLooper(), featureFlags);
         }
         return sInstance;
     }
@@ -196,12 +243,22 @@
      * Create a SatelliteModemInterface to manage connections to the SatelliteService.
      *
      * @param context The Context for the SatelliteModemInterface.
+     * @param featureFlags The telephony feature flags.
      * @param looper The Looper to run binding retry on.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected SatelliteModemInterface(@NonNull Context context,
-            SatelliteController satelliteController, @NonNull Looper looper) {
+            SatelliteController satelliteController,
+            @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags) {
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
         mContext = context;
+        mDemoSimulator = DemoSimulator.make(context, satelliteController);
+        mVendorListener = new SatelliteListener(false);
+        mDemoListener = new SatelliteListener(true);
         mIsSatelliteServiceSupported = getSatelliteServiceSupport();
         mSatelliteController = satelliteController;
         mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
@@ -221,7 +278,7 @@
             bindService();
         });
         mExponentialBackoff.start();
-        logd("Created SatelliteModemInterface. Attempting to bind to SatelliteService.");
+        plogd("Created SatelliteModemInterface. Attempting to bind to SatelliteService.");
         bindService();
     }
 
@@ -254,7 +311,7 @@
         }
         String packageName = getSatellitePackageName();
         if (TextUtils.isEmpty(packageName)) {
-            loge("Unable to bind to the satellite service because the package is undefined.");
+            ploge("Unable to bind to the satellite service because the package is undefined.");
             // Since the package name comes from static device configs, stop retry because
             // rebind will continue to fail without a valid package name.
             synchronized (mLock) {
@@ -267,18 +324,18 @@
         intent.setPackage(packageName);
 
         mSatelliteServiceConnection = new SatelliteServiceConnection();
-        logd("Binding to " + packageName);
+        plogd("Binding to " + packageName);
         try {
             boolean success = mContext.bindService(
                     intent, mSatelliteServiceConnection, Context.BIND_AUTO_CREATE);
             if (success) {
-                logd("Successfully bound to the satellite service.");
+                plogd("Successfully bound to the satellite service.");
             } else {
                 synchronized (mLock) {
                     mIsBinding = false;
                 }
                 mExponentialBackoff.notifyFailed();
-                loge("Error binding to the satellite service. Retrying in "
+                ploge("Error binding to the satellite service. Retrying in "
                         + mExponentialBackoff.getCurrentDelay() + " ms.");
             }
         } catch (Exception e) {
@@ -286,7 +343,7 @@
                 mIsBinding = false;
             }
             mExponentialBackoff.notifyFailed();
-            loge("Exception binding to the satellite service. Retrying in "
+            ploge("Exception binding to the satellite service. Retrying in "
                     + mExponentialBackoff.getCurrentDelay() + " ms. Exception: " + e);
         }
     }
@@ -306,7 +363,7 @@
     private class SatelliteServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            logd("onServiceConnected: ComponentName=" + name);
+            plogd("onServiceConnected: ComponentName=" + name);
             synchronized (mLock) {
                 mIsBound = true;
                 mIsBinding = false;
@@ -314,17 +371,18 @@
             mSatelliteService = ISatellite.Stub.asInterface(service);
             mExponentialBackoff.stop();
             try {
-                mSatelliteService.setSatelliteListener(mListener);
+                mSatelliteService.setSatelliteListener(mVendorListener);
+                mDemoSimulator.setSatelliteListener(mDemoListener);
             } catch (RemoteException e) {
                 // TODO: Retry setSatelliteListener
-                logd("setSatelliteListener: RemoteException " + e);
+                plogd("setSatelliteListener: RemoteException " + e);
             }
             mSatelliteController.onSatelliteServiceConnected();
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            loge("onServiceDisconnected: Waiting for reconnect.");
+            ploge("onServiceDisconnected: Waiting for reconnect.");
             synchronized (mLock) {
                 mIsBinding = false;
             }
@@ -334,7 +392,7 @@
 
         @Override
         public void onBindingDied(ComponentName name) {
-            loge("onBindingDied: Unbinding and rebinding service.");
+            ploge("onBindingDied: Unbinding and rebinding service.");
             synchronized (mLock) {
                 mIsBound = false;
                 mIsBinding = false;
@@ -550,7 +608,7 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("requestSatelliteListeningEnabled: " + error);
+                                plogd("requestSatelliteListeningEnabled: " + error);
                                 Binder.withCleanCallingIdentity(() -> {
                                     if (message != null) {
                                         sendMessageWithResult(message, null, error);
@@ -559,14 +617,14 @@
                             }
                         });
             } catch (RemoteException e) {
-                loge("requestSatelliteListeningEnabled: RemoteException " + e);
+                ploge("requestSatelliteListeningEnabled: RemoteException " + e);
                 if (message != null) {
                     sendMessageWithResult(
                             message, null, SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
                 }
             }
         } else {
-            loge("requestSatelliteListeningEnabled: Satellite service is unavailable.");
+            ploge("requestSatelliteListeningEnabled: Satellite service is unavailable.");
             if (message != null) {
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
@@ -584,28 +642,35 @@
             @Nullable Message message) {
         if (mSatelliteService != null) {
             try {
-                mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(enabled,
-                        new IIntegerConsumer.Stub() {
-                            @Override
-                            public void accept(int result) {
-                                int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("enableCellularModemWhileSatelliteModeIsOn: " + error);
-                                Binder.withCleanCallingIdentity(() -> {
-                                        if (message != null) {
-                                            sendMessageWithResult(message, null, error);
-                                        }
-                                });
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        int error = SatelliteServiceUtils.fromSatelliteError(result);
+                        plogd("enableCellularModemWhileSatelliteModeIsOn: " + error);
+                        Binder.withCleanCallingIdentity(() -> {
+                            if (message != null) {
+                                sendMessageWithResult(message, null, error);
                             }
                         });
+                    }
+                };
+
+                if (mSatelliteController.isDemoModeEnabled()) {
+                    mDemoSimulator.enableCellularModemWhileSatelliteModeIsOn(
+                            enabled, errorCallback);
+                } else {
+                    mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(
+                            enabled, errorCallback);
+                }
             } catch (RemoteException e) {
-                loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
+                ploge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
                 if (message != null) {
                     sendMessageWithResult(
                             message, null, SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
                 }
             }
         } else {
-            loge("enableCellularModemWhileSatelliteModeIsOn: Satellite service is unavailable.");
+            ploge("enableCellularModemWhileSatelliteModeIsOn: Satellite service is unavailable.");
             if (message != null) {
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
@@ -631,18 +696,18 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("setSatelliteEnabled: " + error);
+                                plogd("setSatelliteEnabled: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("setSatelliteEnabled: RemoteException " + e);
+                ploge("setSatelliteEnabled: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("setSatelliteEnabled: Satellite service is unavailable.");
+            ploge("setSatelliteEnabled: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -660,7 +725,7 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("requestIsSatelliteEnabled: " + error);
+                        plogd("requestIsSatelliteEnabled: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
@@ -670,18 +735,18 @@
                         // Convert for compatibility with SatelliteResponse
                         // TODO: This should just report result instead.
                         int[] enabled = new int[] {result ? 1 : 0};
-                        logd("requestIsSatelliteEnabled: " + Arrays.toString(enabled));
+                        plogd("requestIsSatelliteEnabled: " + Arrays.toString(enabled));
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                 message, enabled, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
-                loge("requestIsSatelliteEnabled: RemoteException " + e);
+                ploge("requestIsSatelliteEnabled: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestIsSatelliteEnabled: Satellite service is unavailable.");
+            ploge("requestIsSatelliteEnabled: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -699,25 +764,25 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("requestIsSatelliteSupported: " + error);
+                        plogd("requestIsSatelliteSupported: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 }, new IBooleanConsumer.Stub() {
                     @Override
                     public void accept(boolean result) {
-                        logd("requestIsSatelliteSupported: " + result);
+                        plogd("requestIsSatelliteSupported: " + result);
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                 message, result, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
-                loge("requestIsSatelliteSupported: RemoteException " + e);
+                ploge("requestIsSatelliteSupported: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestIsSatelliteSupported: Satellite service is unavailable.");
+            ploge("requestIsSatelliteSupported: Satellite service is unavailable.");
             sendMessageWithResult(
                     message, null, SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -735,7 +800,7 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("requestSatelliteCapabilities: " + error);
+                        plogd("requestSatelliteCapabilities: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
@@ -745,18 +810,18 @@
                             result) {
                         SatelliteCapabilities capabilities =
                                 SatelliteServiceUtils.fromSatelliteCapabilities(result);
-                        logd("requestSatelliteCapabilities: " + capabilities);
+                        plogd("requestSatelliteCapabilities: " + capabilities);
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                 message, capabilities, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
-                loge("requestSatelliteCapabilities: RemoteException " + e);
+                ploge("requestSatelliteCapabilities: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestSatelliteCapabilities: Satellite service is unavailable.");
+            ploge("requestSatelliteCapabilities: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -776,18 +841,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("startSendingSatellitePointingInfo: " + error);
+                        plogd("startSendingSatellitePointingInfo: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("startSendingSatellitePointingInfo: RemoteException " + e);
+                ploge("startSendingSatellitePointingInfo: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("startSendingSatellitePointingInfo: Satellite service is unavailable.");
+            ploge("startSendingSatellitePointingInfo: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -806,18 +871,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("stopSendingSatellitePointingInfo: " + error);
+                        plogd("stopSendingSatellitePointingInfo: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("stopSendingSatellitePointingInfo: RemoteException " + e);
+                ploge("stopSendingSatellitePointingInfo: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("stopSendingSatellitePointingInfo: Satellite service is unavailable.");
+            ploge("stopSendingSatellitePointingInfo: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -842,18 +907,18 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("provisionSatelliteService: " + error);
+                                plogd("provisionSatelliteService: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
                         });
             } catch (RemoteException e) {
-                loge("provisionSatelliteService: RemoteException " + e);
+                ploge("provisionSatelliteService: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("provisionSatelliteService: Satellite service is unavailable.");
+            ploge("provisionSatelliteService: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -874,18 +939,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("deprovisionSatelliteService: " + error);
+                        plogd("deprovisionSatelliteService: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("deprovisionSatelliteService: RemoteException " + e);
+                ploge("deprovisionSatelliteService: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("deprovisionSatelliteService: Satellite service is unavailable.");
+            ploge("deprovisionSatelliteService: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -903,7 +968,7 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("requestIsSatelliteProvisioned: " + error);
+                        plogd("requestIsSatelliteProvisioned: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
@@ -913,18 +978,18 @@
                         // Convert for compatibility with SatelliteResponse
                         // TODO: This should just report result instead.
                         int[] provisioned = new int[] {result ? 1 : 0};
-                        logd("requestIsSatelliteProvisioned: " + Arrays.toString(provisioned));
+                        plogd("requestIsSatelliteProvisioned: " + Arrays.toString(provisioned));
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                 message, provisioned, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
-                loge("requestIsSatelliteProvisioned: RemoteException " + e);
+                ploge("requestIsSatelliteProvisioned: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestIsSatelliteProvisioned: Satellite service is unavailable.");
+            ploge("requestIsSatelliteProvisioned: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -944,18 +1009,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("pollPendingDatagrams: " + error);
+                        plogd("pollPendingDatagrams: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("pollPendingDatagrams: RemoteException " + e);
+                ploge("pollPendingDatagrams: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("pollPendingDatagrams: Satellite service is unavailable.");
+            ploge("pollPendingDatagrams: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -980,18 +1045,18 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("sendDatagram: " + error);
+                                plogd("sendDatagram: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
                         });
             } catch (RemoteException e) {
-                loge("sendDatagram: RemoteException " + e);
+                ploge("sendDatagram: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("sendDatagram: Satellite service is unavailable.");
+            ploge("sendDatagram: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1011,7 +1076,7 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("requestSatelliteModemState: " + error);
+                        plogd("requestSatelliteModemState: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
@@ -1020,18 +1085,18 @@
                     public void accept(int result) {
                         // Convert SatelliteModemState from service to frameworks definition.
                         int modemState = SatelliteServiceUtils.fromSatelliteModemState(result);
-                        logd("requestSatelliteModemState: " + modemState);
+                        plogd("requestSatelliteModemState: " + modemState);
                         Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                 message, modemState, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                     }
                 });
             } catch (RemoteException e) {
-                loge("requestSatelliteModemState: RemoteException " + e);
+                ploge("requestSatelliteModemState: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestSatelliteModemState: Satellite service is unavailable.");
+            ploge("requestSatelliteModemState: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1052,7 +1117,7 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("requestTimeForNextSatelliteVisibility: " + error);
+                                plogd("requestTimeForNextSatelliteVisibility: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
@@ -1062,19 +1127,19 @@
                                 // Convert for compatibility with SatelliteResponse
                                 // TODO: This should just report result instead.
                                 int[] time = new int[] {result};
-                                logd("requestTimeForNextSatelliteVisibility: "
+                                plogd("requestTimeForNextSatelliteVisibility: "
                                         + Arrays.toString(time));
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                         message, time, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                             }
                         });
             } catch (RemoteException e) {
-                loge("requestTimeForNextSatelliteVisibility: RemoteException " + e);
+                ploge("requestTimeForNextSatelliteVisibility: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestTimeForNextSatelliteVisibility: Satellite service is unavailable.");
+            ploge("requestTimeForNextSatelliteVisibility: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1107,18 +1172,18 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("setSatellitePlmn: " + error);
+                                plogd("setSatellitePlmn: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
                         });
             } catch (RemoteException e) {
-                loge("setSatellitePlmn: RemoteException " + e);
+                ploge("setSatellitePlmn: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("setSatellitePlmn: Satellite service is unavailable.");
+            ploge("setSatellitePlmn: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1142,18 +1207,18 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("requestSetSatelliteEnabledForCarrier: " + error);
+                                plogd("requestSetSatelliteEnabledForCarrier: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
                         });
             } catch (RemoteException e) {
-                loge("requestSetSatelliteEnabledForCarrier: RemoteException " + e);
+                ploge("requestSetSatelliteEnabledForCarrier: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestSetSatelliteEnabledForCarrier: Satellite service is unavailable.");
+            ploge("requestSetSatelliteEnabledForCarrier: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
         }
@@ -1175,7 +1240,7 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("requestIsSatelliteEnabledForCarrier: " + error);
+                                plogd("requestIsSatelliteEnabledForCarrier: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
@@ -1185,7 +1250,7 @@
                                 // Convert for compatibility with SatelliteResponse
                                 // TODO: This should just report result instead.
                                 int[] enabled = new int[] {result ? 1 : 0};
-                                logd("requestIsSatelliteEnabledForCarrier: "
+                                plogd("requestIsSatelliteEnabledForCarrier: "
                                         + Arrays.toString(enabled));
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                         message, enabled,
@@ -1193,12 +1258,12 @@
                             }
                         });
             } catch (RemoteException e) {
-                loge("requestIsSatelliteEnabledForCarrier: RemoteException " + e);
+                ploge("requestIsSatelliteEnabledForCarrier: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestIsSatelliteEnabledForCarrier: Satellite service is unavailable.");
+            ploge("requestIsSatelliteEnabledForCarrier: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1217,7 +1282,7 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("requestNtnSignalStrength: " + error);
+                                plogd("requestNtnSignalStrength: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
@@ -1227,19 +1292,19 @@
                                     android.telephony.satellite.stub.NtnSignalStrength result) {
                                 NtnSignalStrength ntnSignalStrength =
                                         SatelliteServiceUtils.fromNtnSignalStrength(result);
-                                logd("requestNtnSignalStrength: " + ntnSignalStrength);
+                                plogd("requestNtnSignalStrength: " + ntnSignalStrength);
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                         message, ntnSignalStrength,
                                         SatelliteManager.SATELLITE_RESULT_SUCCESS));
                             }
                         });
             } catch (RemoteException e) {
-                loge("requestNtnSignalStrength: RemoteException " + e);
+                ploge("requestNtnSignalStrength: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestNtnSignalStrength: Satellite service is unavailable.");
+            ploge("requestNtnSignalStrength: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1258,18 +1323,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("startSendingNtnSignalStrength: " + error);
+                        plogd("startSendingNtnSignalStrength: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("startSendingNtnSignalStrength: RemoteException " + e);
+                ploge("startSendingNtnSignalStrength: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("startSendingNtnSignalStrength: Satellite service is unavailable.");
+            ploge("startSendingNtnSignalStrength: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1287,18 +1352,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("stopSendingNtnSignalStrength: " + error);
+                        plogd("stopSendingNtnSignalStrength: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("stopSendingNtnSignalStrength: RemoteException " + e);
+                ploge("stopSendingNtnSignalStrength: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("stopSendingNtnSignalStrength: Satellite service is unavailable.");
+            ploge("stopSendingNtnSignalStrength: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1316,18 +1381,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("abortSendingSatelliteDatagrams: " + error);
+                        plogd("abortSendingSatelliteDatagrams: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("abortSendingSatelliteDatagrams: RemoteException " + e);
+                ploge("abortSendingSatelliteDatagrams: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("abortSendingSatelliteDatagrams: Satellite service is unavailable.");
+            ploge("abortSendingSatelliteDatagrams: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1353,7 +1418,7 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public void setSatelliteServicePackageName(@Nullable String servicePackageName) {
-        logd("setSatelliteServicePackageName: config_satellite_service_package is "
+        plogd("setSatelliteServicePackageName: config_satellite_service_package is "
                 + "updated, new packageName=" + servicePackageName);
         mExponentialBackoff.stop();
         if (mSatelliteServiceConnection != null) {
@@ -1390,4 +1455,31 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index 76e5661..3fe2970 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -24,6 +24,7 @@
 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 com.android.internal.telephony.flags.Flags.satellitePersistentLogging;
 import static com.android.internal.telephony.satellite.SatelliteController.INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
 
 import android.annotation.NonNull;
@@ -42,6 +43,10 @@
 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;
@@ -65,6 +70,7 @@
 import com.android.internal.telephony.SmsApplication;
 import com.android.internal.telephony.metrics.SatelliteStats;
 
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 
@@ -109,13 +115,16 @@
     protected int mCountOfTimerStarted = 0;
     private final Object mLock = new Object();
 
+    @Nullable private PersistentLogger mPersistentLogger = null;
+
     /**
      * Create an instance of SatelliteSOSMessageRecommender.
      *
      * @param context The Context for the SatelliteSOSMessageRecommender.
      * @param looper The looper used with the handler of this class.
      */
-    public SatelliteSOSMessageRecommender(@NonNull Context context, @NonNull Looper looper) {
+    public SatelliteSOSMessageRecommender(@NonNull Context context,
+            @NonNull Looper looper) {
         this(context, looper, SatelliteController.getInstance(), null);
     }
 
@@ -131,8 +140,13 @@
      */
     @VisibleForTesting
     protected SatelliteSOSMessageRecommender(@NonNull Context context, @NonNull Looper looper,
-            @NonNull SatelliteController satelliteController, ImsManager imsManager) {
+            @NonNull SatelliteController satelliteController,
+            ImsManager imsManager) {
         super(looper);
+        if (isSatellitePersistentLoggingEnabled(context)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
         mContext = context;
         mSatelliteController = satelliteController;
         mImsManager = imsManager;
@@ -141,7 +155,7 @@
         mISatelliteProvisionStateCallback = new ISatelliteProvisionStateCallback.Stub() {
             @Override
             public void onSatelliteProvisionStateChanged(boolean provisioned) {
-                logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
+                plogd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
                 sendMessage(obtainMessage(EVENT_SATELLITE_PROVISIONED_STATE_CHANGED, provisioned));
             }
         };
@@ -172,7 +186,7 @@
                 handleSatelliteAccessRestrictionCheckingResult((boolean) msg.obj);
                 break;
             default:
-                logd("handleMessage: unexpected message code: " + msg.what);
+                plogd("handleMessage: unexpected message code: " + msg.what);
                 break;
         }
     }
@@ -184,9 +198,8 @@
      *                   call.
      */
     public void onEmergencyCallStarted(@NonNull Connection connection) {
-        if (!mSatelliteController.isSatelliteSupportedViaOem()
-                && !mSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier()) {
-            logd("onEmergencyCallStarted: satellite is not supported");
+        if (!isSatelliteSupported()) {
+            plogd("onEmergencyCallStarted: satellite is not supported");
             return;
         }
 
@@ -215,10 +228,9 @@
      */
     public void onEmergencyCallConnectionStateChanged(
             String callId, @Connection.ConnectionState int state) {
-        logd("callId=" + callId + ", state=" + state);
-        if (!mSatelliteController.isSatelliteSupportedViaOem()
-                && !mSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier()) {
-            logd("onEmergencyCallConnectionStateChanged: satellite is not supported");
+        plogd("callId=" + callId + ", state=" + state);
+        if (!isSatelliteSupported()) {
+            plogd("onEmergencyCallConnectionStateChanged: satellite is not supported");
             return;
         }
         Pair<String, Integer> argument = new Pair<>(callId, state);
@@ -231,6 +243,8 @@
     }
 
     private void handleEmergencyCallStartedEvent(@NonNull Connection connection) {
+        mSatelliteController.setLastEmergencyCallTime();
+
         if (sendEventDisplayEmergencyMessageForcefully(connection)) {
             return;
         }
@@ -263,12 +277,12 @@
     private void evaluateSendingConnectionEventDisplayEmergencyMessage() {
         synchronized (mLock) {
             if (mEmergencyConnection == null) {
-                loge("No emergency call is ongoing...");
+                ploge("No emergency call is ongoing...");
                 return;
             }
 
             if (!mIsTimerTimedOut || mCheckingAccessRestrictionInProgress) {
-                logd("mIsTimerTimedOut=" + mIsTimerTimedOut
+                plogd("mIsTimerTimedOut=" + mIsTimerTimedOut
                         + ", mCheckingAccessRestrictionInProgress="
                         + mCheckingAccessRestrictionInProgress);
                 return;
@@ -286,14 +300,14 @@
                     && isSatelliteAllowed()
                     && (isSatelliteViaOemAvailable() || isSatelliteViaCarrierAvailable())
                     && shouldTrackCall(mEmergencyConnection.getState())) {
-                logd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer");
+                plogd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer");
                 Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
                 mEmergencyConnection.sendConnectionEvent(
                         TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, extras);
                 isDialerNotified = true;
 
             }
-            logd("handleTimeoutEvent: isImsRegistered=" + isImsRegistered()
+            plogd("handleTimeoutEvent: isImsRegistered=" + isImsRegistered()
                     + ", isCellularAvailable=" + isCellularAvailable()
                     + ", isSatelliteAllowed=" + isSatelliteAllowed()
                     + ", shouldTrackCall=" + shouldTrackCall(mEmergencyConnection.getState()));
@@ -332,6 +346,7 @@
 
     private void handleEmergencyCallConnectionStateChangedEvent(
             @NonNull Pair<String, Integer> arg) {
+        mSatelliteController.setLastEmergencyCallTime();
         if (mEmergencyConnection == null) {
             // Either the call was not created or the timer already timed out.
             return;
@@ -340,7 +355,7 @@
         String callId = arg.first;
         int state = arg.second;
         if (!mEmergencyConnection.getTelecomCallId().equals(callId)) {
-            loge("handleEmergencyCallConnectionStateChangedEvent: unexpected state changed event "
+            ploge("handleEmergencyCallConnectionStateChangedEvent: unexpected state changed event "
                     + ", mEmergencyConnection=" + mEmergencyConnection + ", callId=" + callId
                     + ", state=" + state);
             /*
@@ -409,7 +424,7 @@
             imsManager.addRegistrationCallback(
                     getOrCreateImsRegistrationCallback(phone.getPhoneId()), this::post);
         } catch (ImsException ex) {
-            loge("registerForImsRegistrationStateChanged: ex=" + ex);
+            ploge("registerForImsRegistrationStateChanged: ex=" + ex);
         }
     }
 
@@ -430,7 +445,7 @@
             imsManager.removeRegistrationListener(
                     mImsRegistrationCallbacks.get(phone.getPhoneId()));
         } else {
-            loge("Phone ID=" + phone.getPhoneId() + " was not registered with ImsManager");
+            ploge("Phone ID=" + phone.getPhoneId() + " was not registered with ImsManager");
         }
     }
 
@@ -439,8 +454,9 @@
             ServiceState serviceState = phone.getServiceState();
             if (serviceState != null) {
                 int state = serviceState.getState();
-                if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY)
-                        && !serviceState.isUsingNonTerrestrialNetwork()) {
+                if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY
+                        || serviceState.isEmergencyOnly())
+                        && !isSatellitePlmn(phone.getSubId(), serviceState)) {
                     logv("isCellularAvailable true");
                     return true;
                 }
@@ -450,6 +466,34 @@
         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
@@ -522,7 +566,7 @@
         } else {
             mTimeoutMillis = mOemEnabledTimeoutMillis;
         }
-        logd("mTimeoutMillis = " + mTimeoutMillis);
+        plogd("mTimeoutMillis = " + mTimeoutMillis);
     }
 
     private static long getOemEnabledEmergencyCallWaitForConnectionTimeoutMillis(
@@ -630,7 +674,7 @@
             packageName = defaultSmsAppComponent.getPackageName();
             className = defaultSmsAppComponent.getClassName();
         }
-        logd("EVENT_DISPLAY_EMERGENCY_MESSAGE: handoverType=" + handoverType + ", packageName="
+        plogd("EVENT_DISPLAY_EMERGENCY_MESSAGE: handoverType=" + handoverType + ", packageName="
                 + packageName + ", className=" + className + ", action=" + action);
 
         Bundle result = new Bundle();
@@ -651,7 +695,7 @@
             if (mEmergencyConnection != null) {
                 emergencyNumber = mEmergencyConnection.getAddress().getSchemeSpecificPart();
             }
-            logd("emergencyNumber=" + emergencyNumber);
+            plogd("emergencyNumber=" + emergencyNumber);
 
             Uri uri = Uri.parse("smsto:" + emergencyNumber);
             intent = new Intent(Intent.ACTION_SENDTO, uri);
@@ -683,7 +727,7 @@
 
     private void handleCmdSendEventDisplayEmergencyMessageForcefully(
             @NonNull Connection connection) {
-        logd("Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer forcefully.");
+        plogd("Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer forcefully.");
         mEmergencyConnection = connection;
         Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
         connection.sendConnectionEvent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, extras);
@@ -693,7 +737,7 @@
     private boolean isMultiSim() {
         TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
         if (telephonyManager == null) {
-            loge("isMultiSim: telephonyManager is null");
+            ploge("isMultiSim: telephonyManager is null");
             return false;
         }
         return telephonyManager.isMultiSimEnabled();
@@ -710,7 +754,7 @@
     private void requestIsSatelliteAllowedForCurrentLocation() {
         synchronized (mLock) {
             if (mCheckingAccessRestrictionInProgress) {
-                logd("requestIsSatelliteCommunicationAllowedForCurrentLocation was already sent");
+                plogd("requestIsSatelliteCommunicationAllowedForCurrentLocation was already sent");
                 return;
             }
             mCheckingAccessRestrictionInProgress = true;
@@ -720,14 +764,14 @@
                 new OutcomeReceiver<>() {
                     @Override
                     public void onResult(Boolean result) {
-                        logd("requestIsSatelliteAllowedForCurrentLocation: result=" + result);
+                        plogd("requestIsSatelliteAllowedForCurrentLocation: result=" + result);
                         sendMessage(obtainMessage(
                                 EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT, result));
                     }
 
                     @Override
                     public void onError(SatelliteManager.SatelliteException ex) {
-                        logd("requestIsSatelliteAllowedForCurrentLocation: onError, ex=" + ex);
+                        plogd("requestIsSatelliteAllowedForCurrentLocation: onError, ex=" + ex);
                         sendMessage(obtainMessage(
                                 EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT, false));
                     }
@@ -748,6 +792,19 @@
                 || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
     }
 
+    private boolean isSatelliteSupported() {
+        if (mSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier()) return true;
+        if (mSatelliteController.isSatelliteSupportedViaOem() && isSatelliteViaOemProvisioned()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isSatelliteViaOemProvisioned() {
+        Boolean provisioned = mSatelliteController.isSatelliteViaOemProvisioned();
+        return (provisioned != null) && provisioned;
+    }
+
     private static void logv(@NonNull String log) {
         Rlog.v(TAG, log);
     }
@@ -759,4 +816,31 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context) {
+        if (satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        Rlog.d(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        Rlog.e(TAG, log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index 67b2586..d33fd73 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -347,7 +347,9 @@
     }
 
     /**
-     *
+     * Check if the datagramType is the sos message (DATAGRAM_TYPE_SOS_MESSAGE,
+     * DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP,
+     * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED) or not
      */
     public static boolean isSosMessage(int datagramType) {
         return datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE
@@ -356,6 +358,16 @@
     }
 
     /**
+     * Check if the datagramType is the last sos message
+     * (DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP or
+     * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED) or not
+     */
+    public static boolean isLastSosMessage(int datagramType) {
+        return datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP
+                || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED;
+    }
+
+    /**
      * Return phone associated with phoneId 0.
      *
      * @return phone associated with phoneId 0 or {@code null} if it doesn't exist.
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index cd3c05b..dde10a0 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -39,6 +39,8 @@
 import android.os.Message;
 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;
@@ -51,6 +53,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
@@ -142,6 +145,8 @@
     @NonNull private boolean mIsDisableCellularModemInProgress = false;
     @NonNull private final SatelliteController mSatelliteController;
     @NonNull private final DatagramController mDatagramController;
+    @Nullable private PersistentLogger mPersistentLogger = null;
+
 
     /**
      * @return The singleton instance of SatelliteSessionController.
@@ -158,20 +163,22 @@
      *
      * @param context The Context for the SatelliteSessionController.
      * @param looper The looper associated with the handler of this class.
+     * @param featureFlags The telephony feature flags.
      * @param isSatelliteSupported Whether satellite is supported on the device.
      * @return The singleton instance of SatelliteSessionController.
      */
     public static SatelliteSessionController make(
-            @NonNull Context context, @NonNull Looper looper, boolean isSatelliteSupported) {
-        if (sInstance == null) {
-            sInstance = new SatelliteSessionController(context, looper, isSatelliteSupported,
+            @NonNull Context context,
+            @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
+            boolean isSatelliteSupported) {
+        if (sInstance == null || isSatelliteSupported != sInstance.mIsSatelliteSupported) {
+            sInstance = new SatelliteSessionController(
+                    context,
+                    looper,
+                    featureFlags,
+                    isSatelliteSupported,
                     SatelliteModemInterface.getInstance());
-        } else {
-            if (isSatelliteSupported != sInstance.mIsSatelliteSupported) {
-                Rlog.e(TAG, "New satellite support state " + isSatelliteSupported
-                        + " is different from existing state " + sInstance.mIsSatelliteSupported
-                        + ". Ignore the new state.");
-            }
         }
         return sInstance;
     }
@@ -181,15 +188,22 @@
      *
      * @param context The Context for the SatelliteSessionController.
      * @param looper The looper associated with the handler of this class.
+     * @param featureFlags The telephony feature flags.
      * @param isSatelliteSupported Whether satellite is supported on the device.
      * @param satelliteModemInterface The singleton of SatelliteModemInterface.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected SatelliteSessionController(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             boolean isSatelliteSupported,
             @NonNull SatelliteModemInterface satelliteModemInterface) {
         super(TAG, looper);
 
+        if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
+            mPersistentLogger = new PersistentLogger(
+                    DropBoxManagerLoggerBackend.getInstance(context));
+        }
+
         mContext = context;
         mSatelliteModemInterface = satelliteModemInterface;
         mSatelliteController = SatelliteController.getInstance();
@@ -304,7 +318,7 @@
             callback.onSatelliteModemStateChanged(mCurrentState);
             mListeners.put(callback.asBinder(), callback);
         } catch (RemoteException ex) {
-            loge("registerForSatelliteModemStateChanged: Got RemoteException ex=" + ex);
+            ploge("registerForSatelliteModemStateChanged: Got RemoteException ex=" + ex);
         }
     }
 
@@ -330,11 +344,11 @@
      */
     boolean setSatelliteListeningTimeoutDuration(long timeoutMillis) {
         if (!isMockModemAllowed()) {
-            loge("Updating listening timeout duration is not allowed");
+            ploge("Updating listening timeout duration is not allowed");
             return false;
         }
 
-        logd("setSatelliteListeningTimeoutDuration: timeoutMillis=" + timeoutMillis);
+        plogd("setSatelliteListeningTimeoutDuration: timeoutMillis=" + timeoutMillis);
         if (timeoutMillis == 0) {
             mSatelliteStayAtListeningFromSendingMillis =
                     getSatelliteStayAtListeningFromSendingMillis();
@@ -360,12 +374,12 @@
      */
     boolean setSatelliteGatewayServicePackageName(@Nullable String servicePackageName) {
         if (!isMockModemAllowed()) {
-            loge("setSatelliteGatewayServicePackageName: modifying satellite gateway service "
+            ploge("setSatelliteGatewayServicePackageName: modifying satellite gateway service "
                     + "package name is not allowed");
             return false;
         }
 
-        logd("setSatelliteGatewayServicePackageName: config_satellite_gateway_service_package is "
+        plogd("setSatelliteGatewayServicePackageName: config_satellite_gateway_service_package is "
                 + "updated, new packageName=" + servicePackageName);
 
         if (servicePackageName == null || servicePackageName.equals("null")) {
@@ -404,7 +418,7 @@
      * @return {@code true} if state machine is in enabling state and {@code false} otherwise.
      */
     public boolean isInEnablingState() {
-        if (DBG) logd("isInEnablingState: getCurrentState=" + getCurrentState());
+        if (DBG) plogd("isInEnablingState: getCurrentState=" + getCurrentState());
         return getCurrentState() == mEnablingState;
     }
 
@@ -426,14 +440,14 @@
     private class UnavailableState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering UnavailableState");
+            if (DBG) plogd("Entering UnavailableState");
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE);
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            loge("UnavailableState: receive msg " + getWhatToString(msg.what) + " unexpectedly");
+            ploge("UnavailableState: receive msg " + getWhatToString(msg.what) + " unexpectedly");
             return HANDLED;
         }
     }
@@ -441,7 +455,7 @@
     private class PowerOffState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering PowerOffState");
+            if (DBG) plogd("Entering PowerOffState");
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
             mIsSendingTriggeredDuringTransferringState.set(false);
@@ -450,19 +464,20 @@
             }
             unbindService();
             stopNbIotInactivityTimer();
+            DemoSimulator.getInstance().onSatelliteModeOff();
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
         }
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting PowerOffState");
-            logd("Attempting to bind to SatelliteGatewayService.");
+            if (DBG) plogd("Exiting PowerOffState");
+            plogd("Attempting to bind to SatelliteGatewayService.");
             bindService();
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("PowerOffState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("PowerOffState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
@@ -476,7 +491,7 @@
             if (enabled) {
                 transitionTo(mEnablingState);
             } else {
-                logw("Unexpected satellite disablement started in PowerOff state");
+                plogw("Unexpected satellite disablement started in PowerOff state");
             }
         }
     }
@@ -484,7 +499,7 @@
     private class EnablingState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering EnablingState");
+            if (DBG) plogd("Entering EnablingState");
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE);
@@ -492,12 +507,12 @@
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting EnablingState");
+            if (DBG) plogd("Exiting EnablingState");
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("EnablingState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("EnablingState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged((boolean) msg.obj);
@@ -520,6 +535,7 @@
                 } else {
                     transitionTo(mIdleState);
                 }
+                DemoSimulator.getInstance().onSatelliteModeOn();
             } else {
                 /*
                  * During the state transition from ENABLING to NOT_CONNECTED, modem might be
@@ -536,7 +552,7 @@
     private class DisablingState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering DisablingState");
+            if (DBG) plogd("Entering DisablingState");
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_DISABLING_SATELLITE;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_DISABLING_SATELLITE);
@@ -544,12 +560,12 @@
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting DisablingState");
+            if (DBG) plogd("Exiting DisablingState");
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("DisablingState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("DisablingState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged((boolean) msg.obj);
@@ -561,7 +577,7 @@
 
         private void handleSatelliteEnabledStateChanged(boolean on) {
             if (on) {
-                logw("Unexpected power on event while disabling satellite");
+                plogw("Unexpected power on event while disabling satellite");
             } else {
                 transitionTo(mPowerOffState);
             }
@@ -571,7 +587,7 @@
     private class IdleState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering IdleState");
+            if (DBG) plogd("Entering IdleState");
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
             mIsSendingTriggeredDuringTransferringState.set(false);
             stopNbIotInactivityTimer();
@@ -582,7 +598,7 @@
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("IdleState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("IdleState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
                     handleEventDatagramTransferStateChanged((DatagramTransferState) msg.obj);
@@ -608,7 +624,7 @@
                     || (datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING)) {
                 if (mSatelliteController.isSatelliteAttachRequired()) {
-                    loge("Unexpected transferring state received for NB-IOT NTN");
+                    ploge("Unexpected transferring state received for NB-IOT NTN");
                 } else {
                     transitionTo(mTransferringState);
                 }
@@ -619,7 +635,7 @@
                 if (mSatelliteController.isSatelliteAttachRequired()) {
                     disableCellularModemWhileSatelliteModeIsOn();
                 } else {
-                    loge("Unexpected transferring state received for non-NB-IOT NTN");
+                    ploge("Unexpected transferring state received for non-NB-IOT NTN");
                 }
             }
         }
@@ -635,7 +651,7 @@
                     }
                     mIsDisableCellularModemInProgress = false;
                 } else {
-                    loge("DisableCellularModemWhileSatelliteModeIsOn is not in progress");
+                    ploge("DisableCellularModemWhileSatelliteModeIsOn is not in progress");
                 }
             }
         }
@@ -643,7 +659,7 @@
         private void disableCellularModemWhileSatelliteModeIsOn() {
             synchronized (mLock) {
                 if (mIsDisableCellularModemInProgress) {
-                    logd("Cellular scanning is already being disabled");
+                    plogd("Cellular scanning is already being disabled");
                     return;
                 }
 
@@ -657,7 +673,7 @@
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting IdleState");
+            if (DBG) plogd("Exiting IdleState");
             if (!mSatelliteController.isSatelliteAttachRequired()) {
                 // Disable cellular modem scanning
                 mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null);
@@ -668,7 +684,7 @@
     private class TransferringState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering TransferringState");
+            if (DBG) plogd("Entering TransferringState");
             stopNbIotInactivityTimer();
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
@@ -676,7 +692,7 @@
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("TransferringState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("TransferringState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_DATAGRAM_TRANSFER_STATE_CHANGED:
                     handleEventDatagramTransferStateChanged((DatagramTransferState) msg.obj);
@@ -727,7 +743,7 @@
     private class ListeningState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering ListeningState");
+            if (DBG) plogd("Entering ListeningState");
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_LISTENING;
             long timeoutMillis = updateListeningMode(true);
@@ -738,7 +754,7 @@
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting ListeningState");
+            if (DBG) plogd("Exiting ListeningState");
 
             removeMessages(EVENT_LISTENING_TIMER_TIMEOUT);
             updateListeningMode(false);
@@ -746,7 +762,7 @@
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("ListeningState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("ListeningState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_LISTENING_TIMER_TIMEOUT:
                     transitionTo(mIdleState);
@@ -790,7 +806,7 @@
     private class NotConnectedState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering NotConnectedState");
+            if (DBG) plogd("Entering NotConnectedState");
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
@@ -799,12 +815,12 @@
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting NotConnectedState");
+            if (DBG) plogd("Exiting NotConnectedState");
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("NotConnectedState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("NotConnectedState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged(
@@ -855,7 +871,7 @@
     private class ConnectedState extends State {
         @Override
         public void enter() {
-            if (DBG) logd("Entering ConnectedState");
+            if (DBG) plogd("Entering ConnectedState");
 
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
@@ -864,12 +880,12 @@
 
         @Override
         public void exit() {
-            if (DBG) logd("Exiting ConnectedState");
+            if (DBG) plogd("Exiting ConnectedState");
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("ConnectedState: processing " + getWhatToString(msg.what));
+            if (DBG) plogd("ConnectedState: processing " + getWhatToString(msg.what));
             switch (msg.what) {
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged(
@@ -961,7 +977,7 @@
             try {
                 listener.onSatelliteModemStateChanged(state);
             } catch (RemoteException e) {
-                logd("notifyStateChangedEvent RemoteException: " + e);
+                plogd("notifyStateChangedEvent RemoteException: " + e);
                 toBeRemoved.add(listener);
             }
         });
@@ -975,7 +991,7 @@
         if (off) {
             transitionTo(mPowerOffState);
         } else {
-            loge(caller + ": Unexpected satellite radio powered-on state changed event");
+            ploge(caller + ": Unexpected satellite radio powered-on state changed event");
         }
     }
 
@@ -1008,7 +1024,7 @@
 
         String packageName = getSatelliteGatewayPackageName();
         if (TextUtils.isEmpty(packageName)) {
-            loge("Unable to bind to the satellite gateway service because the package is"
+            ploge("Unable to bind to the satellite gateway service because the package is"
                     + " undefined.");
             // Since the package name comes from static device configs, stop retry because
             // rebind will continue to fail without a valid package name.
@@ -1026,13 +1042,13 @@
             boolean success = mContext.bindService(
                     intent, mSatelliteGatewayServiceConnection, Context.BIND_AUTO_CREATE);
             if (success) {
-                logd("Successfully bound to the satellite gateway service.");
+                plogd("Successfully bound to the satellite gateway service.");
             } else {
                 synchronized (mLock) {
                     mIsBinding = false;
                 }
                 mExponentialBackoff.notifyFailed();
-                loge("Error binding to the satellite gateway service. Retrying in "
+                ploge("Error binding to the satellite gateway service. Retrying in "
                         + mExponentialBackoff.getCurrentDelay() + " ms.");
             }
         } catch (Exception e) {
@@ -1040,13 +1056,13 @@
                 mIsBinding = false;
             }
             mExponentialBackoff.notifyFailed();
-            loge("Exception binding to the satellite gateway service. Retrying in "
+            ploge("Exception binding to the satellite gateway service. Retrying in "
                     + mExponentialBackoff.getCurrentDelay() + " ms. Exception: " + e);
         }
     }
 
     private void unbindService() {
-        logd("unbindService");
+        plogd("unbindService");
         mExponentialBackoff.stop();
         mSatelliteGatewayService = null;
         synchronized (mLock) {
@@ -1061,7 +1077,7 @@
     private class SatelliteGatewayServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            logd("onServiceConnected: ComponentName=" + name);
+            plogd("onServiceConnected: ComponentName=" + name);
             synchronized (mLock) {
                 mIsBound = true;
                 mIsBinding = false;
@@ -1072,7 +1088,7 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            loge("onServiceDisconnected: Waiting for reconnect.");
+            ploge("onServiceDisconnected: Waiting for reconnect.");
             synchronized (mLock) {
                 mIsBinding = false;
                 mIsBound = false;
@@ -1082,7 +1098,7 @@
 
         @Override
         public void onBindingDied(ComponentName name) {
-            loge("onBindingDied: Unbinding and rebinding service.");
+            ploge("onBindingDied: Unbinding and rebinding service.");
             synchronized (mLock) {
                 mIsBound = false;
                 mIsBinding = false;
@@ -1137,7 +1153,7 @@
 
     private void startNbIotInactivityTimer() {
         if (isNbIotInactivityTimerStarted()) {
-            logd("NB IOT inactivity timer is already started");
+            plogd("NB IOT inactivity timer is already started");
             return;
         }
 
@@ -1157,4 +1173,38 @@
     private boolean isNbIotInactivityTimerStarted() {
         return hasMessages(EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
     }
+
+    private boolean isSatellitePersistentLoggingEnabled(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (featureFlags.satellitePersistentLogging()) {
+            return true;
+        }
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    private void plogd(@NonNull String log) {
+        logd(log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.debug(TAG, log);
+        }
+    }
+
+    private void plogw(@NonNull String log) {
+        logw(log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.warn(TAG, log);
+        }
+    }
+
+    private void ploge(@NonNull String log) {
+        loge(log);
+        if (mPersistentLogger != null) {
+            mPersistentLogger.error(TAG, log);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
new file mode 100644
index 0000000..13ba709
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
@@ -0,0 +1,192 @@
+/*
+ * 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.satellite.metrics;
+
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+
+import static com.android.internal.telephony.satellite.SatelliteConstants.CONFIG_DATA_SOURCE_UNKNOWN;
+
+import android.annotation.NonNull;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.SatelliteConstants;
+
+import java.util.Arrays;
+import java.util.List;
+public class AccessControllerMetricsStats {
+    private static final String TAG = AccessControllerMetricsStats.class.getSimpleName();
+    private static AccessControllerMetricsStats sInstance = null;
+
+    private @SatelliteConstants.AccessControlType int mAccessControlType;
+    private long mLocationQueryTimeMillis;
+    private long mOnDeviceLookupTimeMillis;
+    private long mTotalCheckingTimeMillis;
+    private Boolean mIsAllowed;
+    private Boolean mIsEmergency;
+    private @SatelliteManager.SatelliteResult int mResultCode;
+    private String[] mCountryCodes;
+    private @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+    private AccessControllerMetricsStats() {
+        initializeAccessControllerMetricsParam();
+    }
+
+    /**
+     * Returns the Singleton instance of AccessControllerMetricsStats class.
+     * If an instance of the Singleton class has not been created,
+     * it creates a new instance and returns it. Otherwise, it returns
+     * the existing instance.
+     * @return the Singleton instance of AccessControllerMetricsStats.
+     */
+    public static AccessControllerMetricsStats getInstance() {
+        if (sInstance == null) {
+            loge("create new AccessControllerMetricsStats.");
+            sInstance = new AccessControllerMetricsStats();
+        }
+        return sInstance;
+    }
+    private void initializeAccessControllerMetricsParam() {
+        mAccessControlType = SatelliteConstants.ACCESS_CONTROL_TYPE_UNKNOWN;
+        mLocationQueryTimeMillis = 0;
+        mOnDeviceLookupTimeMillis = 0;
+        mTotalCheckingTimeMillis = 0;
+        mIsAllowed = null;
+        mIsEmergency = null;
+        mResultCode = SATELLITE_RESULT_SUCCESS;
+        mCountryCodes = new String[0];
+        mConfigDataSource = CONFIG_DATA_SOURCE_UNKNOWN;
+    }
+    /**
+     * Sets the Access Control Type for current satellite enablement.
+     * @param accessControlType access control type of location query is attempted.
+     */
+    public AccessControllerMetricsStats setAccessControlType(
+            @SatelliteConstants.AccessControlType int accessControlType) {
+        mAccessControlType = accessControlType;
+        logd("setAccessControlType: access control type = " + mAccessControlType);
+        return this;
+    }
+    /**
+     * Sets the location query time for current satellite enablement.
+     * @param queryStartTime the time location query is attempted.
+     */
+    public AccessControllerMetricsStats setLocationQueryTime(long queryStartTime) {
+        mLocationQueryTimeMillis =
+                (queryStartTime > 0) ? (getCurrentTime() - queryStartTime) : 0;
+        logd("setLocationQueryTimeMillis: location query time = " + mLocationQueryTimeMillis);
+        return this;
+    }
+    /**
+     * Sets the on device lookup time for current satellite enablement.
+     * @param onDeviceLookupStartTime the time on device lookup is attempted.
+     */
+    public AccessControllerMetricsStats setOnDeviceLookupTime(long onDeviceLookupStartTime) {
+        mOnDeviceLookupTimeMillis =
+                (onDeviceLookupStartTime > 0) ? (getCurrentTime() - onDeviceLookupStartTime) : 0;
+        logd("setLocationQueryTime: on device lookup time = " + mOnDeviceLookupTimeMillis);
+        return this;
+    }
+    /**
+     * Sets the total checking time for current satellite enablement.
+     * @param queryStartTime the time location query is attempted.
+     */
+    public AccessControllerMetricsStats setTotalCheckingTime(long queryStartTime) {
+        mTotalCheckingTimeMillis =
+                (queryStartTime > 0) ? (getCurrentTime() - queryStartTime) : 0;
+        logd("setTotalCheckingTime: location query time = " + mTotalCheckingTimeMillis);
+        return this;
+    }
+    /**
+     * Sets whether the satellite communication is allowed from current location.
+     * @param isAllowed {@code true} if satellite communication is allowed from current location
+     *        {@code false} otherwise.
+     */
+    public AccessControllerMetricsStats setIsAllowed(boolean isAllowed) {
+        mIsAllowed = isAllowed;
+        logd("setIsAllowed: allowed=" + mIsAllowed);
+        return this;
+    }
+    /**
+     * Sets whether the current satellite enablement is for emergency or not.
+     * @param isEmergency {@code true} if current satellite enablement is for emergency SOS message
+     *        {@code false} otherwise.
+     */
+    public AccessControllerMetricsStats setIsEmergency(boolean isEmergency) {
+        mIsEmergency = isEmergency;
+        logd("setIsEmergency: emergency =" + mIsEmergency);
+        return this;
+    }
+    /**
+     * Sets the result code for checking whether satellite service is allowed from current
+     * location.
+     * @param result result code for checking process.
+     */
+    public AccessControllerMetricsStats setResult(@SatelliteManager.SatelliteResult int result) {
+        mResultCode = result;
+        logd("setResult: result = " + mResultCode);
+        return this;
+    }
+    /**
+     * Sets the country code for current location while attempting satellite enablement.
+     * @param countryCodes Country code the user is located in
+     */
+    public AccessControllerMetricsStats setCountryCodes(List<String> countryCodes) {
+        mCountryCodes = countryCodes.stream().toArray(String[]::new);
+        logd("setCountryCodes: country code is " + Arrays.toString(mCountryCodes));
+        return this;
+    }
+    /**
+     * Sets the config data source for checking whether satellite service is allowed from current
+     * location.
+     * @param configDatasource configuration data source.
+     */
+    public AccessControllerMetricsStats setConfigDataSource(
+            @SatelliteConstants.ConfigDataSource int configDatasource) {
+        mConfigDataSource = configDatasource;
+        logd("setConfigDataSource: config data source = " + mConfigDataSource);
+        return this;
+    }
+    /** Report the access controller metrics atoms to PersistAtomsStorage in telephony. */
+    public void reportAccessControllerMetrics() {
+        SatelliteStats.SatelliteAccessControllerParams accessControllerParams =
+                new SatelliteStats.SatelliteAccessControllerParams.Builder()
+                        .setAccessControlType(mAccessControlType)
+                        .setLocationQueryTime(mLocationQueryTimeMillis)
+                        .setOnDeviceLookupTime(mOnDeviceLookupTimeMillis)
+                        .setTotalCheckingTime(mTotalCheckingTimeMillis)
+                        .setIsAllowed(mIsAllowed)
+                        .setIsEmergency(mIsEmergency)
+                        .setResult(mResultCode)
+                        .setCountryCodes(mCountryCodes)
+                        .setConfigDatasource(mConfigDataSource)
+                        .build();
+        logd("reportAccessControllerMetrics: " + accessControllerParams.toString());
+        SatelliteStats.getInstance().onSatelliteAccessControllerMetrics(accessControllerParams);
+        initializeAccessControllerMetricsParam();
+    }
+    @VisibleForTesting
+    public long getCurrentTime() {
+        return System.currentTimeMillis();
+    }
+    private static void logd(@NonNull String log) {
+        Log.d(TAG, log);
+    }
+    private static void loge(@NonNull String log) {
+        Log.e(TAG, log);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
new file mode 100644
index 0000000..9524b75
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
@@ -0,0 +1,86 @@
+/*
+ * 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.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.SatelliteConstants;
+
+public class CarrierRoamingSatelliteControllerStats {
+    private static final String TAG = CarrierRoamingSatelliteControllerStats.class.getSimpleName();
+    private static CarrierRoamingSatelliteControllerStats sInstance = null;
+    private static final int ADD_COUNT = 1;
+
+    private SatelliteStats mSatelliteStats;
+
+    private CarrierRoamingSatelliteControllerStats() {
+        mSatelliteStats = SatelliteStats.getInstance();
+    }
+
+    /**
+     * Returns the Singleton instance of CarrierRoamingSatelliteControllerStats class.
+     * If an instance of the Singleton class has not been created,
+     * it creates a new instance and returns it. Otherwise, it returns
+     * the existing instance.
+     * @return the Singleton instance of CarrierRoamingSatelliteControllerStats
+     */
+    public static CarrierRoamingSatelliteControllerStats getOrCreateInstance() {
+        if (sInstance == null) {
+            logd("Create new CarrierRoamingSatelliteControllerStats.");
+            sInstance = new CarrierRoamingSatelliteControllerStats();
+        }
+        return sInstance;
+    }
+
+    /** Report config data source */
+    public void reportConfigDataSource(@SatelliteConstants.ConfigDataSource int configDataSource) {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setConfigDataSource(configDataSource)
+                        .build());
+    }
+
+    /** Report count of entitlement status query request */
+    public void reportCountOfEntitlementStatusQueryRequest() {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setCountOfEntitlementStatusQueryRequest(ADD_COUNT)
+                        .build());
+    }
+
+    /** Report count of satellite config update request */
+    public void reportCountOfSatelliteConfigUpdateRequest() {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setCountOfSatelliteConfigUpdateRequest(ADD_COUNT)
+                        .build());
+    }
+
+    /** Report count of satellite notification displayed */
+    public void reportCountOfSatelliteNotificationDisplayed() {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setCountOfSatelliteNotificationDisplayed(ADD_COUNT)
+                        .build());
+    }
+
+    private static void logd(@NonNull String log) {
+        Log.d(TAG, log);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java
new file mode 100644
index 0000000..771432e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java
@@ -0,0 +1,347 @@
+/*
+ * 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.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.telephony.CellInfo;
+import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CarrierRoamingSatelliteSessionStats {
+    private static final String TAG = CarrierRoamingSatelliteSessionStats.class.getSimpleName();
+    private static final SparseArray<CarrierRoamingSatelliteSessionStats>
+            sCarrierRoamingSatelliteSessionStats = new SparseArray<>();
+    @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
+    private int mCarrierId;
+    private boolean mIsNtnRoamingInHomeCountry;
+    private int mCountOfIncomingSms;
+    private int mCountOfOutgoingSms;
+    private int mCountOfIncomingMms;
+    private int mCountOfOutgoingMms;
+    private long mIncomingMessageId;
+
+    private int mSessionStartTimeSec;
+    private List<Long> mConnectionStartTimeList;
+    private List<Long> mConnectionEndTimeList;
+    private List<Integer> mRsrpList;
+    private List<Integer> mRssnrList;
+
+    public CarrierRoamingSatelliteSessionStats(int subId) {
+        logd("Create new CarrierRoamingSatelliteSessionStats. subId=" + subId);
+        initializeParams();
+        mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+    }
+
+    /** Gets a CarrierRoamingSatelliteSessionStats instance. */
+    public static CarrierRoamingSatelliteSessionStats getInstance(int subId) {
+        synchronized (sCarrierRoamingSatelliteSessionStats) {
+            if (sCarrierRoamingSatelliteSessionStats.get(subId) == null) {
+                sCarrierRoamingSatelliteSessionStats.put(subId,
+                        new CarrierRoamingSatelliteSessionStats(subId));
+            }
+            return sCarrierRoamingSatelliteSessionStats.get(subId);
+        }
+    }
+
+    /** Log carrier roaming satellite session start */
+    public void onSessionStart(int carrierId, Phone phone) {
+        mCarrierId = carrierId;
+        mSessionStartTimeSec = getCurrentTimeInSec();
+        mIsNtnRoamingInHomeCountry = false;
+        onConnectionStart(phone);
+    }
+
+    /** Log carrier roaming satellite connection start */
+    public void onConnectionStart(Phone phone) {
+        mConnectionStartTimeList.add(getCurrentTime());
+        updateNtnRoamingInHomeCountry(phone);
+    }
+
+    /** Log carrier roaming satellite session end */
+    public void onSessionEnd() {
+        onConnectionEnd();
+        reportMetrics();
+        mIsNtnRoamingInHomeCountry = false;
+    }
+
+    /** Log carrier roaming satellite connection end */
+    public void onConnectionEnd() {
+        mConnectionEndTimeList.add(getCurrentTime());
+    }
+
+    /** Log rsrp and rssnr when occurred the service state change with NTN is connected. */
+    public void onSignalStrength(Phone phone) {
+        CellSignalStrengthLte cellSignalStrengthLte = getCellSignalStrengthLte(phone);
+        int rsrp = cellSignalStrengthLte.getRsrp();
+        int rssnr = cellSignalStrengthLte.getRssnr();
+        if (rsrp == CellInfo.UNAVAILABLE) {
+            logd("onSignalStrength: rsrp unavailable");
+            return;
+        }
+        if (rssnr == CellInfo.UNAVAILABLE) {
+            logd("onSignalStrength: rssnr unavailable");
+            return;
+        }
+        mRsrpList.add(rsrp);
+        mRssnrList.add(rssnr);
+        logd("onSignalStrength : rsrp=" + rsrp + ", rssnr=" + rssnr);
+    }
+
+    /** Log incoming sms success case */
+    public void onIncomingSms(int subId) {
+        if (!isNtnConnected()) {
+            return;
+        }
+        mCountOfIncomingSms += 1;
+        logd("onIncomingSms: subId=" + subId + ", count=" + mCountOfIncomingSms);
+    }
+
+    /** Log outgoing sms success case */
+    public void onOutgoingSms(int subId) {
+        if (!isNtnConnected()) {
+            return;
+        }
+        mCountOfOutgoingSms += 1;
+        logd("onOutgoingSms: subId=" + subId + ", count=" + mCountOfOutgoingSms);
+    }
+
+    /** Log incoming or outgoing mms success case */
+    public void onMms(boolean isIncomingMms, long messageId) {
+        if (!isNtnConnected()) {
+            return;
+        }
+        if (isIncomingMms) {
+            mIncomingMessageId = messageId;
+            mCountOfIncomingMms += 1;
+            logd("onMms: messageId=" + messageId + ", countOfIncomingMms=" + mCountOfIncomingMms);
+        } else {
+            if (mIncomingMessageId == messageId) {
+                logd("onMms: NotifyResponse ignore it.");
+                mIncomingMessageId = 0;
+                return;
+            }
+            mCountOfOutgoingMms += 1;
+            logd("onMms: countOfOutgoingMms=" + mCountOfOutgoingMms);
+        }
+    }
+
+    private void reportMetrics() {
+        int totalSatelliteModeTimeSec = mSessionStartTimeSec > 0
+                ? getCurrentTimeInSec() - mSessionStartTimeSec : 0;
+        int numberOfSatelliteConnections = getNumberOfSatelliteConnections();
+        int avgDurationOfSatelliteConnectionSec = getAvgDurationOfSatelliteConnection(
+                numberOfSatelliteConnections);
+
+        List<Integer> connectionGapList = getSatelliteConnectionGapList(
+                numberOfSatelliteConnections);
+        int satelliteConnectionGapMinSec = 0;
+        int satelliteConnectionGapMaxSec = 0;
+        if (!connectionGapList.isEmpty()) {
+            satelliteConnectionGapMinSec = Collections.min(connectionGapList);
+            satelliteConnectionGapMaxSec = Collections.max(connectionGapList);
+        }
+
+        SatelliteStats.CarrierRoamingSatelliteSessionParams params =
+                new SatelliteStats.CarrierRoamingSatelliteSessionParams.Builder()
+                        .setCarrierId(mCarrierId)
+                        .setIsNtnRoamingInHomeCountry(mIsNtnRoamingInHomeCountry)
+                        .setTotalSatelliteModeTimeSec(totalSatelliteModeTimeSec)
+                        .setNumberOfSatelliteConnections(numberOfSatelliteConnections)
+                        .setAvgDurationOfSatelliteConnectionSec(avgDurationOfSatelliteConnectionSec)
+                        .setSatelliteConnectionGapMinSec(satelliteConnectionGapMinSec)
+                        .setSatelliteConnectionGapAvgSec(getAvg(connectionGapList))
+                        .setSatelliteConnectionGapMaxSec(satelliteConnectionGapMaxSec)
+                        .setRsrpAvg(getAvg(mRsrpList))
+                        .setRsrpMedian(getMedian(mRsrpList))
+                        .setRssnrAvg(getAvg(mRssnrList))
+                        .setRssnrMedian(getMedian(mRssnrList))
+                        .setCountOfIncomingSms(mCountOfIncomingSms)
+                        .setCountOfOutgoingSms(mCountOfOutgoingSms)
+                        .setCountOfIncomingMms(mCountOfIncomingMms)
+                        .setCountOfOutgoingMms(mCountOfOutgoingMms)
+                        .build();
+        SatelliteStats.getInstance().onCarrierRoamingSatelliteSessionMetrics(params);
+        logd("reportMetrics: " + params);
+        initializeParams();
+    }
+
+    private void initializeParams() {
+        mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+        mIsNtnRoamingInHomeCountry = false;
+        mCountOfIncomingSms = 0;
+        mCountOfOutgoingSms = 0;
+        mCountOfIncomingMms = 0;
+        mCountOfOutgoingMms = 0;
+        mIncomingMessageId = 0;
+
+        mSessionStartTimeSec = 0;
+        mConnectionStartTimeList = new ArrayList<>();
+        mConnectionEndTimeList = new ArrayList<>();
+        mRsrpList = new ArrayList<>();
+        mRssnrList = new ArrayList<>();
+        logd("initializeParams");
+    }
+
+    private CellSignalStrengthLte getCellSignalStrengthLte(Phone phone) {
+        SignalStrength signalStrength = phone.getSignalStrength();
+        List<CellSignalStrength> cellSignalStrengths = signalStrength.getCellSignalStrengths();
+        for (CellSignalStrength cellSignalStrength : cellSignalStrengths) {
+            if (cellSignalStrength instanceof CellSignalStrengthLte) {
+                return (CellSignalStrengthLte) cellSignalStrength;
+            }
+        }
+
+        return new CellSignalStrengthLte();
+    }
+
+    private int getNumberOfSatelliteConnections() {
+        return Math.min(mConnectionStartTimeList.size(), mConnectionEndTimeList.size());
+    }
+
+    private int getAvgDurationOfSatelliteConnection(int numberOfSatelliteConnections) {
+        if (numberOfSatelliteConnections == 0) {
+            return 0;
+        }
+
+        long totalConnectionsDuration = 0;
+        for (int i = 0; i < numberOfSatelliteConnections; i++) {
+            long endTime = mConnectionEndTimeList.get(i);
+            long startTime = mConnectionStartTimeList.get(i);
+            if (endTime >= startTime && startTime > 0) {
+                totalConnectionsDuration += endTime - startTime;
+            }
+        }
+
+        long avgConnectionDuration = totalConnectionsDuration / numberOfSatelliteConnections;
+        return (int) (avgConnectionDuration / 1000L);
+    }
+
+    private List<Integer> getSatelliteConnectionGapList(int numberOfSatelliteConnections) {
+        if (numberOfSatelliteConnections == 0) {
+            return new ArrayList<>();
+        }
+
+        List<Integer> connectionGapList = new ArrayList<>();
+        for (int i = 1; i < numberOfSatelliteConnections; i++) {
+            long prevConnectionEndTime = mConnectionEndTimeList.get(i - 1);
+            long currentConnectionStartTime = mConnectionStartTimeList.get(i);
+            if (currentConnectionStartTime > prevConnectionEndTime && prevConnectionEndTime > 0) {
+                connectionGapList.add((int) (
+                        (currentConnectionStartTime - prevConnectionEndTime) / 1000));
+            }
+        }
+        return connectionGapList;
+    }
+
+    private int getAvg(@NonNull List<Integer> list) {
+        if (list.isEmpty()) {
+            return 0;
+        }
+
+        int total = 0;
+        for (int num : list) {
+            total += num;
+        }
+
+        return total / list.size();
+    }
+
+    private int getMedian(@NonNull List<Integer> list) {
+        if (list.isEmpty()) {
+            return 0;
+        }
+        int size = list.size();
+        if (size == 1) {
+            return list.get(0);
+        }
+
+        Collections.sort(list);
+        return size % 2 == 0 ? (list.get(size / 2 - 1) + list.get(size / 2)) / 2
+                : list.get(size / 2);
+    }
+
+    private int getCurrentTimeInSec() {
+        return (int) (System.currentTimeMillis() / 1000);
+    }
+
+    private long getCurrentTime() {
+        return System.currentTimeMillis();
+    }
+
+    private boolean isNtnConnected() {
+        return mSessionStartTimeSec != 0;
+    }
+
+    private void updateNtnRoamingInHomeCountry(Phone phone) {
+        int subId = phone.getSubId();
+        ServiceState serviceState = phone.getServiceState();
+        if (serviceState == null) {
+            logd("ServiceState is null");
+            return;
+        }
+
+        String satelliteRegisteredPlmn = "";
+        for (NetworkRegistrationInfo nri
+                : serviceState.getNetworkRegistrationInfoList()) {
+            if (nri.isNonTerrestrialNetwork()) {
+                satelliteRegisteredPlmn = nri.getRegisteredPlmn();
+            }
+        }
+
+        SubscriptionInfoInternal subscriptionInfoInternal =
+                mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+        if (subscriptionInfoInternal == null) {
+            logd("SubscriptionInfoInternal is null");
+            return;
+        }
+        String simCountry = MccTable.countryCodeForMcc(subscriptionInfoInternal.getMcc());
+        String satelliteRegisteredCountry = MccTable.countryCodeForMcc(
+                satelliteRegisteredPlmn.substring(0, 3));
+        if (simCountry.equalsIgnoreCase(satelliteRegisteredCountry)) {
+            mIsNtnRoamingInHomeCountry = false;
+        } else {
+            // If device is connected to roaming non-terrestrial network, update to true.
+            mIsNtnRoamingInHomeCountry = true;
+        }
+        logd("updateNtnRoamingInHomeCountry: mIsNtnRoamingInHomeCountry="
+                + mIsNtnRoamingInHomeCountry);
+    }
+
+    private void logd(@NonNull String log) {
+        Log.d(TAG, log);
+    }
+
+    private void loge(@NonNull String log) {
+        Log.e(TAG, log);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ConfigUpdaterMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ConfigUpdaterMetricsStats.java
new file mode 100644
index 0000000..c379b83
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ConfigUpdaterMetricsStats.java
@@ -0,0 +1,124 @@
+/*
+ * 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.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.SatelliteConstants;
+
+public class ConfigUpdaterMetricsStats {
+    private static final String TAG = ConfigUpdaterMetricsStats.class.getSimpleName();
+    private static ConfigUpdaterMetricsStats sInstance = null;
+
+    private int mConfigVersion;
+    private int mOemConfigResult;
+    private int mCarrierConfigResult;
+
+    private ConfigUpdaterMetricsStats() {
+        initializeConfigUpdaterParams();
+    }
+
+    /**
+     * Returns the Singleton instance of ConfigUpdaterMetricsStats class.
+     * If an instance of the Singleton class has not been created,
+     * it creates a new instance and returns it. Otherwise, it returns
+     * the existing instance.
+     * @return the Singleton instance of ConfigUpdaterMetricsStats
+     */
+    public static ConfigUpdaterMetricsStats getOrCreateInstance() {
+        if (sInstance == null) {
+            logd("Create new ConfigUpdaterMetricsStats.");
+            sInstance = new ConfigUpdaterMetricsStats();
+        }
+        return sInstance;
+    }
+
+    /** Set config version for config updater metrics */
+    public ConfigUpdaterMetricsStats setConfigVersion(int configVersion) {
+        mConfigVersion = configVersion;
+        return this;
+    }
+
+    /** Set oem config result for config updater metrics */
+    public ConfigUpdaterMetricsStats setOemConfigResult(int oemConfigResult) {
+        mOemConfigResult = oemConfigResult;
+        return this;
+    }
+
+    /** Set carrier config result for config updater metrics */
+    public ConfigUpdaterMetricsStats setCarrierConfigResult(int carrierConfigResult) {
+        mCarrierConfigResult = carrierConfigResult;
+        return this;
+    }
+
+    /** Report metrics on oem config update error */
+    public void reportOemConfigError(int error) {
+        mOemConfigResult = error;
+        reportConfigUpdaterMetrics();
+    }
+
+    /** Report metrics on carrier config update error */
+    public void reportCarrierConfigError(int error) {
+        mCarrierConfigResult = error;
+        reportConfigUpdaterMetrics();
+    }
+
+    /** Report metrics on config update error */
+    public void reportOemAndCarrierConfigError(int error) {
+        mOemConfigResult = error;
+        mCarrierConfigResult = error;
+        reportConfigUpdaterMetrics();
+    }
+
+    /** Report metrics on config update success */
+    public void reportConfigUpdateSuccess() {
+        mOemConfigResult = SatelliteConstants.CONFIG_UPDATE_RESULT_SUCCESS;
+        mCarrierConfigResult = SatelliteConstants.CONFIG_UPDATE_RESULT_SUCCESS;
+        reportConfigUpdaterMetrics();
+    }
+
+
+    /** Report config updater metrics atom to PersistAtomsStorage in telephony */
+    private void reportConfigUpdaterMetrics() {
+        SatelliteStats.SatelliteConfigUpdaterParams configUpdaterParams =
+                new SatelliteStats.SatelliteConfigUpdaterParams.Builder()
+                        .setConfigVersion(mConfigVersion)
+                        .setOemConfigResult(mOemConfigResult)
+                        .setCarrierConfigResult(mCarrierConfigResult)
+                        .setCount(1)
+                        .build();
+        SatelliteStats.getInstance().onSatelliteConfigUpdaterMetrics(configUpdaterParams);
+        logd("reportConfigUpdaterMetrics: " + configUpdaterParams);
+
+        CarrierRoamingSatelliteControllerStats.getOrCreateInstance()
+                .reportCountOfSatelliteConfigUpdateRequest();
+
+        initializeConfigUpdaterParams();
+    }
+
+    private void initializeConfigUpdaterParams() {
+        mConfigVersion = -1;
+        mOemConfigResult = SatelliteConstants.CONFIG_UPDATE_RESULT_UNKNOWN;
+        mCarrierConfigResult = SatelliteConstants.CONFIG_UPDATE_RESULT_UNKNOWN;
+    }
+
+    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 fdc7c5c..7dff2e6 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
@@ -236,6 +236,36 @@
         mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
     }
 
+    /**
+     * Report a counter when checking result whether satellite communication is allowed or not for
+     * current location.
+     */
+    public void reportAllowedSatelliteAccessCount(boolean isAllowed) {
+        SatelliteStats.SatelliteControllerParams.Builder builder;
+        if (isAllowed) {
+            builder = new SatelliteStats.SatelliteControllerParams.Builder()
+                    .setCountOfAllowedSatelliteAccess(ADD_COUNT);
+        } else {
+            builder = new SatelliteStats.SatelliteControllerParams.Builder()
+                    .setCountOfDisallowedSatelliteAccess(ADD_COUNT);
+        }
+        SatelliteStats.SatelliteControllerParams controllerParam = builder.build();
+        logd("reportAllowedSatelliteAccessCount:" + controllerParam);
+        mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+    }
+
+    /**
+     * Report a counter when checking whether satellite communication for current location is
+     * allowed has failed.
+     */
+    public void reportFailedSatelliteAccessCheckCount() {
+        SatelliteStats.SatelliteControllerParams controllerParam =
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setCountOfSatelliteAccessCheckFail(ADD_COUNT).build();
+        logd("reportFailedSatelliteAccessCheckCount:" + controllerParam);
+        mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+    }
+
     /** Return the total service up time for satellite service */
     @VisibleForTesting
     public int captureTotalServiceUpTimeSec() {
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/EntitlementMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/EntitlementMetricsStats.java
new file mode 100644
index 0000000..4862188
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/metrics/EntitlementMetricsStats.java
@@ -0,0 +1,103 @@
+/*
+ * 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.satellite.metrics;
+
+import android.annotation.NonNull;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.SatelliteConstants;
+
+public class EntitlementMetricsStats {
+    private static final String TAG = EntitlementMetricsStats.class.getSimpleName();
+    private static EntitlementMetricsStats sInstance = null;
+    private static final int RESULT_SUCCESS = 200;
+
+    private int mSubId;
+    private int mResult;
+    private int mEntitlementStatus;
+    private boolean mIsRetry;
+
+    private EntitlementMetricsStats() {}
+
+    /**
+     * Returns the Singleton instance of EntitlementMetricsStats class.
+     * If an instance of the Singleton class has not been created,
+     * it creates a new instance and returns it. Otherwise, it returns
+     * the existing instance.
+     * @return the Singleton instance of EntitlementMetricsStats
+     */
+    public static EntitlementMetricsStats getOrCreateInstance() {
+        if (sInstance == null) {
+            logd("Create new EntitlementMetricsStats.");
+            sInstance = new EntitlementMetricsStats();
+        }
+        return sInstance;
+    }
+
+    /** Report metrics on entitlement query request success */
+    public void reportSuccess(int subId,
+            @SatelliteConstants.SatelliteEntitlementStatus int entitlementStatus,
+            boolean isRetry) {
+        mSubId = subId;
+        mResult = RESULT_SUCCESS;
+        mEntitlementStatus = entitlementStatus;
+        mIsRetry = isRetry;
+        reportEntitlementMetrics();
+    }
+
+    /** Report metrics on entitlement query request error */
+    public void reportError(int subId, int result, boolean isRetry) {
+        mSubId = subId;
+        mResult = result;
+        mIsRetry = isRetry;
+        mEntitlementStatus = SatelliteConstants.SATELLITE_ENTITLEMENT_STATUS_UNKNOWN;
+        reportEntitlementMetrics();
+    }
+
+    /** Report entitlement metrics atom to PersistAtomsStorage in telephony */
+    private void reportEntitlementMetrics() {
+        SatelliteStats.SatelliteEntitlementParams entitlementParams =
+                new SatelliteStats.SatelliteEntitlementParams.Builder()
+                        .setCarrierId(getCarrierId(mSubId))
+                        .setResult(mResult)
+                        .setEntitlementStatus(mEntitlementStatus)
+                        .setIsRetry(mIsRetry)
+                        .setCount(1)
+                        .build();
+        SatelliteStats.getInstance().onSatelliteEntitlementMetrics(entitlementParams);
+        logd("reportEntitlementMetrics: " + entitlementParams);
+
+        CarrierRoamingSatelliteControllerStats.getOrCreateInstance()
+                .reportCountOfEntitlementStatusQueryRequest();
+    }
+
+    /** Returns the carrier ID of the given subscription id. */
+    private int getCarrierId(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID;
+    }
+
+    private static void logd(@NonNull String log) {
+        Log.d(TAG, 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 c50db07..65181c0 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -16,11 +16,19 @@
 
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+
 import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteSessionStats;
 import android.util.Log;
 
 import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.DatagramDispatcher;
 
 /**
  * Stats to log to satellite session metrics
@@ -38,10 +46,12 @@
     private int mSessionDurationSec;
     private int mCountOfSuccessfulOutgoingDatagram;
     private int mCountOfFailedOutgoingDatagram;
+    private int mCountOfTimedOutUserMessagesWaitingForConnection;
+    private int mCountOfTimedOutUserMessagesWaitingForAck;
     private int mCountOfSuccessfulIncomingDatagram;
     private int mCountOfIncomingDatagramFailed;
     private boolean mIsDemoMode;
-
+    private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
 
     private SessionMetricsStats() {
         initializeSessionMetricsParam();
@@ -108,7 +118,13 @@
     }
 
     /** Increase the count of successful outgoing datagram transmission. */
-    public SessionMetricsStats addCountOfSuccessfulOutgoingDatagram() {
+    public SessionMetricsStats addCountOfSuccessfulOutgoingDatagram(
+            @NonNull @SatelliteManager.DatagramType int datagramType) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
         mCountOfSuccessfulOutgoingDatagram++;
         logd("addCountOfSuccessfulOutgoingDatagram: current count="
                 + mCountOfSuccessfulOutgoingDatagram);
@@ -116,9 +132,51 @@
     }
 
     /** Increase the count of failed outgoing datagram transmission. */
-    public SessionMetricsStats addCountOfFailedOutgoingDatagram() {
+    public SessionMetricsStats addCountOfFailedOutgoingDatagram(
+            @NonNull @SatelliteManager.DatagramType int datagramType,
+            @NonNull @SatelliteManager.SatelliteResult int resultCode) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
         mCountOfFailedOutgoingDatagram++;
         logd("addCountOfFailedOutgoingDatagram: current count=" + mCountOfFailedOutgoingDatagram);
+
+        if (resultCode == SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE) {
+            addCountOfTimedOutUserMessagesWaitingForConnection(datagramType);
+        } else if (resultCode == SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT) {
+            addCountOfTimedOutUserMessagesWaitingForAck(datagramType);
+        }
+
+        return this;
+    }
+
+    /** Increase the count of user messages that timed out waiting for connection. */
+    private SessionMetricsStats addCountOfTimedOutUserMessagesWaitingForConnection(
+            @NonNull @SatelliteManager.DatagramType int datagramType) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
+        mCountOfTimedOutUserMessagesWaitingForConnection++;
+        logd("addCountOfTimedOutUserMessagesWaitingForConnection: current count="
+                + mCountOfTimedOutUserMessagesWaitingForConnection);
+        return this;
+    }
+
+    /** Increase the count of user messages that timed out waiting for ack. */
+    private SessionMetricsStats addCountOfTimedOutUserMessagesWaitingForAck(
+            @NonNull @SatelliteManager.DatagramType int datagramType) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
+        mCountOfTimedOutUserMessagesWaitingForAck++;
+        logd("addCountOfTimedOutUserMessagesWaitingForAck: current count="
+                + mCountOfTimedOutUserMessagesWaitingForAck);
         return this;
     }
 
@@ -144,6 +202,17 @@
         return this;
     }
 
+    /** Updates the max Ntn signal strength level for the session. */
+    public SessionMetricsStats updateMaxNtnSignalStrengthLevel(
+            @NtnSignalStrength.NtnSignalStrengthLevel int latestNtnSignalStrengthLevel) {
+        if (latestNtnSignalStrengthLevel > mMaxNtnSignalStrengthLevel) {
+            mMaxNtnSignalStrengthLevel = latestNtnSignalStrengthLevel;
+        }
+        logd("updateMaxNtnSignalsStrength: latest signal strength=" + latestNtnSignalStrengthLevel
+                + ", max signal strength=" + mMaxNtnSignalStrengthLevel);
+        return this;
+    }
+
     /** Report the session metrics atoms to PersistAtomsStorage in telephony. */
     public void reportSessionMetrics() {
         SatelliteStats.SatelliteSessionParams sessionParams =
@@ -159,12 +228,30 @@
                         .setCountOfIncomingDatagramSuccess(mCountOfSuccessfulIncomingDatagram)
                         .setCountOfIncomingDatagramFailed(mCountOfIncomingDatagramFailed)
                         .setIsDemoMode(mIsDemoMode)
+                        .setMaxNtnSignalStrengthLevel(mMaxNtnSignalStrengthLevel)
                         .build();
         logd("reportSessionMetrics: " + sessionParams.toString());
         SatelliteStats.getInstance().onSatelliteSessionMetrics(sessionParams);
         initializeSessionMetricsParam();
     }
 
+    /** Returns {@link SatelliteSessionStats} of the satellite service. */
+    public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) {
+        Bundle bundle = new Bundle();
+        SatelliteSessionStats sessionStats = new SatelliteSessionStats.Builder()
+                .setCountOfSuccessfulUserMessages(mCountOfSuccessfulOutgoingDatagram)
+                .setCountOfUnsuccessfulUserMessages(mCountOfFailedOutgoingDatagram)
+                .setCountOfTimedOutUserMessagesWaitingForConnection(
+                        mCountOfTimedOutUserMessagesWaitingForConnection)
+                .setCountOfTimedOutUserMessagesWaitingForAck(
+                        mCountOfTimedOutUserMessagesWaitingForAck)
+                .setCountOfUserMessagesInQueueToBeSent(
+                        DatagramDispatcher.getInstance().getPendingUserMessagesCount())
+                .build();
+        bundle.putParcelable(SatelliteManager.KEY_SESSION_STATS, sessionStats);
+        result.send(SATELLITE_RESULT_SUCCESS, bundle);
+    }
+
     /** Returns the processing time for satellite session initialization. */
     public long getSessionInitializationProcessingTimeMillis() {
         return mInitializationProcessingTimeMillis;
@@ -184,9 +271,12 @@
         mSessionDurationSec = 0;
         mCountOfSuccessfulOutgoingDatagram = 0;
         mCountOfFailedOutgoingDatagram = 0;
+        mCountOfTimedOutUserMessagesWaitingForConnection = 0;
+        mCountOfTimedOutUserMessagesWaitingForAck = 0;
         mCountOfSuccessfulIncomingDatagram = 0;
         mCountOfIncomingDatagramFailed = 0;
         mIsDemoMode = false;
+        mMaxNtnSignalStrengthLevel = NTN_SIGNAL_STRENGTH_NONE;
     }
 
     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 3d07d47..7596754 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -1044,7 +1044,7 @@
                 throw new IllegalArgumentException("updateSubscription: subscription does not "
                         + "exist. subId=" + subId);
             }
-            if (oldSubInfo.equals(newSubInfo)) return;
+            if (oldSubInfo.equalsDbItemsOnly(newSubInfo)) return;
 
             if (updateDatabase(subId, createDeltaContentValues(oldSubInfo, newSubInfo)) > 0) {
                 mAllSubscriptionInfoInternalCache.put(subId, newSubInfo);
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index bb77d5c..c6dee7c 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -454,12 +454,13 @@
      */
     private final int mIsOnlyNonTerrestrialNetwork;
 
-    // Below are the fields that do not exist in the SimInfo table.
+    // This field does not exist in the SimInfo table.
     /**
      * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
      */
     private final int mCardId;
 
+    // This field does not exist in the SimInfo table.
     /**
      * Whether group of the subscription is disabled. This is only useful if it's a grouped
      * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
@@ -1370,11 +1371,14 @@
                 + "]";
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        SubscriptionInfoInternal that = (SubscriptionInfoInternal) o;
+    /**
+     * Campare only the columns existing in the SimInfo table and the mapped variables to see if
+     * they are equal.
+     *
+     * @param that SubscriptionInfoInternal to be compared
+     * @return {@code true} if equals.
+     */
+    public boolean equalsDbItemsOnly(@NonNull SubscriptionInfoInternal that) {
         return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
                 && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
                 && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
@@ -1407,7 +1411,6 @@
                 && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
                 && mLastUsedTPMessageReference == that.mLastUsedTPMessageReference
                 && mUserId == that.mUserId && mIsSatelliteEnabled == that.mIsSatelliteEnabled
-                && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled
                 && mIccId.equals(that.mIccId) && mDisplayName.equals(that.mDisplayName)
                 && mCarrierName.equals(that.mCarrierName) && mNumber.equals(that.mNumber)
                 && mMcc.equals(that.mMcc) && mMnc.equals(that.mMnc) && mEhplmns.equals(
@@ -1431,6 +1434,15 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SubscriptionInfoInternal that = (SubscriptionInfoInternal) o;
+        return equalsDbItemsOnly(that)
+                && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled;
+    }
+
+    @Override
     public int hashCode() {
         int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
                 mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mEhplmns, mHplmns,
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 5d653c7..0e98a9e 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1452,6 +1452,11 @@
             if (mSlotIndexToSubId.containsKey(phoneId)) {
                 markSubscriptionsInactive(phoneId);
             }
+
+            if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
+                // Clear the cached Ims phone number
+                setNumberFromIms(getSubId(phoneId), new String(""));
+            }
         } else if (simState == TelephonyManager.SIM_STATE_NOT_READY) {
             // Check if this is the final state. Only update the subscription if NOT_READY is a
             // final state.
@@ -1464,6 +1469,11 @@
             } else {
                 logl("updateSubscription: UICC app disabled on slot " + phoneId);
                 markSubscriptionsInactive(phoneId);
+
+                if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
+                    // Clear the cached Ims phone number
+                    setNumberFromIms(getSubId(phoneId), new String(""));
+                }
             }
         } else {
             String iccId = getIccId(phoneId);
@@ -1572,12 +1582,6 @@
                         loge("updateSubscription: ICC card is not available.");
                     }
 
-                    if (Flags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
-                        // Clear the cached Ims phone number
-                        // before proceeding with Ims Registration
-                        setNumberFromIms(subId, new String(""));
-                    }
-
                     // Attempt to restore SIM specific settings when SIM is loaded.
                     Bundle result = mContext.getContentResolver().call(
                             SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java
index 23769ad..e4a0cfa 100644
--- a/src/java/com/android/internal/telephony/uicc/PinStorage.java
+++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java
@@ -193,9 +193,11 @@
 
         CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
         // Callback directly handle config change and should be executed in 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));
+        }
 
         // Initialize the long term secret key. This needs to be present in all cases:
         //  - if the device is not secure or is locked: key does not require user authentication
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
index 0bce5cb..329b0b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ConnectionTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -25,9 +26,14 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.util.ArrayList;
+
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.telephony.emergency.EmergencyNumber;
 
+import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 
 import org.junit.After;
@@ -150,7 +156,7 @@
         assertNull(connection1.getEmergencyNumberInfo());
         assertFalse(connection1.hasKnownUserIntentEmergency());
 
-        connection2.setEmergencyCallInfo(mPhone.getCallTracker());
+        connection2.setEmergencyCallInfo(mPhone.getCallTracker(), null);
         connection2.setHasKnownUserIntentEmergency(true);
         connection1.migrateFrom(connection2);
 
@@ -164,7 +170,7 @@
     @Test
     public void testEmergencyCallParameters() {
         Connection connection = new TestConnection(TEST_PHONE_TYPE);
-        connection.setEmergencyCallInfo(mPhone.getCallTracker());
+        connection.setEmergencyCallInfo(mPhone.getCallTracker(), null);
         assertTrue(connection.isEmergencyCall());
         assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
         connection.setHasKnownUserIntentEmergency(true);
@@ -186,9 +192,44 @@
                 .thenReturn(getTestEmergencyNumber());
 
         //Ensure the connection is considered as an emergency call:
-        mTestConnection.setEmergencyCallInfo(mCT);
+        mTestConnection.setEmergencyCallInfo(mCT, null);
         assertTrue(mTestConnection.isEmergencyCall());
     }
 
+    @Test
+    public void testUpdateEmergencyRouting() {
+        DialArgs dialArgs = new DialArgs.Builder().build();
+        Connection connection = new TestConnection(TEST_PHONE_TYPE);
+        connection.setEmergencyCallInfo(mPhone.getCallTracker(), dialArgs);
+
+        // Not updated when DomainSelectionService is disabled.
+        assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+
+        // Enable DomainSelectionService
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        connection = new TestConnection(TEST_PHONE_TYPE);
+        connection.setEmergencyCallInfo(mPhone.getCallTracker(), dialArgs);
+
+        // Not updated when IS_EMERGENCY_ROUTING is not specified.
+        assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+
+        Bundle extras = new Bundle();
+        extras.putBoolean(PhoneConstants.EXTRA_USE_EMERGENCY_ROUTING, true);
+        extras.putInt(PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+        dialArgs = new DialArgs.Builder().setIntentExtras(extras).build();
+
+        connection = new TestConnection(TEST_PHONE_TYPE);
+        connection.setEmergencyCallInfo(mPhone.getCallTracker(), 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 DomainSelectionService is enabled.
+        assertNotEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+        assertEquals(expectedNumber, connection.getEmergencyNumberInfo());
+    }
+
     // TODO Verify more methods in Connection
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 552b5a7..4612ad9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -65,6 +65,7 @@
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
 import android.os.Bundle;
+import android.os.DropBoxManager;
 import android.os.Handler;
 import android.os.IInterface;
 import android.os.PersistableBundle;
@@ -313,6 +314,8 @@
                     return mImsManager;
                 case Context.DEVICE_POLICY_SERVICE:
                     return mDevicePolicyManager;
+                case Context.DROPBOX_SERVICE:
+                    return mDropBoxManager;
                 default:
                     return null;
             }
@@ -364,6 +367,8 @@
                 return Context.DEVICE_POLICY_SERVICE;
             } else if (serviceClass == NotificationManager.class) {
                 return Context.NOTIFICATION_SERVICE;
+            } else if (serviceClass == DropBoxManager.class) {
+                return Context.DROPBOX_SERVICE;
             }
             return super.getSystemServiceName(serviceClass);
         }
@@ -739,6 +744,7 @@
     private final NetworkPolicyManager mNetworkPolicyManager = mock(NetworkPolicyManager.class);
     private final ImsManager mImsManager = mock(ImsManager.class);
     private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
+    private final DropBoxManager mDropBoxManager = mock(DropBoxManager.class);
     private final Configuration mConfiguration = new Configuration();
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
     private final SharedPreferences mSharedPreferences = PreferenceManager
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index a6b2000..855a5dc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -18,10 +18,8 @@
 
 import static android.telephony.TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
-import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS;
-import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -251,6 +249,8 @@
         bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
         doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
 
+        doReturn(true).when(mFeatureFlags).resetPrimarySimDefaultValues();
+
         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
         // Capture listener to emulate the carrier config change notification used later
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
@@ -496,18 +496,9 @@
         sendCarrierConfigChanged(1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         processAllMessages();
 
-        verify(mSubscriptionManagerService).setDefaultDataSubId(
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        verify(mSubscriptionManagerService).setDefaultSmsSubId(
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
-
-        // Verify intent sent to select sub 2 as default for all types.
-        Intent intent = captureBroadcastIntent();
-        assertEquals(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED, intent.getAction());
-        assertEquals(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL,
-                intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1));
-        assertEquals(2, intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, -1));
+        verify(mSubscriptionManagerService).setDefaultDataSubId(2);
+        verify(mSubscriptionManagerService).setDefaultSmsSubId(2);
+        verify(mSubscriptionManagerService).setDefaultVoiceSubId(2);
     }
 
     @Test
@@ -917,8 +908,7 @@
         markSubscriptionInactive(1/*subid*/);
         sendCarrierConfigChanged(0/*phoneid*/, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
-        verify(mSubscriptionManagerService).setDefaultDataSubId(
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        verify(mSubscriptionManagerService).setDefaultDataSubId(2);
 
         // insert it back, but carrier config not loaded yet
         clearInvocations(mSubscriptionManagerService);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index fd99ad0..e3da458 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -3471,9 +3471,12 @@
                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
         assertEquals(2, nriList.size());
         for (NetworkRegistrationInfo nri : nriList) {
-            assertTrue(Arrays.equals(satelliteSupportedServices, nri.getAvailableServices().stream()
-                    .mapToInt(Integer::intValue)
-                    .toArray()));
+            if (nri.isInService()) {
+                assertTrue(Arrays.equals(satelliteSupportedServices,
+                        nri.getAvailableServices().stream()
+                        .mapToInt(Integer::intValue)
+                        .toArray()));
+            }
         }
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 81712c6..414fb3b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -816,15 +816,237 @@
         setUpDomainSelectionConnection();
         setUpSmsDispatchers();
         when(mFeatureFlags.smsDomainSelectionEnabled()).thenReturn(false);
+        when(mImsSmsDispatcher.isAvailable()).thenReturn(true);
+
+        mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
+                "test-app", 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));
+    }
+
+    @Test
+    @SmallTest
+    public void testSendTextWhenDomainSelectionFinishedAndNewTextSent() throws Exception {
+        setUpDomainSelectionConnection();
+        setUpSmsDispatchers();
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
                 "test-app", false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
-        // Expect that the domain selection logic will not be executed.
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
                 mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
-        assertNull(holder);
+        verify(mSmsDsc).requestDomainSelection(any(), any());
+        assertNotNull(holder);
+        assertNotNull(holder.getConnection());
+        assertTrue(holder.isDomainSelectionRequested());
+        assertEquals(1, holder.getPendingRequests().size());
+
+        // Expect that finishDomainSelection is called while a new pending request is posted.
+        mDscFuture.complete(NetworkRegistrationInfo.DOMAIN_PS);
+
+        SmsDomainSelectionConnection newSmsDsc = Mockito.mock(SmsDomainSelectionConnection.class);
+        mSmsDispatchersController.setDomainSelectionResolverProxy(
+                new SmsDispatchersController.DomainSelectionResolverProxy() {
+                    @Override
+                    @Nullable
+                    public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+                            @DomainSelectionService.SelectorType int selectorType,
+                            boolean isEmergency) {
+                        return newSmsDsc;
+                    }
+
+                    @Override
+                    public boolean isDomainSelectionSupported() {
+                        return true;
+                    }
+                });
+        CompletableFuture newDscFuture = new CompletableFuture<>();
+        when(newSmsDsc.requestDomainSelection(
+                any(DomainSelectionService.SelectionAttributes.class),
+                any(DomainSelectionConnection.DomainSelectionConnectionCallback.class)))
+                .thenReturn(newDscFuture);
+
+        // 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);
+        processAllMessages();
+
+        verify(mSmsDsc).finishSelection();
+
+        verify(newSmsDsc).requestDomainSelection(any(), any());
+        assertNotNull(holder.getConnection());
+        assertTrue(holder.isDomainSelectionRequested());
+        assertEquals(1, holder.getPendingRequests().size());
+
+        newDscFuture.complete(NetworkRegistrationInfo.DOMAIN_PS);
+        processAllMessages();
+
+        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));
+        assertNull(holder.getConnection());
+        assertFalse(holder.isDomainSelectionRequested());
+        assertEquals(0, holder.getPendingRequests().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testSendTextForEmergencyWhenDomainSelectionFinishedAndNewTextSent()
+            throws Exception {
+        setUpDomainSelectionConnection();
+        setUpSmsDispatchers();
+        setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
+
+        mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
+                "test-app", false, 0, false, 10, false, 1L, false);
+        processAllMessages();
+
+        SmsDispatchersController.DomainSelectionConnectionHolder holder =
+                mSmsDispatchersController.testGetDomainSelectionConnectionHolder(true);
+        verify(mEmergencySmsDsc).requestDomainSelection(any(), any());
+        assertNotNull(holder);
+        assertNotNull(holder.getConnection());
+        assertTrue(holder.isEmergency());
+        assertTrue(holder.isDomainSelectionRequested());
+        assertEquals(1, holder.getPendingRequests().size());
+
+        // Expect that finishDomainSelection is called while a new pending request is posted.
+        mDscFuture.complete(NetworkRegistrationInfo.DOMAIN_PS);
+
+        EmergencySmsDomainSelectionConnection newEmergencySmsDsc =
+                Mockito.mock(EmergencySmsDomainSelectionConnection.class);
+        mSmsDispatchersController.setDomainSelectionResolverProxy(
+                new SmsDispatchersController.DomainSelectionResolverProxy() {
+                    @Override
+                    @Nullable
+                    public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+                            @DomainSelectionService.SelectorType int selectorType,
+                            boolean isEmergency) {
+                        return newEmergencySmsDsc;
+                    }
+
+                    @Override
+                    public boolean isDomainSelectionSupported() {
+                        return true;
+                    }
+                });
+        CompletableFuture newDscFuture = new CompletableFuture<>();
+        when(newEmergencySmsDsc.requestDomainSelection(
+                any(DomainSelectionService.SelectionAttributes.class),
+                any(DomainSelectionConnection.DomainSelectionConnectionCallback.class)))
+                .thenReturn(newDscFuture);
+
+        // 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);
+        processAllMessages();
+
+        verify(mEmergencySmsDsc).finishSelection();
+
+        verify(newEmergencySmsDsc).requestDomainSelection(any(), any());
+        assertNotNull(holder.getConnection());
+        assertTrue(holder.isDomainSelectionRequested());
+        assertEquals(1, holder.getPendingRequests().size());
+
+        newDscFuture.complete(NetworkRegistrationInfo.DOMAIN_PS);
+        processAllMessages();
+
+        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));
+        assertNull(holder.getConnection());
+        assertFalse(holder.isDomainSelectionRequested());
+        assertEquals(0, holder.getPendingRequests().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testSendTextFallbackWhenDomainSelectionConnectionNotCreated() throws Exception {
+        mSmsDispatchersController.setDomainSelectionResolverProxy(
+                new SmsDispatchersController.DomainSelectionResolverProxy() {
+                    @Override
+                    @Nullable
+                    public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+                            @DomainSelectionService.SelectorType int selectorType,
+                            boolean isEmergency) {
+                        return null;
+                    }
+
+                    @Override
+                    public boolean isDomainSelectionSupported() {
+                        return true;
+                    }
+                });
+        when(mFeatureFlags.smsDomainSelectionEnabled()).thenReturn(true);
+        setUpSmsDispatchers();
+        when(mImsSmsDispatcher.isAvailable()).thenReturn(true);
+
+        // 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);
+        processAllMessages();
+
+        SmsDispatchersController.DomainSelectionConnectionHolder holder =
+                mSmsDispatchersController.testGetDomainSelectionConnectionHolder(false);
+        assertNotNull(holder);
+        assertNull(holder.getConnection());
+        assertFalse(holder.isDomainSelectionRequested());
+        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));
+    }
+
+    @Test
+    @SmallTest
+    public void testSendTextFallbackForEmergencyWhenDomainSelectionConnectionNotCreated()
+            throws Exception {
+        mSmsDispatchersController.setDomainSelectionResolverProxy(
+                new SmsDispatchersController.DomainSelectionResolverProxy() {
+                    @Override
+                    @Nullable
+                    public DomainSelectionConnection getDomainSelectionConnection(Phone phone,
+                            @DomainSelectionService.SelectorType int selectorType,
+                            boolean isEmergency) {
+                        return null;
+                    }
+
+                    @Override
+                    public boolean isDomainSelectionSupported() {
+                        return true;
+                    }
+                });
+        when(mFeatureFlags.smsDomainSelectionEnabled()).thenReturn(true);
+        setUpSmsDispatchers();
+        setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
+        when(mImsSmsDispatcher.isAvailable()).thenReturn(true);
+
+        // 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);
+        processAllMessages();
+
+        SmsDispatchersController.DomainSelectionConnectionHolder holder =
+                mSmsDispatchersController.testGetDomainSelectionConnectionHolder(true);
+        assertNotNull(holder);
+        assertNull(holder.getConnection());
+        assertTrue(holder.isEmergency());
+        assertFalse(holder.isDomainSelectionRequested());
+        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));
     }
 
     private void switchImsSmsFormat(int phoneType) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
index ddbe9c0..0e2676e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -93,8 +93,10 @@
     private TelephonyDisplayInfo mGoodTelephonyDisplayInfo;
     private TelephonyDisplayInfo mBadTelephonyDisplayInfo;
     private int mDefaultDataSub;
+    private DataEvaluation mDataEvaluation;
     private AutoDataSwitchController mAutoDataSwitchControllerUT;
     private Map<Integer, AlarmManager.OnAlarmListener> mEventsToAlarmListener;
+    private Map<Integer, Object> mScheduledEventsToExtras;
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -137,6 +139,8 @@
             doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
                     .when(phone).isUserDataEnabled();
         }
+        mDataEvaluation = new DataEvaluation(DataEvaluation.DataEvaluationReason.EXTERNAL_QUERY);
+        doReturn(mDataEvaluation).when(mDataNetworkController).getInternetEvaluation(anyBoolean());
         doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService)
                 .getActiveSubIdList(true);
         doAnswer(invocation -> {
@@ -184,9 +188,12 @@
                 mAutoDataSwitchControllerUT, mMockedAlarmManager);
         mEventsToAlarmListener = getPrivateField(mAutoDataSwitchControllerUT,
                 "mEventsToAlarmListener", Map.class);
+        mScheduledEventsToExtras = getPrivateField(mAutoDataSwitchControllerUT,
+                "mScheduledEventsToExtras", Map.class);
 
         doReturn(true).when(mFeatureFlags).autoDataSwitchAllowRoaming();
         doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
+        doReturn(true).when(mFeatureFlags).autoDataSwitchUsesDataEnabled();
     }
 
     @After
@@ -240,7 +247,10 @@
         prepareIdealUsesNonDdsCondition();
         processAllFutureMessages();
         clearInvocations(mMockedPhoneSwitcherCallback);
-        doReturn(false).when(mPhone2).isDataAllowed();
+        mDataEvaluation.addDataDisallowedReason(DataEvaluation.DataDisallowedReason
+                .NO_SUITABLE_DATA_PROFILE);
+        doReturn(mDataEvaluation)
+                .when(mDataNetworkController).getInternetEvaluation(anyBoolean());
         mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
         processAllFutureMessages();
 
@@ -350,7 +360,6 @@
 
     @Test
     public void testRoaming_same_roaming_condition_uses_rat_signalStrength() {
-        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
         // On primary phone
         // 1. Both roaming, user allow roaming on both phone, uses RAT score to decide switch.
         prepareIdealUsesNonDdsCondition();
@@ -375,7 +384,6 @@
 
     @Test
     public void testCancelSwitch_onPrimary_rat_signalStrength() {
-        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
         // 4.1.1 Display info and signal strength on secondary phone became bad,
         // but primary is still OOS, so still switch to the secondary.
         prepareIdealUsesNonDdsCondition();
@@ -467,7 +475,7 @@
         prepareIdealUsesNonDdsCondition();
         // 2.2 Auto switch feature is disabled, no need validation
         clearInvocations(mCellularNetworkValidator);
-        doReturn(false).when(mPhone2).isDataAllowed();
+        mDataEvaluation.addDataDisallowedReason(DataEvaluation.DataDisallowedReason.DATA_DISABLED);
         mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
         processAllFutureMessages();
 
@@ -483,11 +491,13 @@
 
         verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
                 false/*needValidation*/);
+
+        clearInvocations(mMockedPhoneSwitcherCallback);
+        prepareIdealUsesNonDdsCondition();
     }
 
     @Test
     public void testOnNonDdsSwitchBackToPrimary_rat_signalStrength() {
-        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
         prepareIdealUsesNonDdsCondition();
         processAllFutureMessages();
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
@@ -536,10 +546,16 @@
 
     @Test
     public void testStabilityCheckOverride_basic() {
+        // Disable RAT + signalStrength base switching.
+        doReturn(-1).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
+        mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
+
         // Starting stability check for switching to non-DDS
         prepareIdealUsesNonDdsCondition();
-        processAllMessages();
+        processAllFutureMessages();
 
+        clearInvocations(mMockedPhoneSwitcherCallback);
         // Switch success, but the previous stability check is still pending
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
 
@@ -557,7 +573,6 @@
 
     @Test
     public void testStabilityCheckOverride_uses_rat_signalStrength() {
-        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
         // Switching due to availability first.
         prepareIdealUsesNonDdsCondition();
 
@@ -578,6 +593,7 @@
     public void testValidationFailedRetry() {
         prepareIdealUsesNonDdsCondition();
 
+        clearInvocations(mMockedPhoneSwitcherCallback);
         for (int i = 0; i < MAX_RETRY; i++) {
             mAutoDataSwitchControllerUT.evaluateRetryOnValidationFailed();
             processAllFutureMessages();
@@ -730,7 +746,7 @@
 
         // 4.2 Auto switch feature is enabled
         doReturn(true).when(mPhone2).getDataRoamingEnabled();
-        doReturn(true).when(mPhone2).isDataAllowed();
+        mDataEvaluation.addDataAllowedReason(DataEvaluation.DataAllowedReason.NORMAL);
 
         // 5. No default network
         mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(null /*networkCapabilities*/);
@@ -783,10 +799,12 @@
 
     @Override
     public void processAllFutureMessages() {
-        if (mFeatureFlags.autoDataSwitchRatSs()
-                && mEventsToAlarmListener.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
+        if (mScheduledEventsToExtras.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
             mEventsToAlarmListener.get(EVENT_STABILITY_CHECK_PASSED).onAlarm();
         }
+        if (mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) {
+            mEventsToAlarmListener.get(EVENT_EVALUATE_AUTO_SWITCH).onAlarm();
+        }
         super.processAllFutureMessages();
     }
 }
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 28e129c..60dcb59 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -139,6 +139,8 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 
+import javax.annotation.Nullable;
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class DataNetworkControllerTest extends TelephonyTest {
@@ -183,7 +185,6 @@
     private LinkBandwidthEstimatorCallback mLinkBandwidthEstimatorCallback;
 
     private boolean mIsNonTerrestrialNetwork = false;
-    private FeatureFlags mFeatureFlags;
 
     private final DataProfile mGeneralPurposeDataProfile = new DataProfile.Builder()
             .setApnSetting(new ApnSetting.Builder()
@@ -393,17 +394,6 @@
                             "PRIORITIZE_LATENCY", 1).getBytes()))
             .build();
 
-    private final DataProfile mMmsOnWlanDataProfile = new DataProfile.Builder()
-            .setApnSetting(new ApnSetting.Builder()
-                    .setEntryName("mms_wlan")
-                    .setApnName("mms_wlan")
-                    .setApnTypeBitmask(ApnSetting.TYPE_MMS)
-                    .setCarrierEnabled(true)
-                    .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
-                    .build())
-            .setPreferred(false)
-            .build();
-
     private final DataProfile mNtnDataProfile = new DataProfile.Builder()
             .setApnSetting(new ApnSetting.Builder()
                     .setEntryName("ntn")
@@ -873,7 +863,6 @@
         mMockedDataNetworkControllerCallback = Mockito.mock(DataNetworkControllerCallback.class);
         mMockedDataRetryManagerCallback = Mockito.mock(DataRetryManagerCallback.class);
         mMockSubInfo = Mockito.mock(SubscriptionInfo.class);
-        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         when(mTelephonyComponentFactory.makeDataSettingsManager(any(Phone.class),
                 any(DataNetworkController.class), any(FeatureFlags.class), any(Looper.class),
                 any(DataSettingsManager.DataSettingsManagerCallback.class))).thenCallRealMethod();
@@ -901,6 +890,8 @@
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
         doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
         doReturn(true).when(mFeatureFlags).satelliteInternet();
+        doReturn(true).when(mFeatureFlags)
+                .ignoreExistingNetworksForInternetAllowedChecking();
 
         List<SubscriptionInfo> infoList = new ArrayList<>();
         infoList.add(mMockSubInfo);
@@ -1002,7 +993,7 @@
         List<DataProfile> profiles = List.of(mGeneralPurposeDataProfile,
                 mGeneralPurposeDataProfileAlternative, mImsCellularDataProfile,
                 mImsIwlanDataProfile, mEmergencyDataProfile, mFotaDataProfile,
-                mTetheringDataProfile, mMmsOnWlanDataProfile, mLowLatencyDataProfile,
+                mTetheringDataProfile, mLowLatencyDataProfile,
                 mNtnDataProfile, mEsimBootstrapDataProfile,
                 mEsimBootstrapImsProfile, mEsimBootstrapRcsInfraStructureProfile);
 
@@ -1141,18 +1132,23 @@
     }
 
     private @NonNull TelephonyNetworkRequest createNetworkRequest(Integer... capabilities) {
-        return createNetworkRequest(false, capabilities);
+        return createNetworkRequest(null, capabilities);
     }
 
-    private @NonNull TelephonyNetworkRequest createNetworkRequest(boolean restricted,
+    private @NonNull TelephonyNetworkRequest createNetworkRequest(@Nullable Boolean restricted,
                                                                   Integer... capabilities) {
         NetworkCapabilities netCaps = new NetworkCapabilities();
         for (int networkCapability : capabilities) {
             netCaps.addCapability(networkCapability);
         }
 
-        if (restricted) {
-            netCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        if (restricted != null) {
+            if (restricted) {
+                netCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+            }
+        } else {
+            // Data Network uses the same to define its own capabilities.
+            netCaps.maybeMarkCapabilitiesRestricted();
         }
 
         NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
@@ -1744,6 +1740,14 @@
         verify(mMockedDataNetworkControllerCallback, never())
                 .onConnectedInternetDataNetworksChanged(any());
 
+        // However, WLAN network setup shouldn't be affected
+        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
+                .getPreferredTransportByNetworkCapability(anyInt());
+        mDataNetworkControllerUT.obtainMessage(5 /*EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS*/,
+                DataEvaluation.DataEvaluationReason.PREFERRED_TRANSPORT_CHANGED).sendToTarget();
+        processAllMessages();
+        verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
+
         mIsNonTerrestrialNetwork = false;
     }
 
@@ -3322,12 +3326,20 @@
                 NetworkCapabilities.NET_CAPABILITY_IMS);
         TelephonyNetworkRequest secondTnr = createNetworkRequest(
                 NetworkCapabilities.NET_CAPABILITY_IMS);
+        TelephonyNetworkRequest thirdTnr = new TelephonyNetworkRequest(
+                new NetworkRequest((new NetworkCapabilities.Builder())
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+                        .addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE)
+                        .build(),
+                        ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId,
+                        NetworkRequest.Type.REQUEST), mPhone, mFeatureFlags);
 
         mDataNetworkControllerUT.addNetworkRequest(firstTnr);
         processAllMessages();
 
         mDataNetworkControllerUT.removeNetworkRequest(firstTnr);
         mDataNetworkControllerUT.addNetworkRequest(secondTnr);
+        mDataNetworkControllerUT.addNetworkRequest(thirdTnr);
         processAllFutureMessages();
 
         verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
@@ -4192,6 +4204,7 @@
 
         NetworkCapabilities netCaps = new NetworkCapabilities();
         netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+        netCaps.maybeMarkCapabilitiesRestricted();
         netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE);
 
         NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
@@ -4244,6 +4257,7 @@
         // setup emergency data network.
         NetworkCapabilities netCaps = new NetworkCapabilities();
         netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
+        netCaps.maybeMarkCapabilitiesRestricted();
         netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE);
 
         NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
@@ -5037,47 +5051,85 @@
     }
 
     @Test
-    public void testAllowBringUpWithDifferentDataProfileForWlan() throws Exception {
-        // Mock MMS preferred on WLAN
-        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
-                .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
-
-        // Setup a default cellular network that's capable of MMS
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS));
-        processAllMessages();
-        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
-
-        // Mock the designated MMS profile when WLAN is preferred
-        doReturn(mMmsOnWlanDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
-                any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_IWLAN),
-                anyBoolean(), anyBoolean(), anyBoolean());
-        setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager,
-                createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE));
-
-        // Verify the designated MMS profile is used to satisfy MMS request
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_MMS));
-        processAllMessages();
-        verifyConnectedNetworkHasDataProfile(mMmsOnWlanDataProfile);
-    }
-
-    @Test
     public void testNonTerrestrialNetwork() throws Exception {
+        TelephonyNetworkRequest request;
         mIsNonTerrestrialNetwork = true;
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(false, NetworkCapabilities.NET_CAPABILITY_RCS));
+        // By default, Test only support restricted network, regardless whether constrained.
+        // Verify not_restricted network not supported.
+        request = createNetworkRequest(false, NetworkCapabilities.NET_CAPABILITY_RCS);
+        mDataNetworkControllerUT.addNetworkRequest(request);
         processAllMessages();
         verifyAllDataDisconnected();
+        mDataNetworkControllerUT.removeNetworkRequest(request);
 
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(true, NetworkCapabilities.NET_CAPABILITY_RCS));
+        // Verify restricted network is supported.
+        request = createNetworkRequest(true, NetworkCapabilities.NET_CAPABILITY_RCS);
+        mDataNetworkControllerUT.addNetworkRequest(request);
         processAllMessages();
         verifyConnectedNetworkHasDataProfile(mNtnDataProfile);
+        mDataNetworkControllerUT.removeNetworkRequest(request);
+        getDataNetworks().get(0).tearDown(DataNetwork
+                .TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED);
+
+        // Test constrained network is supported, regardless whether it's restricted
+        processAllFutureMessages();
+        verifyAllDataDisconnected();
+        mCarrierConfig.putInt(CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
+                CarrierConfigManager.SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED);
+        carrierConfigChanged();
+        // Verify not_restricted, not_constrained network not supported.
+        NetworkCapabilities netCaps = new NetworkCapabilities();
+        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
+        try {
+            netCaps.addCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+        } catch (Exception ignored) { }
+        request = new TelephonyNetworkRequest(new NetworkRequest(netCaps,
+                ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId,
+                NetworkRequest.Type.REQUEST), mPhone, mFeatureFlags);
+
+        mDataNetworkControllerUT.addNetworkRequest(request);
+        processAllMessages();
+        verifyAllDataDisconnected();
+        mDataNetworkControllerUT.removeNetworkRequest(request);
+
+        // Verify restricted, not_constrained network is supported.
+        netCaps = new NetworkCapabilities();
+        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
+        netCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        try {
+            netCaps.addCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+        } catch (Exception ignored) { }
+        request = new TelephonyNetworkRequest(new NetworkRequest(netCaps,
+                ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId,
+                NetworkRequest.Type.REQUEST), mPhone, mFeatureFlags);
+        mDataNetworkControllerUT.addNetworkRequest(request);
+        processAllMessages();
+        verifyConnectedNetworkHasDataProfile(mNtnDataProfile);
+        mDataNetworkControllerUT.removeNetworkRequest(request);
+        getDataNetworks().get(0).tearDown(DataNetwork
+                .TEAR_DOWN_REASON_CONNECTIVITY_SERVICE_UNWANTED);
+
+        // TODO(enable after NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED become a default cap)
+        // Test not constrained network supported
+//        processAllFutureMessages();
+//        verifyAllDataDisconnected();
+//        mCarrierConfig.putInt(CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
+//                CarrierConfigManager.SATELLITE_DATA_SUPPORT_ALL);
+//        carrierConfigChanged();
+//
+//        netCaps = new NetworkCapabilities();
+//        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
+//        try {
+//            netCaps.addCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+//        } catch (Exception ignored) {}
+//        mDataNetworkControllerUT.addNetworkRequest(
+//                new TelephonyNetworkRequest(new NetworkRequest(netCaps,
+//                        ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId,
+//                        NetworkRequest.Type.REQUEST), mPhone, mFeatureFlags));
+//        processAllMessages();
+//        verifyConnectedNetworkHasDataProfile(mNtnDataProfile);
     }
 
     @Test
@@ -5132,7 +5184,8 @@
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+                createNetworkRequest(true/*restricted*/,
+                        NetworkCapabilities.NET_CAPABILITY_INTERNET));
         processAllMessages();
         verifyConnectedNetworkHasDataProfile(mEsimBootstrapDataProfile);
 
@@ -5143,13 +5196,6 @@
                 .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), 2);
         processAllMessages();
         verifyConnectedNetworkHasDataProfile(mEsimBootstrapImsProfile);
-
-        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
-                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
-        mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_RCS));
-        processAllMessages();
-        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
     }
 
     @Test
@@ -5188,7 +5234,7 @@
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         mDataNetworkControllerUT.addNetworkRequest(
-                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+                createNetworkRequest(true, NetworkCapabilities.NET_CAPABILITY_INTERNET));
         processAllMessages();
         // With allowed data limit unlimited, connection is allowed
         verifyConnectedNetworkHasDataProfile(mEsimBootstrapDataProfile);
@@ -5414,4 +5460,14 @@
         verify(mMockedDataNetworkControllerCallback, never()).onAnyDataNetworkExistingChanged(
                 anyBoolean());
     }
+
+    @Test
+    public void testGetInternetEvaluation() throws Exception {
+        testSetupDataNetwork();
+        doReturn(true).when(mSST).isPendingRadioPowerOffAfterDataOff();
+        assertThat(mDataNetworkControllerUT.getInternetEvaluation(false/*ignoreExistingNetworks*/)
+                .containsDisallowedReasons()).isFalse();
+        assertThat(mDataNetworkControllerUT.getInternetEvaluation(true/*ignoreExistingNetworks*/)
+                .containsDisallowedReasons()).isTrue();
+    }
 }
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 d5a52ea..7795e42 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -53,6 +53,7 @@
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.Annotation;
 import android.telephony.Annotation.DataFailureCause;
+import android.telephony.CarrierConfigManager;
 import android.telephony.DataFailCause;
 import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.LteVopsSupportInfo;
@@ -333,33 +334,40 @@
     private void serviceStateChanged(@Annotation.NetworkType int networkType,
             @NetworkRegistrationInfo.RegistrationState int regState,
             DataSpecificRegistrationInfo dsri, boolean isNtn) {
-        ServiceState ss = new ServiceState();
-        ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+        doReturn(isNtn).when(mServiceState).isUsingNonTerrestrialNetwork();
+
+        doReturn(new NetworkRegistrationInfo.Builder()
                 .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                 .setAccessNetworkTechnology(networkType)
                 .setRegistrationState(regState)
                 .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
                 .setDataSpecificInfo(dsri)
                 .setIsNonTerrestrialNetwork(isNtn)
-                .build());
+                .build()).when(mServiceState)
+                .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
-        ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+        doReturn(new NetworkRegistrationInfo.Builder()
                 .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
                 .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
                 .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
                 .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
-                .build());
+                .build()).when(mServiceState)
+                .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
 
-        ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+        doReturn(new NetworkRegistrationInfo.Builder()
                 .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                 .setAccessNetworkTechnology(networkType)
                 .setRegistrationState(regState)
                 .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
-                .build());
-        ss.setDataRoamingFromRegistration(regState
-                == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
-        doReturn(ss).when(mSST).getServiceState();
-        doReturn(ss).when(mPhone).getServiceState();
+                .build()).when(mServiceState)
+                .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        boolean isRoaming = regState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+        doReturn(isRoaming).when(mServiceState).getDataRoamingFromRegistration();
+        doReturn(isRoaming).when(mServiceState).getDataRoaming();
 
         if (mDataNetworkUT != null) {
             mDataNetworkUT.obtainMessage(9/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
@@ -420,6 +428,8 @@
         doReturn(Set.of(NetworkCapabilities.NET_CAPABILITY_IMS,
                 NetworkCapabilities.NET_CAPABILITY_EIMS, NetworkCapabilities.NET_CAPABILITY_XCAP))
                 .when(mDataConfigManager).getForcedCellularTransportCapabilities();
+        doReturn(CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED)
+                .when(mDataConfigManager).getSatelliteDataSupportMode();
         doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
         doReturn(true).when(mFeatureFlags).satelliteInternet();
 
@@ -2373,6 +2383,7 @@
     }
 
     private void setupNonTerrestrialDataNetwork() {
+        doReturn(true).when(mServiceState).isUsingNonTerrestrialNetwork();
         NetworkRequestList networkRequestList = new NetworkRequestList();
 
         networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
@@ -2387,6 +2398,8 @@
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
                 DataAllowedReason.NORMAL, mDataNetworkCallback);
         processAllMessages();
+
+        assertThat(mDataNetworkUT.isSatellite()).isTrue();
     }
 
     private void setupTerrestrialDataNetwork() {
@@ -2509,6 +2522,42 @@
     }
 
     @Test
+    public void testUnrestrictedSatelliteNetworkCapabilities() {
+        setupNonTerrestrialDataNetwork();
+        assertThat(mDataNetworkUT.isSatellite()).isTrue();
+
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isFalse();
+
+        // Test constrained traffic
+        doReturn(CarrierConfigManager.SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED)
+                .when(mDataConfigManager).getSatelliteDataSupportMode();
+        mDataNetworkUT.sendMessage(22/*EVENT_VOICE_CALL_STARTED*/); // update network capabilities
+        processAllMessages();
+
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isTrue();
+        try {
+            assertThat(mDataNetworkUT.getNetworkCapabilities()
+                    .hasCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED)).isFalse();
+        } catch (Exception ignored) { }
+
+        // Test not constrained traffic
+        doReturn(CarrierConfigManager.SATELLITE_DATA_SUPPORT_ALL)
+                .when(mDataConfigManager).getSatelliteDataSupportMode();
+        mDataNetworkUT.sendMessage(22/*EVENT_VOICE_CALL_STARTED*/); // update network capabilities
+        processAllMessages();
+
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isTrue();
+        // TODO(enable after NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED become a default cap)
+//        try {
+//            assertThat(mDataNetworkUT.getNetworkCapabilities()
+//                    .hasCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED)).isTrue();
+//        } catch (Exception ignored) {}
+    }
+
+    @Test
     public void testIsTransportSatelliteSupportNonImsNonTerrestrialNetwork() throws Exception {
         // Service state at Non-terrestrial network
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
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 1517006..9f11a3a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
@@ -168,18 +168,12 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                         .build(), mPhone, mFeatureFlags);
-        assertThat(internetRequest.getCapabilities()).isEqualTo(
-                new int[]{NetworkCapabilities.NET_CAPABILITY_INTERNET,
-                        NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED,
-                        NetworkCapabilities.NET_CAPABILITY_TRUSTED,
-                        NetworkCapabilities.NET_CAPABILITY_NOT_VPN,
-                        NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED});
+        assertThat(internetRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED))
+                .isTrue();
         assertThat(internetRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN))
                 .isTrue();
         assertThat(internetRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET))
                 .isTrue();
-        assertThat(internetRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN))
-                .isTrue();
         assertThat(internetRequest.hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)).isTrue();
         assertThat(internetRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS))
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionControllerTest.java
index f5ccdd6..a55ad69 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionControllerTest.java
@@ -16,8 +16,11 @@
 
 package com.android.internal.telephony.domainselection;
 
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -36,6 +39,8 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.DomainSelectionService;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContext;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -71,6 +76,8 @@
                 }
             };
 
+    Context mTestContext;
+
     // Mocked classes
     IDomainSelectionServiceController mMockServiceControllerBinder;
     Context mMockContext;
@@ -84,9 +91,40 @@
     public void setUp() throws Exception {
         super.setUp(this.getClass().getSimpleName());
 
+        when(mTelephonyManager.getSupportedModemCount()).thenReturn(2);
         mMockContext = mock(Context.class);
+        mTestContext = new MockContext() {
+            @Override
+            public String getSystemServiceName(Class<?> serviceClass) {
+                if (serviceClass == TelephonyManager.class) {
+                    return Context.TELEPHONY_SERVICE;
+                }
+                return super.getSystemServiceName(serviceClass);
+            }
+
+            @Override
+            public Object getSystemService(String name) {
+                switch (name) {
+                    case (Context.TELEPHONY_SERVICE) : {
+                        return mTelephonyManager;
+                    }
+                }
+                return super.getSystemService(name);
+            }
+
+            @Override
+            public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+                return mMockContext.bindService(service, conn, flags);
+            }
+
+            @Override
+            public void unbindService(ServiceConnection conn) {
+                mMockContext.unbindService(conn);
+            }
+        };
+
         mMockServiceControllerBinder = mock(IDomainSelectionServiceController.class);
-        mTestController = new DomainSelectionController(mMockContext,
+        mTestController = new DomainSelectionController(mTestContext,
                 Looper.getMainLooper(), BIND_RETRY);
         mHandler = mTestController.getHandlerForTest();
 
@@ -264,6 +302,17 @@
         verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
     }
 
+    @SmallTest
+    @Test
+    public void testGetDomainSelectionConnection() throws Exception {
+        when(mPhone.getPhoneId()).thenReturn(1);
+        DomainSelectionConnection dsc = mTestController.getDomainSelectionConnection(
+                mPhone, SELECTOR_TYPE_CALLING, false);
+
+        assertNotNull(dsc);
+        assertTrue(dsc instanceof NormalCallDomainSelectionConnection);
+    }
+
     private void bindAndNullServiceError() {
         ServiceConnection connection = bindService(mTestComponentName);
         connection.onNullBinding(mTestComponentName);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
index 5ac21f8..7374aef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
@@ -40,6 +40,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -356,4 +357,55 @@
 
         assertFalse(stats.isNtn);
     }
+
+    @Test
+    public void testIsProvisioningProfile() {
+        SubscriptionInfoInternal mSubInfoInternal = new SubscriptionInfoInternal.Builder()
+            .setProfileClass(mSubscriptionManager.PROFILE_CLASS_PROVISIONING).build();
+
+        when(mSubscriptionManagerService.getSubscriptionInfoInternal(mPhone.getSubId()))
+            .thenReturn(mSubInfoInternal);
+
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
+        mDataCallSessionStats.onSetupDataCallResponse(
+            mDefaultImsResponse,
+            TelephonyManager.NETWORK_TYPE_IWLAN,
+            ApnSetting.TYPE_IMS,
+            ApnSetting.PROTOCOL_IP,
+            DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(60000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+            ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+            callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertTrue(stats.isProvisioningProfile);
+
+        reset(mPersistAtomsStorage);
+
+        mSubInfoInternal = new SubscriptionInfoInternal.Builder()
+            .setProfileClass(mSubscriptionManager.PROFILE_CLASS_OPERATIONAL).build();
+
+        when(mSubscriptionManagerService.getSubscriptionInfoInternal(mPhone.getSubId()))
+            .thenReturn(mSubInfoInternal);
+
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS, false);
+        mDataCallSessionStats.onSetupDataCallResponse(
+            mDefaultImsResponse,
+            TelephonyManager.NETWORK_TYPE_IWLAN,
+            ApnSetting.TYPE_IMS,
+            ApnSetting.PROTOCOL_IP,
+            DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(60000L);
+        mDataCallSessionStats.conclude();
+        verify(mPersistAtomsStorage).addDataCallSession(callCaptor.capture());
+        stats = callCaptor.getValue();
+
+        assertFalse(stats.isProvisioningProfile);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 35f1bb7..04b45b3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -16,9 +16,13 @@
 
 package com.android.internal.telephony.metrics;
 
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ROAMING_SATELLITE_SESSION;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SHORT_CODE_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_CONFIG_UPDATER;
+import static com.android.internal.telephony.TelephonyStatsLog.SATELLITE_ENTITLEMENT;
 import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
 import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -45,9 +49,13 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteSession;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteConfigUpdater;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteEntitlement;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -115,7 +123,7 @@
         mFeatureFlags = mock(FeatureFlags.class);
         mMetricsCollector =
                 new MetricsCollector(mContext, mPersistAtomsStorage,
-                        mDeviceStateHelper, mVonrHelper, mFeatureFlags);
+                        mDeviceStateHelper, mVonrHelper, mDefaultNetworkMonitor, mFeatureFlags);
         doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
         doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
     }
@@ -472,4 +480,178 @@
         assertThat(actualAtoms).hasSize(4);
         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
     }
+
+    @Test
+    public void onPullAtom_carrierRoamingSatelliteSession_empty() {
+        doReturn(new CarrierRoamingSatelliteSession[0]).when(mPersistAtomsStorage)
+                .getCarrierRoamingSatelliteSessionStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(CARRIER_ROAMING_SATELLITE_SESSION, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_carrierRoamingSatelliteSession_tooFrequent() {
+        doReturn(null).when(mPersistAtomsStorage)
+                .getCarrierRoamingSatelliteSessionStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(CARRIER_ROAMING_SATELLITE_SESSION, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+        verify(mPersistAtomsStorage, times(1))
+                .getCarrierRoamingSatelliteSessionStats(eq(MIN_COOLDOWN_MILLIS));
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onPullAtom_carrierRoamingSatelliteSession_multipleAtoms() {
+        CarrierRoamingSatelliteSession carrierRoamingSatelliteSession =
+                new CarrierRoamingSatelliteSession();
+        doReturn(new CarrierRoamingSatelliteSession[] {carrierRoamingSatelliteSession,
+                carrierRoamingSatelliteSession, carrierRoamingSatelliteSession,
+                carrierRoamingSatelliteSession})
+                .when(mPersistAtomsStorage)
+                .getCarrierRoamingSatelliteSessionStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(CARRIER_ROAMING_SATELLITE_SESSION, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(4);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_carrierRoamingSatelliteControllerStats_empty() {
+        doReturn(new CarrierRoamingSatelliteControllerStats[0]).when(mPersistAtomsStorage)
+                .getCarrierRoamingSatelliteControllerStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS,
+                actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_carrierRoamingSatelliteControllerStats_multipleAtoms() {
+        CarrierRoamingSatelliteControllerStats carrierRoamingSatelliteControllerStats =
+                new CarrierRoamingSatelliteControllerStats();
+        doReturn(new CarrierRoamingSatelliteControllerStats[] {
+                carrierRoamingSatelliteControllerStats})
+                .when(mPersistAtomsStorage)
+                .getCarrierRoamingSatelliteControllerStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS,
+                actualAtoms);
+
+        assertThat(actualAtoms).hasSize(1);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_carrierRoamingSatelliteControllerStats_tooFrequent() {
+        doReturn(null).when(mPersistAtomsStorage)
+                .getCarrierRoamingSatelliteControllerStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS,
+                actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+        verify(mPersistAtomsStorage, times(1))
+                .getCarrierRoamingSatelliteControllerStats(eq(MIN_COOLDOWN_MILLIS));
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onPullAtom_satelliteEntitlement_empty() {
+        doReturn(new SatelliteEntitlement[0]).when(mPersistAtomsStorage)
+                .getSatelliteEntitlementStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(SATELLITE_ENTITLEMENT, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_satelliteEntitlement_tooFrequent() {
+        doReturn(null).when(mPersistAtomsStorage).getSatelliteEntitlementStats(
+                anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(SATELLITE_ENTITLEMENT, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+        verify(mPersistAtomsStorage, times(1))
+                .getSatelliteEntitlementStats(eq(MIN_COOLDOWN_MILLIS));
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onPullAtom_satelliteEntitlement_multipleAtoms() {
+        SatelliteEntitlement satelliteEntitlement = new SatelliteEntitlement();
+        doReturn(new SatelliteEntitlement[] {satelliteEntitlement, satelliteEntitlement,
+                satelliteEntitlement, satelliteEntitlement})
+                .when(mPersistAtomsStorage)
+                .getSatelliteEntitlementStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(SATELLITE_ENTITLEMENT, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(4);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_satelliteConfigUpdater_empty() {
+        doReturn(new SatelliteConfigUpdater[0]).when(mPersistAtomsStorage)
+                .getSatelliteConfigUpdaterStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(SATELLITE_CONFIG_UPDATER, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
+
+    @Test
+    public void onPullAtom_satelliteConfigUpdater_tooFrequent() {
+        doReturn(null).when(mPersistAtomsStorage).getSatelliteConfigUpdaterStats(
+                anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(SATELLITE_CONFIG_UPDATER, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(0);
+        assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+        verify(mPersistAtomsStorage, times(1))
+                .getSatelliteConfigUpdaterStats(eq(MIN_COOLDOWN_MILLIS));
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onPullAtom_satelliteConfigUpdater_multipleAtoms() {
+        SatelliteConfigUpdater satelliteConfigUpdater = new SatelliteConfigUpdater();
+        doReturn(new SatelliteConfigUpdater[] {satelliteConfigUpdater, satelliteConfigUpdater,
+                satelliteConfigUpdater, satelliteConfigUpdater})
+                .when(mPersistAtomsStorage)
+                .getSatelliteConfigUpdaterStats(anyLong());
+        List<StatsEvent> actualAtoms = new ArrayList<>();
+
+        int result = mMetricsCollector.onPullAtom(SATELLITE_CONFIG_UPDATER, actualAtoms);
+
+        assertThat(actualAtoms).hasSize(4);
+        assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+    }
 }
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 16a4f0d..8ee3a37 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -20,6 +20,7 @@
 import static android.telephony.SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY;
 import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
 import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 
@@ -44,6 +45,10 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
 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.satellite.SatelliteConstants.ACCESS_CONTROL_TYPE_CURRENT_LOCATION;
+import static com.android.internal.telephony.satellite.SatelliteConstants.ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE;
+import static com.android.internal.telephony.satellite.SatelliteConstants.CONFIG_DATA_SOURCE_CONFIG_UPDATER;
+import static com.android.internal.telephony.satellite.SatelliteConstants.CONFIG_DATA_SOURCE_DEVICE_CONFIG;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -76,6 +81,8 @@
 
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteSession;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
@@ -94,7 +101,10 @@
 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteAccessController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteConfigUpdater;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteEntitlement;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
@@ -125,6 +135,7 @@
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.Queue;
+import java.util.concurrent.TimeUnit;
 
 public class PersistAtomsStorageTest extends TelephonyTest {
     private static final String TEST_FILE = "PersistAtomsStorageTest.pb";
@@ -296,6 +307,26 @@
     private DataNetworkValidation mDataNetworkValidationIwlan2;
     private DataNetworkValidation[] mDataNetworkValidations;
 
+    private CarrierRoamingSatelliteSession mCarrierRoamingSatelliteSession1;
+    private CarrierRoamingSatelliteSession mCarrierRoamingSatelliteSession2;
+    private CarrierRoamingSatelliteSession[] mCarrierRoamingSatelliteSessions;
+
+    private CarrierRoamingSatelliteControllerStats mCarrierRoamingSatelliteControllerStats1;
+    private CarrierRoamingSatelliteControllerStats mCarrierRoamingSatelliteControllerStats2;
+    private CarrierRoamingSatelliteControllerStats[] mCarrierRoamingSatelliteControllerStats;
+
+    private SatelliteEntitlement mSatelliteEntitlement1;
+    private SatelliteEntitlement mSatelliteEntitlement2;
+    private SatelliteEntitlement[] mSatelliteEntitlements;
+
+    private SatelliteConfigUpdater mSatelliteConfigUpdater1;
+    private SatelliteConfigUpdater mSatelliteConfigUpdater2;
+    private SatelliteConfigUpdater[] mSatelliteConfigUpdaters;
+
+    private SatelliteAccessController mSatelliteAccessController1;
+    private SatelliteAccessController mSatelliteAccessController2;
+    private SatelliteAccessController[] mSatelliteAccessControllers;
+
     private void makeTestData() {
         // MO call with SRVCC (LTE to UMTS)
         mCall1Proto = new VoiceCallSession();
@@ -1002,6 +1033,7 @@
         mIncomingSms1.count = 1;
         mIncomingSms1.isManagedProfile = false;
         mIncomingSms1.isNtn = false;
+        mIncomingSms1.isEmergency = true;
 
         mIncomingSms2 = new IncomingSms();
         mIncomingSms2.smsFormat = INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2;
@@ -1021,6 +1053,7 @@
         mIncomingSms2.count = 2;
         mIncomingSms2.isManagedProfile = true;
         mIncomingSms2.isNtn = true;
+        mIncomingSms2.isEmergency = true;
 
         mIncomingSms = new IncomingSms[] {mIncomingSms1, mIncomingSms2};
 
@@ -1163,6 +1196,7 @@
         mSatelliteSession1.countOfIncomingDatagramSuccess = 1;
         mSatelliteSession1.countOfIncomingDatagramFailed = 0;
         mSatelliteSession1.isDemoMode = false;
+        mSatelliteSession1.maxNtnSignalStrengthLevel = 2;
 
         mSatelliteSession2 = new SatelliteSession();
         mSatelliteSession2.satelliteServiceInitializationResult =
@@ -1170,16 +1204,17 @@
         mSatelliteSession2.satelliteTechnology =
                 SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
         mSatelliteSession2.count = 1;
-        mSatelliteSession1.satelliteServiceTerminationResult =
+        mSatelliteSession2.satelliteServiceTerminationResult =
                 SatelliteProtoEnums.SATELLITE_ERROR_NONE;
-        mSatelliteSession1.initializationProcessingTimeMillis = 300;
-        mSatelliteSession1.terminationProcessingTimeMillis = 100;
-        mSatelliteSession1.sessionDurationSeconds = 10;
-        mSatelliteSession1.countOfOutgoingDatagramSuccess = 0;
-        mSatelliteSession1.countOfOutgoingDatagramFailed = 2;
-        mSatelliteSession1.countOfIncomingDatagramSuccess = 0;
-        mSatelliteSession1.countOfIncomingDatagramFailed = 1;
+        mSatelliteSession2.initializationProcessingTimeMillis = 300;
+        mSatelliteSession2.terminationProcessingTimeMillis = 100;
+        mSatelliteSession2.sessionDurationSeconds = 10;
+        mSatelliteSession2.countOfOutgoingDatagramSuccess = 0;
+        mSatelliteSession2.countOfOutgoingDatagramFailed = 2;
+        mSatelliteSession2.countOfIncomingDatagramSuccess = 0;
+        mSatelliteSession2.countOfIncomingDatagramFailed = 1;
         mSatelliteSession2.isDemoMode = true;
+        mSatelliteSession2.maxNtnSignalStrengthLevel = 4;
 
         mSatelliteSessions =
                 new SatelliteSession[] {
@@ -1268,6 +1303,132 @@
                 new SatelliteSosMessageRecommender[] {
                         mSatelliteSosMessageRecommender1, mSatelliteSosMessageRecommender2
                 };
+
+        mCarrierRoamingSatelliteSession1 = new CarrierRoamingSatelliteSession();
+        mCarrierRoamingSatelliteSession1.carrierId = 1;
+        mCarrierRoamingSatelliteSession1.isNtnRoamingInHomeCountry = false;
+        mCarrierRoamingSatelliteSession1.totalSatelliteModeTimeSec = 60;
+        mCarrierRoamingSatelliteSession1.numberOfSatelliteConnections = 3;
+        mCarrierRoamingSatelliteSession1.avgDurationOfSatelliteConnectionSec = 20;
+        mCarrierRoamingSatelliteSession1.satelliteConnectionGapMinSec = 2;
+        mCarrierRoamingSatelliteSession1.satelliteConnectionGapAvgSec = 5;
+        mCarrierRoamingSatelliteSession1.satelliteConnectionGapMaxSec = 8;
+        mCarrierRoamingSatelliteSession1.rsrpAvg = 3;
+        mCarrierRoamingSatelliteSession1.rsrpMedian = 2;
+        mCarrierRoamingSatelliteSession1.rssnrAvg = 5;
+        mCarrierRoamingSatelliteSession1.rssnrMedian = 3;
+        mCarrierRoamingSatelliteSession1.countOfIncomingSms = 2;
+        mCarrierRoamingSatelliteSession1.countOfOutgoingSms = 4;
+        mCarrierRoamingSatelliteSession1.countOfIncomingMms = 1;
+        mCarrierRoamingSatelliteSession1.countOfOutgoingMms = 1;
+
+        mCarrierRoamingSatelliteSession2 = new CarrierRoamingSatelliteSession();
+        mCarrierRoamingSatelliteSession2.carrierId = 2;
+        mCarrierRoamingSatelliteSession2.isNtnRoamingInHomeCountry = true;
+        mCarrierRoamingSatelliteSession2.totalSatelliteModeTimeSec = 120;
+        mCarrierRoamingSatelliteSession2.numberOfSatelliteConnections = 5;
+        mCarrierRoamingSatelliteSession2.avgDurationOfSatelliteConnectionSec = 20;
+        mCarrierRoamingSatelliteSession2.satelliteConnectionGapMinSec = 2;
+        mCarrierRoamingSatelliteSession2.satelliteConnectionGapAvgSec = 5;
+        mCarrierRoamingSatelliteSession2.satelliteConnectionGapMaxSec = 8;
+        mCarrierRoamingSatelliteSession2.rsrpAvg = 3;
+        mCarrierRoamingSatelliteSession2.rsrpMedian = 2;
+        mCarrierRoamingSatelliteSession2.rssnrAvg = 8;
+        mCarrierRoamingSatelliteSession2.rssnrMedian = 15;
+        mCarrierRoamingSatelliteSession2.countOfIncomingSms = 2;
+        mCarrierRoamingSatelliteSession2.countOfOutgoingSms = 4;
+        mCarrierRoamingSatelliteSession2.countOfIncomingMms = 1;
+        mCarrierRoamingSatelliteSession2.countOfOutgoingMms = 1;
+
+        mCarrierRoamingSatelliteSessions = new CarrierRoamingSatelliteSession[] {
+                mCarrierRoamingSatelliteSession1, mCarrierRoamingSatelliteSession2};
+
+        mCarrierRoamingSatelliteControllerStats1 = new CarrierRoamingSatelliteControllerStats();
+        mCarrierRoamingSatelliteControllerStats1.configDataSource =
+                SatelliteProtoEnums.CONFIG_DATA_SOURCE_ENTITLEMENT;
+        mCarrierRoamingSatelliteControllerStats1.countOfEntitlementStatusQueryRequest = 2;
+        mCarrierRoamingSatelliteControllerStats1.countOfSatelliteConfigUpdateRequest = 1;
+        mCarrierRoamingSatelliteControllerStats1.countOfSatelliteNotificationDisplayed = 1;
+        mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMinSec = 2;
+        mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapAvgSec = 3;
+        mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMaxSec = 4;
+
+        mCarrierRoamingSatelliteControllerStats2 = new CarrierRoamingSatelliteControllerStats();
+        mCarrierRoamingSatelliteControllerStats2.configDataSource =
+                SatelliteProtoEnums.CONFIG_DATA_SOURCE_CONFIG_UPDATER;
+        mCarrierRoamingSatelliteControllerStats2.countOfEntitlementStatusQueryRequest = 4;
+        mCarrierRoamingSatelliteControllerStats2.countOfSatelliteConfigUpdateRequest = 1;
+        mCarrierRoamingSatelliteControllerStats2.countOfSatelliteNotificationDisplayed = 1;
+        mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMinSec = 5;
+        mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec = 10;
+        mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec = 15;
+
+        // CarrierRoamingSatelliteController has one data point
+        mCarrierRoamingSatelliteControllerStats = new CarrierRoamingSatelliteControllerStats[] {
+                mCarrierRoamingSatelliteControllerStats1};
+
+        mSatelliteEntitlement1 = new SatelliteEntitlement();
+        mSatelliteEntitlement1.carrierId = 1;
+        mSatelliteEntitlement1.result = 0;
+        mSatelliteEntitlement1.entitlementStatus =
+                SatelliteProtoEnums.SATELLITE_ENTITLEMENT_STATUS_ENABLED;
+        mSatelliteEntitlement1.isRetry = false;
+        mSatelliteEntitlement1.count = 1;
+
+        mSatelliteEntitlement2 = new SatelliteEntitlement();
+        mSatelliteEntitlement2.carrierId = 2;
+        mSatelliteEntitlement2.result = 1;
+        mSatelliteEntitlement2.entitlementStatus =
+                SatelliteProtoEnums.SATELLITE_ENTITLEMENT_STATUS_DISABLED;
+        mSatelliteEntitlement1.isRetry = true;
+        mSatelliteEntitlement2.count = 1;
+
+        mSatelliteEntitlements = new SatelliteEntitlement[] {mSatelliteEntitlement1,
+                mSatelliteEntitlement2};
+
+        mSatelliteConfigUpdater1 = new SatelliteConfigUpdater();
+        mSatelliteConfigUpdater1.configVersion = 1;
+        mSatelliteConfigUpdater1.oemConfigResult = SatelliteProtoEnums.CONFIG_UPDATE_RESULT_SUCCESS;
+        mSatelliteConfigUpdater1.carrierConfigResult =
+                SatelliteProtoEnums.CONFIG_UPDATE_RESULT_CARRIER_DATA_INVALID_PLMN;
+        mSatelliteConfigUpdater1.count = 1;
+
+        mSatelliteConfigUpdater2 = new SatelliteConfigUpdater();
+        mSatelliteConfigUpdater2.configVersion = 2;
+        mSatelliteConfigUpdater2.oemConfigResult =
+                SatelliteProtoEnums.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_COUNTRY_CODE;
+        mSatelliteConfigUpdater2.carrierConfigResult =
+                SatelliteProtoEnums.CONFIG_UPDATE_RESULT_SUCCESS;
+        mSatelliteConfigUpdater2.count = 1;
+
+        mSatelliteConfigUpdaters = new SatelliteConfigUpdater[] {mSatelliteConfigUpdater1,
+                mSatelliteConfigUpdater2};
+
+        mSatelliteAccessController1 = new SatelliteAccessController();
+        mSatelliteAccessController1.accessControlType = ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE;
+        mSatelliteAccessController1.locationQueryTimeMillis = TimeUnit.SECONDS.toMillis(1);
+        mSatelliteAccessController1.onDeviceLookupTimeMillis = TimeUnit.SECONDS.toMillis(2);
+        mSatelliteAccessController1.totalCheckingTimeMillis = TimeUnit.SECONDS.toMillis(3);
+        mSatelliteAccessController1.isAllowed = true;
+        mSatelliteAccessController1.isEmergency = false;
+        mSatelliteAccessController1.resultCode = SATELLITE_RESULT_SUCCESS;
+        mSatelliteAccessController1.countryCodes = new String[]{"AB", "CD"};
+        mSatelliteAccessController1.configDataSource = CONFIG_DATA_SOURCE_DEVICE_CONFIG;
+
+        mSatelliteAccessController2 = new SatelliteAccessController();
+        mSatelliteAccessController1.accessControlType = ACCESS_CONTROL_TYPE_CURRENT_LOCATION;
+        mSatelliteAccessController1.locationQueryTimeMillis = TimeUnit.SECONDS.toMillis(4);
+        mSatelliteAccessController2.onDeviceLookupTimeMillis = TimeUnit.SECONDS.toMillis(5);
+        mSatelliteAccessController2.totalCheckingTimeMillis = TimeUnit.SECONDS.toMillis(6);
+        mSatelliteAccessController2.isAllowed = false;
+        mSatelliteAccessController2.isEmergency = true;
+        mSatelliteAccessController2.resultCode = SATELLITE_RESULT_SUCCESS;
+        mSatelliteAccessController2.countryCodes = new String[]{"EF", "GH"};
+        mSatelliteAccessController2.configDataSource = CONFIG_DATA_SOURCE_CONFIG_UPDATER;
+
+        mSatelliteAccessControllers = new SatelliteAccessController[]{
+                mSatelliteAccessController1, mSatelliteAccessController2
+        };
     }
 
     private void generateTestDataNetworkValidationsData() {
@@ -1494,6 +1655,21 @@
         mDataNetworkValidationLte2 = null;
         mDataNetworkValidationIwlan1 = null;
         mDataNetworkValidationIwlan2 = null;
+        mCarrierRoamingSatelliteSession1 = null;
+        mCarrierRoamingSatelliteSession2 = null;
+        mCarrierRoamingSatelliteSessions = null;
+        mCarrierRoamingSatelliteControllerStats1 = null;
+        mCarrierRoamingSatelliteControllerStats2 = null;
+        mCarrierRoamingSatelliteControllerStats = null;
+        mSatelliteEntitlement1 = null;
+        mSatelliteEntitlement2 = null;
+        mSatelliteEntitlements = null;
+        mSatelliteConfigUpdater1 = null;
+        mSatelliteConfigUpdater2 = null;
+        mSatelliteConfigUpdaters = null;
+        mSatelliteAccessController1 = null;
+        mSatelliteAccessController2 = null;
+        mSatelliteAccessControllers = null;
         super.tearDown();
     }
 
@@ -4649,6 +4825,462 @@
     }
 
     @Test
+    public void addCarrierRoamingSatelliteSessionStats_emptyProto() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteSessionStats(
+                mCarrierRoamingSatelliteSession1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        CarrierRoamingSatelliteSession[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteSessionStats(0L);
+        assertProtoArrayEquals(new CarrierRoamingSatelliteSession[] {
+                mCarrierRoamingSatelliteSession1}, output);
+    }
+
+    @Test
+    public void addCarrierRoamingSatelliteSessionStats_withExistingEntries() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteSessionStats(
+                mCarrierRoamingSatelliteSession1);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteSessionStats(
+                mCarrierRoamingSatelliteSession2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        CarrierRoamingSatelliteSession[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteSessionStats(0L);
+        assertProtoArrayEqualsIgnoringOrder(
+                new CarrierRoamingSatelliteSession[] {mCarrierRoamingSatelliteSession2}, output);
+    }
+
+    @Test
+    public void addCarrierRoamingSatelliteSessionStats_tooManyEntries() throws Exception {
+        createEmptyTestFile();
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        // Store atoms up to maximum number + 1
+        int maxCount = 1 + 1;
+        for (int i = 0; i < maxCount; i++) {
+            mPersistAtomsStorage.addCarrierRoamingSatelliteSessionStats(
+                    copyOf(mCarrierRoamingSatelliteSession1));
+            mPersistAtomsStorage.incTimeMillis(100L);
+        }
+
+        // Store 1 different atom
+        mPersistAtomsStorage.addCarrierRoamingSatelliteSessionStats(
+                mCarrierRoamingSatelliteSession2);
+
+        verifyCurrentStateSavedToFileOnce();
+
+        CarrierRoamingSatelliteSession[] result =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteSessionStats(0L);
+
+        // First atom has count 0, the other has 1
+        assertHasStatsAndCount(result, mCarrierRoamingSatelliteSession1, 0);
+        assertHasStatsAndCount(result, mCarrierRoamingSatelliteSession2, 1);
+    }
+
+    @Test
+    public void getCarrierRoamingSatelliteSessionStats_tooFrequent() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+        CarrierRoamingSatelliteSession[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteSessionStats(100L);
+
+        // Should be denied
+        assertNull(output);
+    }
+
+    @Test
+    public void getCarrierRoamingSatelliteSessionStats_withSavedAtoms() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        CarrierRoamingSatelliteSession[] carrierRoamingSatelliteSessionStatsList1 =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteSessionStats(50L);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        CarrierRoamingSatelliteSession[] carrierRoamingSatelliteSessionStatsList2 =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteSessionStats(50L);
+
+        // First set of results should be equal to file contents.
+        CarrierRoamingSatelliteSession[] expectedList = new CarrierRoamingSatelliteSession[] {
+                mCarrierRoamingSatelliteSession1, mCarrierRoamingSatelliteSession2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList, carrierRoamingSatelliteSessionStatsList1);
+        // Second set of results should be empty.
+        assertProtoArrayEquals(new CarrierRoamingSatelliteSession[0],
+                carrierRoamingSatelliteSessionStatsList2);
+        // Corresponding pull timestamp should be updated and saved.
+        assertEquals(START_TIME_MILLIS + 200L, mPersistAtomsStorage
+                .getAtomsProto().carrierRoamingSatelliteSessionPullTimestampMillis);
+        InOrder inOrder = inOrder(mTestFileOutputStream);
+        assertEquals(START_TIME_MILLIS + 100L,
+                getAtomsWritten(inOrder).carrierRoamingSatelliteSessionPullTimestampMillis);
+        assertEquals(START_TIME_MILLIS + 200L,
+                getAtomsWritten(inOrder).carrierRoamingSatelliteSessionPullTimestampMillis);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void addCarrierRoamingSatelliteControllerStats_emptyProto() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
+                mCarrierRoamingSatelliteControllerStats1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        CarrierRoamingSatelliteControllerStats[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(0L);
+        assertProtoArrayEquals(new CarrierRoamingSatelliteControllerStats[] {
+                mCarrierRoamingSatelliteControllerStats1}, output);
+    }
+
+    @Test
+    public void addCarrierRoamingSatelliteControllerStats_withExistingEntries() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
+                mCarrierRoamingSatelliteControllerStats1);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
+                mCarrierRoamingSatelliteControllerStats2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        CarrierRoamingSatelliteControllerStats expected =
+                new CarrierRoamingSatelliteControllerStats();
+        expected.configDataSource = mCarrierRoamingSatelliteControllerStats2.configDataSource;
+        expected.countOfEntitlementStatusQueryRequest =
+                mCarrierRoamingSatelliteControllerStats1.countOfEntitlementStatusQueryRequest
+                        + mCarrierRoamingSatelliteControllerStats2
+                        .countOfEntitlementStatusQueryRequest;
+        expected.countOfSatelliteConfigUpdateRequest =
+                mCarrierRoamingSatelliteControllerStats1.countOfSatelliteConfigUpdateRequest
+                        + mCarrierRoamingSatelliteControllerStats2
+                        .countOfSatelliteConfigUpdateRequest;
+        expected.countOfSatelliteNotificationDisplayed =
+                mCarrierRoamingSatelliteControllerStats1.countOfSatelliteNotificationDisplayed
+                + mCarrierRoamingSatelliteControllerStats2
+                        .countOfSatelliteNotificationDisplayed;
+        expected.satelliteSessionGapMinSec =
+                mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMinSec;
+        expected.satelliteSessionGapAvgSec =
+                mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec;
+        expected.satelliteSessionGapMaxSec =
+                mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec;
+
+        verifyCurrentStateSavedToFileOnce();
+        CarrierRoamingSatelliteControllerStats[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(0L);
+        assertHasStats(output, expected);
+    }
+
+    @Test
+    public void getCarrierRoamingSatelliteControllerStats_tooFrequent() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+        CarrierRoamingSatelliteControllerStats[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(100L);
+
+        // Should be denied
+        assertNull(output);
+    }
+
+
+    @Test
+    public void addSatelliteEntitlementStats_emptyProto() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteEntitlementStats(mSatelliteEntitlement1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteEntitlement[] output =
+                mPersistAtomsStorage.getSatelliteEntitlementStats(0L);
+        assertProtoArrayEquals(new SatelliteEntitlement[] {mSatelliteEntitlement1}, output);
+    }
+
+    @Test
+    public void addSatelliteEntitlementStats_withExistingEntries() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteEntitlementStats(mSatelliteEntitlement1);
+        mPersistAtomsStorage.addSatelliteEntitlementStats(mSatelliteEntitlement2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteEntitlement[] output =
+                mPersistAtomsStorage.getSatelliteEntitlementStats(0L);
+        assertProtoArrayEqualsIgnoringOrder(
+                new SatelliteEntitlement[] {
+                        mSatelliteEntitlement1, mSatelliteEntitlement2}, output);
+    }
+
+    @Test
+    public void addSatelliteEntitlementStats_updateExistingEntries() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteEntitlementStats(copyOf(mSatelliteEntitlement1));
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // Count should be increased by 1.
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteEntitlement newSatelliteEntitlement1 = copyOf(mSatelliteEntitlement1);
+        newSatelliteEntitlement1.count = 2;
+        SatelliteEntitlement[] expectedList = new SatelliteEntitlement[] {newSatelliteEntitlement1,
+                mSatelliteEntitlement2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList,
+                mPersistAtomsStorage.getSatelliteEntitlementStats(0L));
+    }
+
+    @Test
+    public void addSatelliteEntitlementStats_tooManyEntries() throws Exception {
+        createEmptyTestFile();
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        // Store atoms up to maximum number + 1
+        int maxCount = 15 + 1;
+        for (int i = 0; i < maxCount; i++) {
+            mPersistAtomsStorage.addSatelliteEntitlementStats(mSatelliteEntitlement1);
+            mPersistAtomsStorage.incTimeMillis(100L);
+        }
+
+        // Store 1 different atom
+        mPersistAtomsStorage.addSatelliteEntitlementStats(mSatelliteEntitlement2);
+
+        verifyCurrentStateSavedToFileOnce();
+
+        SatelliteEntitlement[] result =
+                mPersistAtomsStorage.getSatelliteEntitlementStats(0L);
+
+        // First atom has count 14, the other has 1
+        assertHasStatsAndCount(result, mSatelliteEntitlement1, 16);
+        assertHasStatsAndCount(result, mSatelliteEntitlement2, 1);
+    }
+
+    @Test
+    public void getSatelliteEntitlementStats_tooFrequent() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+        SatelliteEntitlement[] output =
+                mPersistAtomsStorage.getSatelliteEntitlementStats(100L);
+
+        // Should be denied
+        assertNull(output);
+    }
+
+    @Test
+    public void getSatelliteEntitlementStats_withSavedAtoms() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        SatelliteEntitlement[] satelliteEntitlementStatsList1 =
+                mPersistAtomsStorage.getSatelliteEntitlementStats(50L);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        SatelliteEntitlement[] satelliteEntitlementStatsList2 =
+                mPersistAtomsStorage.getSatelliteEntitlementStats(50L);
+
+        // First set of results should be equal to file contents.
+        SatelliteEntitlement[] expectedList = new SatelliteEntitlement[] {
+                mSatelliteEntitlement1, mSatelliteEntitlement2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList, satelliteEntitlementStatsList1);
+        // Second set of results should be empty.
+        assertProtoArrayEquals(new SatelliteEntitlement[0], satelliteEntitlementStatsList2);
+        // Corresponding pull timestamp should be updated and saved.
+        assertEquals(START_TIME_MILLIS + 200L, mPersistAtomsStorage
+                .getAtomsProto().satelliteEntitlementPullTimestampMillis);
+        InOrder inOrder = inOrder(mTestFileOutputStream);
+        assertEquals(START_TIME_MILLIS + 100L,
+                getAtomsWritten(inOrder).satelliteEntitlementPullTimestampMillis);
+        assertEquals(START_TIME_MILLIS + 200L,
+                getAtomsWritten(inOrder).satelliteEntitlementPullTimestampMillis);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void addSatelliteConfigUpdaterStats_emptyProto() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteConfigUpdaterStats(mSatelliteConfigUpdater1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteConfigUpdater[] output =
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(0L);
+        assertProtoArrayEquals(new SatelliteConfigUpdater[] {mSatelliteConfigUpdater1}, output);
+    }
+
+    @Test
+    public void addSatelliteConfigUpdaterStats_withExistingEntries() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteConfigUpdaterStats(mSatelliteConfigUpdater1);
+        mPersistAtomsStorage.addSatelliteConfigUpdaterStats(mSatelliteConfigUpdater2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteConfigUpdater[] output =
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(0L);
+        assertProtoArrayEqualsIgnoringOrder(new SatelliteConfigUpdater[] {
+                mSatelliteConfigUpdater1, mSatelliteConfigUpdater2}, output);
+    }
+
+    @Test
+    public void addSatelliteConfigUpdaterStats_updateExistingEntries() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteConfigUpdaterStats(copyOf(mSatelliteConfigUpdater1));
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // Count should be increased by 1.
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteConfigUpdater newSatelliteConfigUpdater1 = copyOf(mSatelliteConfigUpdater1);
+        newSatelliteConfigUpdater1.count = 2;
+        SatelliteConfigUpdater[] expectedList = new SatelliteConfigUpdater[] {
+                newSatelliteConfigUpdater1, mSatelliteConfigUpdater2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList,
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(0L));
+    }
+
+    @Test
+    public void addSatelliteConfigUpdaterStats_tooManyEntries() throws Exception {
+        createEmptyTestFile();
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        // Store atoms up to maximum number + 1
+        int maxCount = 15 + 1;
+        for (int i = 0; i < maxCount; i++) {
+            mPersistAtomsStorage.addSatelliteConfigUpdaterStats(mSatelliteConfigUpdater1);
+            mPersistAtomsStorage.incTimeMillis(100L);
+        }
+
+        // Store 1 different atom
+        mPersistAtomsStorage.addSatelliteConfigUpdaterStats(mSatelliteConfigUpdater2);
+
+        verifyCurrentStateSavedToFileOnce();
+
+        SatelliteConfigUpdater[] result =
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(0L);
+
+        // First atom has count 14, the other has 1
+        assertHasStatsAndCount(result, mSatelliteConfigUpdater1, 16);
+        assertHasStatsAndCount(result, mSatelliteConfigUpdater2, 1);
+    }
+
+    @Test
+    public void getSatelliteConfigUpdaterStats_tooFrequent() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+        SatelliteConfigUpdater[] output =
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(100L);
+
+        // Should be denied
+        assertNull(output);
+    }
+
+    @Test
+    public void getSatelliteConfigUpdaterStats_withSavedAtoms() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        SatelliteConfigUpdater[] satelliteConfigUpdaterStatsList1 =
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(50L);
+        mPersistAtomsStorage.incTimeMillis(100L);
+        SatelliteConfigUpdater[] satelliteConfigUpdaterStatsList2 =
+                mPersistAtomsStorage.getSatelliteConfigUpdaterStats(50L);
+
+        // First set of results should be equal to file contents.
+        SatelliteConfigUpdater[] expectedList = new SatelliteConfigUpdater[] {
+                mSatelliteConfigUpdater1, mSatelliteConfigUpdater2};
+        assertProtoArrayEqualsIgnoringOrder(expectedList, satelliteConfigUpdaterStatsList1);
+        // Second set of results should be empty.
+        assertProtoArrayEquals(new SatelliteConfigUpdater[0], satelliteConfigUpdaterStatsList2);
+        // Corresponding pull timestamp should be updated and saved.
+        assertEquals(START_TIME_MILLIS + 200L, mPersistAtomsStorage
+                .getAtomsProto().satelliteConfigUpdaterPullTimestampMillis);
+        InOrder inOrder = inOrder(mTestFileOutputStream);
+        assertEquals(START_TIME_MILLIS + 100L,
+                getAtomsWritten(inOrder).satelliteConfigUpdaterPullTimestampMillis);
+        assertEquals(START_TIME_MILLIS + 200L,
+                getAtomsWritten(inOrder).satelliteConfigUpdaterPullTimestampMillis);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void addSatelliteAccessControllerStats_emptyProto() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteAccessControllerStats(
+                mSatelliteAccessController1);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        // Service state and service switch should be added successfully
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteAccessController[] output =
+                mPersistAtomsStorage.getSatelliteAccessControllerStats(0L);
+        assertProtoArrayEquals(
+                new SatelliteAccessController[] {mSatelliteAccessController1}, output);
+    }
+
+    @Test
+    public void addSatelliteAccessControllerStats_tooManyEntries() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+        // Store atoms up to maximum number + 1
+        int maxCount = 15 + 1;
+        for (int i = 0; i < maxCount; i++) {
+            mPersistAtomsStorage
+                    .addSatelliteAccessControllerStats(//mSatelliteAccessController1);
+                            copyOf(mSatelliteAccessController1));
+            mPersistAtomsStorage.incTimeMillis(100L);
+        }
+
+        // Store 1 different atom
+        mPersistAtomsStorage
+                .addSatelliteAccessControllerStats(mSatelliteAccessController2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        verifyCurrentStateSavedToFileOnce();
+
+        SatelliteAccessController[] result =
+                mPersistAtomsStorage.getSatelliteAccessControllerStats(0L);
+        // First atom should have count 14, the other should have 1
+        assertHasStatsAndCount(result, mSatelliteAccessController1, 14);
+        assertHasStatsAndCount(result, mSatelliteAccessController2, 1);
+    }
+
+    @Test
+    public void getSatelliteAccessControllerStats_tooFrequent() throws Exception {
+        createTestFile(START_TIME_MILLIS);
+
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+        SatelliteAccessController[] output =
+                mPersistAtomsStorage.getSatelliteAccessControllerStats(100L);
+
+        // Should be denied
+        assertNull(output);
+    }
+
+    @Test
     @SmallTest
     public void addDataNetworkValidation_newEntry() throws Exception {
         createEmptyTestFile();
@@ -4806,6 +5438,15 @@
         atoms.satelliteSosMessageRecommenderPullTimestampMillis = lastPullTimeMillis;
         atoms.dataNetworkValidation = mDataNetworkValidations;
         atoms.dataNetworkValidationPullTimestampMillis = lastPullTimeMillis;
+        atoms.carrierRoamingSatelliteSession = mCarrierRoamingSatelliteSessions;
+        atoms.carrierRoamingSatelliteSessionPullTimestampMillis = lastPullTimeMillis;
+        atoms.carrierRoamingSatelliteControllerStats = mCarrierRoamingSatelliteControllerStats;
+        atoms.carrierRoamingSatelliteControllerStatsPullTimestampMillis = lastPullTimeMillis;
+        atoms.satelliteEntitlement = mSatelliteEntitlements;
+        atoms.satelliteEntitlementPullTimestampMillis = lastPullTimeMillis;
+        atoms.satelliteConfigUpdater = mSatelliteConfigUpdaters;
+        atoms.satelliteConfigUpdaterPullTimestampMillis = lastPullTimeMillis;
+        atoms.satelliteAccessControllerPullTimestampMillis = lastPullTimeMillis;
         FileOutputStream stream = new FileOutputStream(mTestFile);
         stream.write(PersistAtoms.toByteArray(atoms));
         stream.close();
@@ -4974,6 +5615,29 @@
         return DataNetworkValidation.parseFrom(MessageNano.toByteArray(source));
     }
 
+    private static CarrierRoamingSatelliteSession copyOf(CarrierRoamingSatelliteSession source)
+            throws Exception {
+        return CarrierRoamingSatelliteSession.parseFrom(MessageNano.toByteArray(source));
+    }
+
+    private static CarrierRoamingSatelliteControllerStats copyOf(
+            CarrierRoamingSatelliteControllerStats source) throws Exception {
+        return CarrierRoamingSatelliteControllerStats.parseFrom(MessageNano.toByteArray(source));
+    }
+
+    private static SatelliteEntitlement copyOf(SatelliteEntitlement source) throws Exception {
+        return SatelliteEntitlement.parseFrom(MessageNano.toByteArray(source));
+    }
+
+    private static SatelliteConfigUpdater copyOf(SatelliteConfigUpdater source) throws Exception {
+        return SatelliteConfigUpdater.parseFrom(MessageNano.toByteArray(source));
+    }
+
+    private static SatelliteAccessController copyOf(SatelliteAccessController source)
+            throws Exception {
+        return SatelliteAccessController.parseFrom(MessageNano.toByteArray(source));
+    }
+
     private void assertAllPullTimestampEquals(long timestamp) {
         assertEquals(
                 timestamp,
@@ -5266,6 +5930,28 @@
         assertEquals(expectedCount, actualCount);
     }
 
+    private static void assertHasStatsAndCount(
+            SatelliteAccessController[] tested,
+            @Nullable SatelliteAccessController expectedStats, int expectedCount) {
+        assertNotNull(tested);
+        int actualCount = 0;
+        for (SatelliteAccessController stats : tested) {
+            if (stats.accessControlType
+                    == expectedStats.accessControlType
+                    && stats.locationQueryTimeMillis == expectedStats.locationQueryTimeMillis
+                    && stats.onDeviceLookupTimeMillis == expectedStats.onDeviceLookupTimeMillis
+                    && stats.totalCheckingTimeMillis == expectedStats.totalCheckingTimeMillis
+                    && stats.isAllowed == expectedStats.isAllowed
+                    && stats.isEmergency == expectedStats.isEmergency
+                    && stats.resultCode == expectedStats.resultCode
+                    && Arrays.equals(stats.countryCodes, expectedStats.countryCodes)
+                    && stats.configDataSource == expectedStats.configDataSource) {
+                actualCount++;
+            }
+        }
+        assertEquals(expectedCount, actualCount);
+    }
+
     private static void assertHasStatsAndCountDuration(
             RcsAcsProvisioningStats[] statses,
             @Nullable RcsAcsProvisioningStats expectedStats, int count, long duration) {
@@ -5422,7 +6108,8 @@
                     && incomingSms.carrierId == expectedIncomingSms.carrierId
                     && incomingSms.messageId == expectedIncomingSms.messageId
                     && incomingSms.isManagedProfile == expectedIncomingSms.isManagedProfile
-                    && incomingSms.isNtn == expectedIncomingSms.isNtn) {
+                    && incomingSms.isNtn == expectedIncomingSms.isNtn
+                    && incomingSms.isEmergency == expectedIncomingSms.isEmergency) {
                 actualCount = incomingSms.count;
             }
         }
@@ -5472,4 +6159,82 @@
         }
         assertEquals(expectedCount, actualCount);
     }
+
+    private static void assertHasStatsAndCount(CarrierRoamingSatelliteSession[] tested,
+            @Nullable CarrierRoamingSatelliteSession expectedStats, int expectedCount) {
+        assertNotNull(tested);
+        int actualCount = 0;
+        for (CarrierRoamingSatelliteSession stats : tested) {
+            if (stats.carrierId == expectedStats.carrierId
+                    && stats.isNtnRoamingInHomeCountry == expectedStats.isNtnRoamingInHomeCountry
+                    && stats.totalSatelliteModeTimeSec == expectedStats.totalSatelliteModeTimeSec
+                    && stats.numberOfSatelliteConnections
+                    == expectedStats.numberOfSatelliteConnections
+                    && stats.avgDurationOfSatelliteConnectionSec
+                    == expectedStats.avgDurationOfSatelliteConnectionSec
+                    && stats.satelliteConnectionGapMinSec
+                    == expectedStats.satelliteConnectionGapMinSec
+                    && stats.satelliteConnectionGapAvgSec
+                    == expectedStats.satelliteConnectionGapAvgSec
+                    && stats.satelliteConnectionGapMaxSec
+                    == expectedStats.satelliteConnectionGapMaxSec
+                    && stats.rsrpAvg == expectedStats.rsrpAvg
+                    && stats.rsrpMedian == expectedStats.rsrpMedian
+                    && stats.rssnrAvg == expectedStats.rssnrAvg
+                    && stats.rssnrMedian == expectedStats.rssnrMedian
+                    && stats.countOfIncomingSms == expectedStats.countOfIncomingSms
+                    && stats.countOfOutgoingSms == expectedStats.countOfOutgoingSms
+                    && stats.countOfIncomingMms == expectedStats.countOfIncomingMms
+                    && stats.countOfOutgoingMms == expectedStats.countOfOutgoingMms) {
+                actualCount++;
+            }
+        }
+        assertEquals(expectedCount, actualCount);
+    }
+
+    private static void assertHasStats(CarrierRoamingSatelliteControllerStats[] tested,
+            @Nullable CarrierRoamingSatelliteControllerStats expectedStats) {
+        assertNotNull(tested);
+        assertEquals(tested[0].configDataSource, expectedStats.configDataSource);
+        assertEquals(tested[0].countOfEntitlementStatusQueryRequest,
+                expectedStats.countOfEntitlementStatusQueryRequest);
+        assertEquals(tested[0].countOfSatelliteConfigUpdateRequest,
+                expectedStats.countOfSatelliteConfigUpdateRequest);
+        assertEquals(tested[0].countOfSatelliteNotificationDisplayed,
+                expectedStats.countOfSatelliteNotificationDisplayed);
+        assertEquals(tested[0].satelliteSessionGapMinSec, expectedStats.satelliteSessionGapMinSec);
+        assertEquals(tested[0].satelliteSessionGapAvgSec, expectedStats.satelliteSessionGapAvgSec);
+        assertEquals(tested[0].satelliteSessionGapMaxSec, expectedStats.satelliteSessionGapMaxSec);
+    }
+
+    private static void assertHasStatsAndCount(
+            SatelliteEntitlement[] tested,
+            @Nullable SatelliteEntitlement expectedStats, int expectedCount) {
+        assertNotNull(tested);
+        int actualCount = 0;
+        for (SatelliteEntitlement stats : tested) {
+            if (stats.carrierId == expectedStats.carrierId
+                    && stats.result == expectedStats.result
+                    && stats.entitlementStatus == expectedStats.entitlementStatus
+                    && stats.isRetry == expectedStats.isRetry) {
+                actualCount = stats.count;
+            }
+        }
+        assertEquals(expectedCount, actualCount);
+    }
+
+    private static void assertHasStatsAndCount(
+            SatelliteConfigUpdater[] tested,
+            @Nullable SatelliteConfigUpdater expectedStats, int expectedCount) {
+        assertNotNull(tested);
+        int actualCount = 0;
+        for (SatelliteConfigUpdater stats : tested) {
+            if (stats.configVersion == expectedStats.configVersion
+                    && stats.oemConfigResult == expectedStats.oemConfigResult
+                    && stats.carrierConfigResult == expectedStats.carrierConfigResult) {
+                actualCount = stats.count;
+            }
+        }
+        assertEquals(expectedCount, actualCount);
+    }
 }
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 9a84224..cda96ef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -16,6 +16,12 @@
 
 package com.android.internal.telephony.metrics;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+
+import static com.android.internal.telephony.satellite.SatelliteConstants.CONFIG_DATA_SOURCE_DEVICE_CONFIG;
+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.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -24,7 +30,12 @@
 import android.telephony.TelephonyProtoEnums;
 
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteControllerStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierRoamingSatelliteSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteAccessController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteConfigUpdater;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController;
+import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteEntitlement;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram;
 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision;
@@ -36,6 +47,8 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
+import java.util.concurrent.TimeUnit;
+
 public class SatelliteStatsTest extends TelephonyTest {
     private static final String TAG = SatelliteStatsTest.class.getSimpleName();
 
@@ -167,6 +180,7 @@
                         .setCountOfIncomingDatagramSuccess(1)
                         .setCountOfIncomingDatagramFailed(0)
                         .setIsDemoMode(false)
+                        .setMaxNtnSignalStrengthLevel(NTN_SIGNAL_STRENGTH_GOOD)
                         .build();
 
         mSatelliteStats.onSatelliteSessionMetrics(param);
@@ -189,6 +203,7 @@
                 stats.countOfIncomingDatagramSuccess);
         assertEquals(param.getCountOfIncomingDatagramFailed(), stats.countOfIncomingDatagramFailed);
         assertEquals(param.getIsDemoMode(), stats.isDemoMode);
+        assertEquals(param.getMaxNtnSignalStrengthLevel(), stats.maxNtnSignalStrengthLevel);
 
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
@@ -296,4 +311,170 @@
                 stats.isSatelliteAllowedInCurrentLocation);
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
+
+    @Test
+    public void onCarrierRoamingSatelliteSessionMetrics_withAtoms() throws Exception {
+        SatelliteStats.CarrierRoamingSatelliteSessionParams param =
+                new SatelliteStats.CarrierRoamingSatelliteSessionParams.Builder()
+                        .setCarrierId(100)
+                        .setIsNtnRoamingInHomeCountry(true)
+                        .setTotalSatelliteModeTimeSec(10 * 60)
+                        .setNumberOfSatelliteConnections(5)
+                        .setAvgDurationOfSatelliteConnectionSec(2 * 60)
+                        .setSatelliteConnectionGapMinSec(30)
+                        .setSatelliteConnectionGapAvgSec(300)
+                        .setSatelliteConnectionGapMaxSec(500)
+                        .setRsrpAvg(2)
+                        .setRsrpMedian(3)
+                        .setRssnrAvg(12)
+                        .setRssnrMedian(18)
+                        .setCountOfIncomingSms(6)
+                        .setCountOfOutgoingSms(11)
+                        .setCountOfIncomingMms(9)
+                        .setCountOfOutgoingMms(14)
+                        .build();
+
+        mSatelliteStats.onCarrierRoamingSatelliteSessionMetrics(param);
+
+        ArgumentCaptor<CarrierRoamingSatelliteSession> captor =
+                ArgumentCaptor.forClass(CarrierRoamingSatelliteSession.class);
+        verify(mPersistAtomsStorage).addCarrierRoamingSatelliteSessionStats(captor.capture());
+        CarrierRoamingSatelliteSession stats = captor.getValue();
+        assertEquals(param.getCarrierId(), stats.carrierId);
+        assertEquals(param.getIsNtnRoamingInHomeCountry(), stats.isNtnRoamingInHomeCountry);
+        assertEquals(param.getTotalSatelliteModeTimeSec(), stats.totalSatelliteModeTimeSec);
+        assertEquals(param.getNumberOfSatelliteConnections(), stats.numberOfSatelliteConnections);
+        assertEquals(param.getAvgDurationOfSatelliteConnectionSec(),
+                stats.avgDurationOfSatelliteConnectionSec);
+        assertEquals(param.getSatelliteConnectionGapMinSec(), stats.satelliteConnectionGapMinSec);
+        assertEquals(param.getSatelliteConnectionGapAvgSec(), stats.satelliteConnectionGapAvgSec);
+        assertEquals(param.getSatelliteConnectionGapMaxSec(), stats.satelliteConnectionGapMaxSec);
+        assertEquals(param.getRsrpAvg(), stats.rsrpAvg);
+        assertEquals(param.getRsrpMedian(), stats.rsrpMedian);
+        assertEquals(param.getRssnrAvg(), stats.rssnrAvg);
+        assertEquals(param.getRssnrMedian(), stats.rssnrMedian);
+        assertEquals(param.getCountOfIncomingSms(), stats.countOfIncomingSms);
+        assertEquals(param.getCountOfOutgoingSms(), stats.countOfOutgoingSms);
+        assertEquals(param.getCountOfIncomingMms(), stats.countOfIncomingMms);
+        assertEquals(param.getCountOfOutgoingMms(), stats.countOfOutgoingMms);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onCarrierRoamingSatelliteControllerStatsMetrics_withAtoms() throws Exception {
+        SatelliteStats.CarrierRoamingSatelliteControllerStatsParams param =
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setConfigDataSource(4)
+                        .setCountOfEntitlementStatusQueryRequest(6)
+                        .setCountOfSatelliteConfigUpdateRequest(2)
+                        .setCountOfSatelliteNotificationDisplayed(1)
+                        .setSatelliteSessionGapMinSec(15)
+                        .setSatelliteSessionGapAvgSec(30)
+                        .setSatelliteSessionGapMaxSec(45)
+                        .build();
+
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        ArgumentCaptor<CarrierRoamingSatelliteControllerStats> captor =
+                ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage).addCarrierRoamingSatelliteControllerStats(captor.capture());
+        CarrierRoamingSatelliteControllerStats stats = captor.getValue();
+        assertEquals(param.getConfigDataSource(), stats.configDataSource);
+        assertEquals(param.getCountOfEntitlementStatusQueryRequest(),
+                stats.countOfEntitlementStatusQueryRequest);
+        assertEquals(param.getCountOfSatelliteConfigUpdateRequest(),
+                stats.countOfSatelliteConfigUpdateRequest);
+        assertEquals(param.getCountOfSatelliteNotificationDisplayed(),
+                stats.countOfSatelliteNotificationDisplayed);
+        assertEquals(param.getSatelliteSessionGapMinSec(), stats.satelliteSessionGapMinSec);
+        assertEquals(param.getSatelliteSessionGapAvgSec(), stats.satelliteSessionGapAvgSec);
+        assertEquals(param.getSatelliteSessionGapMaxSec(), stats.satelliteSessionGapMaxSec);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onSatelliteEntitlementMetrics_withAtoms() throws Exception {
+        SatelliteStats.SatelliteEntitlementParams param =
+                new SatelliteStats.SatelliteEntitlementParams.Builder()
+                        .setCarrierId(10)
+                        .setResult(500)
+                        .setEntitlementStatus(2)
+                        .setIsRetry(true)
+                        .setCount(5)
+                        .build();
+
+        mSatelliteStats.onSatelliteEntitlementMetrics(param);
+
+        ArgumentCaptor<SatelliteEntitlement> captor =
+                ArgumentCaptor.forClass(SatelliteEntitlement.class);
+        verify(mPersistAtomsStorage).addSatelliteEntitlementStats(captor.capture());
+        SatelliteEntitlement stats = captor.getValue();
+        assertEquals(param.getCarrierId(), stats.carrierId);
+        assertEquals(param.getResult(), stats.result);
+        assertEquals(param.getEntitlementStatus(), stats.entitlementStatus);
+        assertEquals(param.getIsRetry(), stats.isRetry);
+        assertEquals(param.getCount(), stats.count);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onSatelliteConfigUpdaterMetrics_withAtoms() throws Exception {
+        SatelliteStats.SatelliteConfigUpdaterParams param =
+                new SatelliteStats.SatelliteConfigUpdaterParams.Builder()
+                        .setConfigVersion(8)
+                        .setOemConfigResult(9)
+                        .setCarrierConfigResult(7)
+                        .setCount(3)
+                        .build();
+
+        mSatelliteStats.onSatelliteConfigUpdaterMetrics(param);
+
+        ArgumentCaptor<SatelliteConfigUpdater> captor =
+                ArgumentCaptor.forClass(SatelliteConfigUpdater.class);
+        verify(mPersistAtomsStorage).addSatelliteConfigUpdaterStats(captor.capture());
+        SatelliteConfigUpdater stats = captor.getValue();
+        assertEquals(param.getConfigVersion(), stats.configVersion);
+        assertEquals(param.getOemConfigResult(), stats.oemConfigResult);
+        assertEquals(param.getCarrierConfigResult(), stats.carrierConfigResult);
+        assertEquals(param.getCount(), stats.count);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+
+    @Test
+    public void onSatelliteAccessControllerMetrics_withAtoms() {
+        SatelliteStats.SatelliteAccessControllerParams param =
+                new SatelliteStats.SatelliteAccessControllerParams.Builder()
+                        .setAccessControlType(ACCESS_CONTROL_TYPE_CACHED_COUNTRY_CODE)
+                        .setLocationQueryTime(TimeUnit.SECONDS.toMillis(1))
+                        .setOnDeviceLookupTime(TimeUnit.SECONDS.toMillis(2))
+                        .setTotalCheckingTime(TimeUnit.SECONDS.toMillis(3))
+                        .setIsAllowed(true)
+                        .setIsEmergency(false)
+                        .setResult(SATELLITE_RESULT_SUCCESS)
+                        .setCountryCodes(new String[]{"AB", "CD"})
+                        .setConfigDatasource(CONFIG_DATA_SOURCE_DEVICE_CONFIG)
+                        .build();
+
+        mSatelliteStats.onSatelliteAccessControllerMetrics(param);
+
+        ArgumentCaptor<SatelliteAccessController> captor = ArgumentCaptor.forClass(
+                SatelliteAccessController.class);
+        verify(mPersistAtomsStorage).addSatelliteAccessControllerStats(captor.capture());
+        SatelliteAccessController stats = captor.getValue();
+        assertEquals(param.getAccessControlType(), stats.accessControlType);
+        assertEquals(param.getLocationQueryTime(), stats.locationQueryTimeMillis);
+        assertEquals(param.getOnDeviceLookupTime(), stats.onDeviceLookupTimeMillis);
+        assertEquals(param.getTotalCheckingTime(), stats.totalCheckingTimeMillis);
+        assertEquals(param.getIsAllowed(), stats.isAllowed);
+        assertEquals(param.getIsEmergency(), stats.isEmergency);
+        assertEquals(param.getResultCode(), stats.resultCode);
+        assertEquals(param.getCountryCodes(), stats.countryCodes);
+        assertEquals(param.getConfigDataSource(), stats.configDataSource);
+        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 edc481a..04d140c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -50,8 +50,10 @@
 
 import android.annotation.NonNull;
 import android.os.Looper;
+import android.os.PersistableBundle;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.NetworkType;
+import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PreciseDataConnectionState;
@@ -226,7 +228,8 @@
 
         mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone, mFeatureFlags);
         mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
-        mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone, mFeatureFlags);
+        mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(
+                1, mSecondPhone, mFeatureFlags);
         mVoiceCallSessionStats1.onServiceStateChanged(mSecondServiceState);
 
         doReturn(true).when(mFeatureFlags).vonrEnabledMetric();
@@ -2787,6 +2790,72 @@
         assertFalse(session.isNtn);
     }
 
+
+    @Test
+    @SmallTest
+    public void singleCall_supportBusinessCall() {
+        PersistableBundle mCarrierConfig = new PersistableBundle();
+        mCarrierConfig.putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(eq(mPhone.getSubId()),
+                eq(CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL)))
+                        .thenReturn(mCarrierConfig);
+        setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(false).when(mImsConnection0).isIncoming();
+        doReturn(2000L).when(mImsConnection0).getCreateTime();
+        doReturn(0L).when(mImsConnection0).getDurationMillis();
+        doReturn(mImsCall0).when(mImsConnection0).getCall();
+        doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+        doReturn(2).when(mTelephonyManager).getCallComposerStatus();
+        VoiceCallSession expectedCall =
+                makeSlot0CallProto(
+                        VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+                        VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
+                        TelephonyManager.NETWORK_TYPE_LTE,
+                        ImsReasonInfo.CODE_REMOTE_CALL_DECLINE);
+        expectedCall.setupDurationMillis = 200;
+        expectedCall.setupFailed = true;
+        expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+        expectedCall.mainCodecQuality =
+                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.supportsBusinessCallComposer = true;
+        // 0 is defined as UNKNOWN, adding 1 to original value.
+        expectedCall.callComposerStatus = 3;
+        VoiceCallRatUsage expectedRatUsage =
+                makeRatUsageProto(
+                        CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
+        final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+        mVoiceCallSessionStats0.setTimeMillis(2000L);
+        doReturn(Call.State.DIALING).when(mImsCall0).getState();
+        doReturn(Call.State.DIALING).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onImsDial(mImsConnection0);
+        mVoiceCallSessionStats0.setTimeMillis(2100L);
+        mVoiceCallSessionStats0.onAudioCodecChanged(
+                mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB);
+        mVoiceCallSessionStats0.setTimeMillis(2200L);
+        doReturn(Call.State.ALERTING).when(mImsCall0).getState();
+        doReturn(Call.State.ALERTING).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+        mVoiceCallSessionStats0.setTimeMillis(12000L);
+        mVoiceCallSessionStats0.onImsCallTerminated(
+                mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, 0));
+
+        ArgumentCaptor<VoiceCallSession> callCaptor =
+                ArgumentCaptor.forClass(VoiceCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+        verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+        assertProtoEquals(expectedCall, callCaptor.getValue());
+        assertThat(ratUsage.get()).hasLength(1);
+        assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+    }
+
     private AtomicReference<VoiceCallRatUsage[]> setupRatUsageCapture() {
         final AtomicReference<VoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
         doAnswer(
@@ -2875,6 +2944,7 @@
         call.setupBeginMillis = 0L;
         call.signalStrengthAtEnd = 2;
         call.vonrEnabled = false;
+        call.callComposerStatus = 1;
         return call;
     }
 
@@ -2910,6 +2980,7 @@
         call.isRoaming = false;
         call.setupBeginMillis = 0L;
         call.signalStrengthAtEnd = 2;
+        call.callComposerStatus = 1;
         return call;
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java
index f1508ee..2961b4d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java
@@ -87,7 +87,7 @@
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
         mDatagramControllerUT = new DatagramController(
-                mContext, Looper.myLooper(), mMockPointingAppController);
+                mContext, Looper.myLooper(), mFeatureFlags, mMockPointingAppController);
 
         // Move both send and receive to IDLE state
         mDatagramControllerUT.updateSendStatus(SUB_ID, DATAGRAM_TYPE_UNKNOWN,
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 096c8dc..7094399 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -60,6 +60,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
 
@@ -98,6 +99,12 @@
     private static final long TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS =
             TimeUnit.SECONDS.toMillis(60);
     private static final Long TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE = TimeUnit.SECONDS.toMillis(10);
+    private static final long
+            TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS =
+            TimeUnit.SECONDS.toMillis(60);
+    private static final int
+            TEST_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS =
+            (int) TimeUnit.SECONDS.toMillis(60);
 
     private TestDatagramDispatcher mDatagramDispatcherUT;
 
@@ -149,7 +156,9 @@
                 mMockSessionMetricsStats);
 
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         mDatagramDispatcherUT = new TestDatagramDispatcher(mContext, Looper.myLooper(),
+                mFeatureFlags,
                 mMockDatagramController);
 
         mResultListener = new LinkedBlockingQueue<>(1);
@@ -188,8 +197,11 @@
             clearInvocations(mMockSatelliteModemInterface);
             doReturn(true).when(mMockDatagramController)
                     .needsWaitingForSatelliteConnected(eq(datagramType));
-            when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
+            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.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
@@ -201,7 +213,8 @@
                     eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT),
                     eq(1),
                     eq(SATELLITE_RESULT_SUCCESS));
-            mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+            mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                    eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
             verifyZeroInteractions(mMockSatelliteModemInterface);
             assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
 
@@ -228,7 +241,8 @@
                             eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                             eq(SATELLITE_RESULT_SUCCESS));
             verifyNoMoreInteractions(mMockDatagramController);
-            verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType));
             verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
                     any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
@@ -237,10 +251,7 @@
 
             clearInvocations(mMockSatelliteModemInterface);
             clearInvocations(mMockDatagramController);
-            mResultListener.clear();
-
-            clearInvocations(mMockSatelliteModemInterface);
-            clearInvocations(mMockDatagramController);
+            clearInvocations(mMockSessionMetricsStats);
             mResultListener.clear();
             doReturn(true).when(mMockDatagramController)
                     .needsWaitingForSatelliteConnected(eq(datagramType));
@@ -250,7 +261,8 @@
             verifyZeroInteractions(mMockSatelliteModemInterface);
             mInOrder.verify(mMockDatagramController)
                     .needsWaitingForSatelliteConnected(eq(datagramType));
-            mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+            mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                    eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
             assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
 
             moveTimeForward(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
@@ -271,7 +283,8 @@
             assertThat(mResultListener.peek()).isEqualTo(
                     SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
 
             mResultListener.clear();
             mDatagramDispatcherUT.onSatelliteModemStateChanged(
@@ -282,6 +295,7 @@
 
             clearInvocations(mMockSatelliteModemInterface);
             clearInvocations(mMockDatagramController);
+            clearInvocations(mMockSessionMetricsStats);
             mResultListener.clear();
             mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
                     true, mResultListener::offer);
@@ -289,7 +303,8 @@
             verifyZeroInteractions(mMockSatelliteModemInterface);
             mInOrder.verify(mMockDatagramController)
                     .needsWaitingForSatelliteConnected(eq(datagramType));
-            mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+            mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                    eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
             assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
             assertEquals(0, mResultListener.size());
 
@@ -301,91 +316,111 @@
             assertThat(mResultListener.peek()).isEqualTo(
                     SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
         }
     }
 
     @Test
     public void testSendSatelliteDatagram_timeout() throws  Exception {
-        doAnswer(invocation -> {
-            Message message = (Message) invocation.getArguments()[3];
-
-            mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
-                            new AsyncResult(message.obj, null, null))
-                    .sendToTarget();
-
-            // DatagramDispatcher should ignore the second EVENT_SEND_SATELLITE_DATAGRAM_DONE
-            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));
-        doReturn(false).when(mMockDatagramController)
-                .needsWaitingForSatelliteConnected(eq(DATAGRAM_TYPE1));
-        when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
+        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);
         mContextFixture.putIntResource(
                 R.integer.config_wait_for_datagram_sending_response_timeout_millis,
                 TEST_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMEOUT_MILLIS);
+        mContextFixture.putIntResource(
+                R.integer.config_wait_for_datagram_sending_response_for_last_message_timeout_millis,
+                TEST_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS);
         mResultListener.clear();
+        int[] sosDatagramTypes = {DATAGRAM_TYPE1, DATAGRAM_TYPE4, DATAGRAM_TYPE5};
+        for (int datagramType : sosDatagramTypes) {
+            doAnswer(invocation -> {
+                Message message = (Message) invocation.getArguments()[3];
 
-        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
-                true, mResultListener::offer);
-        processAllMessages();
-        mInOrder.verify(mMockDatagramController)
-                .needsWaitingForSatelliteConnected(eq(DATAGRAM_TYPE1));
-        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
-        mInOrder.verify(mMockDatagramController)
-                .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE1),
-                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SATELLITE_RESULT_SUCCESS));
-        mInOrder.verify(mMockDatagramController)
-                .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE1),
-                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
-                        eq(SATELLITE_RESULT_SUCCESS));
-        mInOrder.verify(mMockDatagramController)
-                .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE1),
-                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SATELLITE_RESULT_SUCCESS));
-        verifyNoMoreInteractions(mMockDatagramController);
-        verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
-                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
-        assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
-        verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
-        clearInvocations(mMockSatelliteModemInterface);
-        clearInvocations(mMockDatagramController);
-        mResultListener.clear();
+                mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+                                new AsyncResult(message.obj, null, null))
+                        .sendToTarget();
 
-        // No response for the send request from modem
-        doNothing().when(mMockSatelliteModemInterface).sendSatelliteDatagram(
-                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+                // DatagramDispatcher should ignore the second EVENT_SEND_SATELLITE_DATAGRAM_DONE
+                mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+                                new AsyncResult(message.obj, null, null))
+                        .sendToTarget();
 
-        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
-                true, mResultListener::offer);
-        processAllMessages();
-        mInOrder.verify(mMockDatagramController)
-                .needsWaitingForSatelliteConnected(eq(DATAGRAM_TYPE1));
-        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
-        mInOrder.verify(mMockDatagramController)
-                .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE1),
-                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
-                        eq(SATELLITE_RESULT_SUCCESS));
-        mInOrder.verify(mMockDatagramController)
-                .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE1),
-                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(1),
-                        eq(SATELLITE_RESULT_MODEM_TIMEOUT));
-        mInOrder.verify(mMockDatagramController)
-                .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE1),
-                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
-                        eq(SATELLITE_RESULT_SUCCESS));
-        verifyNoMoreInteractions(mMockDatagramController);
-        verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
-                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
-        verify(mMockSatelliteModemInterface).abortSendingSatelliteDatagrams(any(Message.class));
-        assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_MODEM_TIMEOUT);
-        verify(mMockSessionMetricsStats, never()).addCountOfFailedOutgoingDatagram();
+                return null;
+            }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class),
+                    anyBoolean(), anyBoolean(), any(Message.class));
+            doReturn(false).when(mMockDatagramController)
+                    .needsWaitingForSatelliteConnected(eq(datagramType));
+
+            mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
+                    true, mResultListener::offer);
+            processAllMessages();
+            mInOrder.verify(mMockDatagramController)
+                    .needsWaitingForSatelliteConnected(eq(datagramType));
+            mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+            mInOrder.verify(mMockDatagramController)
+                    .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                            eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                            eq(SATELLITE_RESULT_SUCCESS));
+            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);
+            verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+            assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(anyInt());
+            clearInvocations(mMockSatelliteModemInterface);
+            clearInvocations(mMockDatagramController);
+            clearInvocations(mMockSessionMetricsStats);
+            mResultListener.clear();
+
+            // No response for the send request from modem
+            doNothing().when(mMockSatelliteModemInterface).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+
+            mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
+                    true, mResultListener::offer);
+            processAllMessages();
+            mInOrder.verify(mMockDatagramController)
+                    .needsWaitingForSatelliteConnected(eq(datagramType));
+            mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+            mInOrder.verify(mMockDatagramController)
+                    .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                            eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                            eq(SATELLITE_RESULT_SUCCESS));
+            mInOrder.verify(mMockDatagramController)
+                    .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                            eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
+                            eq(1),
+                            eq(SATELLITE_RESULT_MODEM_TIMEOUT));
+            mInOrder.verify(mMockDatagramController)
+                    .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                            eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                            eq(SATELLITE_RESULT_SUCCESS));
+            verifyNoMoreInteractions(mMockDatagramController);
+            verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+            verify(mMockSatelliteModemInterface).abortSendingSatelliteDatagrams(any(Message.class));
+            assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_MODEM_TIMEOUT);
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
+
+            clearInvocations(mMockSatelliteModemInterface);
+            clearInvocations(mMockDatagramController);
+            clearInvocations(mMockSessionMetricsStats);
+            mResultListener.clear();
+        }
     }
 
     @Test
@@ -426,7 +461,8 @@
 
         assertThat(mResultListener.peek()).isEqualTo(
                 SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
-        verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+        verify(mMockSessionMetricsStats, times(1))
+                .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
     }
 
     @Test
@@ -467,7 +503,8 @@
                             eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                             eq(SATELLITE_RESULT_SUCCESS));
             assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
-            verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType));
             mDatagramDispatcherUT.setDemoMode(false);
             mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
         }
@@ -488,6 +525,7 @@
         mDatagramDispatcherUT.setDemoMode(true);
         mDatagramDispatcherUT.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
+        when(mMockDatagramController.waitForAligningToSatellite(false)).thenReturn(true);
 
         int[] sosDatagramTypes = {DATAGRAM_TYPE1, DATAGRAM_TYPE4, DATAGRAM_TYPE5};
         for (int datagramType : sosDatagramTypes) {
@@ -514,7 +552,8 @@
             verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
             verify(mMockDatagramController, never()).pushDemoModeDatagram(
                     anyInt(), any(SatelliteDatagram.class));
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
         }
 
         mDatagramDispatcherUT.setDemoMode(false);
@@ -559,7 +598,8 @@
                 .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE2),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                         eq(SATELLITE_RESULT_SUCCESS));
-        verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+        verify(mMockSessionMetricsStats, times(1))
+                .addCountOfSuccessfulOutgoingDatagram(eq(DATAGRAM_TYPE2));
 
         mDatagramDispatcherUT.setDemoMode(false);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
@@ -629,87 +669,97 @@
     }
 
     @Test
-    public void testSendSatelliteDatagramToModemInDemoMode()
-            throws Exception {
+    public void testSendSatelliteDatagramToModemInDemoMode() throws Exception {
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
 
-        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));
         mDatagramDispatcherUT.setDemoMode(true);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(true);
-        mIntegerConsumerSemaphore.drainPermits();
 
-        // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is true
-        mDatagramDispatcherUT.setShouldSendDatagramToModemInDemoMode(null);
-        mContextFixture.putBooleanResource(mConfigSendSatelliteDatagramToModemInDemoMode, true);
-        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
-                true, mIntegerConsumer);
-        processAllMessages();
-        moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
-        processAllMessages();
-        waitForIntegerConsumerResult(1);
-        assertEquals(SATELLITE_RESULT_SUCCESS,
-                (int) mIntegerConsumerResult.get(0));
-        mIntegerConsumerResult.clear();
-        verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
-                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+        int[] sosDatagramTypes = {DATAGRAM_TYPE1, DATAGRAM_TYPE4, DATAGRAM_TYPE5};
+        for (int datagramType : sosDatagramTypes) {
+            mIntegerConsumerSemaphore.drainPermits();
+            mIntegerConsumerResult.clear();
+            clearInvocations(mMockDatagramController);
+            clearInvocations(mMockSatelliteModemInterface);
+            clearInvocations(mMockSessionMetricsStats);
+            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));
 
-        moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
-        processAllMessages();
-        verify(mMockDatagramController).pushDemoModeDatagram(
-                anyInt(), any(SatelliteDatagram.class));
-        verify(mMockDatagramController).pollPendingSatelliteDatagrams(anyInt(), any());
-        verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is true
+            mDatagramDispatcherUT.setShouldSendDatagramToModemInDemoMode(null);
+            mContextFixture.putBooleanResource(mConfigSendSatelliteDatagramToModemInDemoMode, true);
+            mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
+                    true, mIntegerConsumer);
+            processAllMessages();
+            moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
+            processAllMessages();
+            waitForIntegerConsumerResult(1);
+            assertEquals(SATELLITE_RESULT_SUCCESS, (int) mIntegerConsumerResult.get(0));
+            mIntegerConsumerResult.clear();
+            verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
 
-        // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is false
-        reset(mMockSatelliteModemInterface);
-        mDatagramDispatcherUT.setShouldSendDatagramToModemInDemoMode(null);
-        mContextFixture.putBooleanResource(mConfigSendSatelliteDatagramToModemInDemoMode, false);
-        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
-                true, mIntegerConsumer);
-        processAllMessages();
-        moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
-        processAllMessages();
+            moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
+            processAllMessages();
+            verify(mMockDatagramController).pushDemoModeDatagram(
+                    anyInt(), any(SatelliteDatagram.class));
+            verify(mMockDatagramController).pollPendingSatelliteDatagrams(anyInt(), any());
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(anyInt());
 
-        waitForIntegerConsumerResult(1);
-        assertEquals(SATELLITE_RESULT_SUCCESS,
-                (int) mIntegerConsumerResult.get(0));
-        mIntegerConsumerResult.clear();
-        verify(mMockSatelliteModemInterface, never()).sendSatelliteDatagram(
-                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+            // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is
+            // false
+            reset(mMockSatelliteModemInterface);
+            mDatagramDispatcherUT.setShouldSendDatagramToModemInDemoMode(null);
+            mContextFixture.putBooleanResource(mConfigSendSatelliteDatagramToModemInDemoMode,
+                    false);
+            mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
+                    true, mIntegerConsumer);
+            processAllMessages();
+            moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
+            processAllMessages();
 
-        moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
-        processAllMessages();
-        verify(mMockDatagramController, times(2)).pushDemoModeDatagram(
-                anyInt(), any(SatelliteDatagram.class));
-        verify(mMockDatagramController, times(2)).pollPendingSatelliteDatagrams(anyInt(), any());
+            waitForIntegerConsumerResult(1);
+            assertEquals(SATELLITE_RESULT_SUCCESS, (int) mIntegerConsumerResult.get(0));
+            mIntegerConsumerResult.clear();
+            verify(mMockSatelliteModemInterface, never()).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
 
-        // Send datagram one more time
-        reset(mMockSatelliteModemInterface);
-        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
-                true, mIntegerConsumer);
-        processAllMessages();
-        moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
-        processAllMessages();
+            moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
+            processAllMessages();
+            verify(mMockDatagramController, times(2)).pushDemoModeDatagram(
+                    anyInt(), any(SatelliteDatagram.class));
+            verify(mMockDatagramController, times(2)).pollPendingSatelliteDatagrams(anyInt(),
+                    any());
 
-        waitForIntegerConsumerResult(1);
-        assertEquals(SATELLITE_RESULT_SUCCESS,
-                (int) mIntegerConsumerResult.get(0));
-        mIntegerConsumerResult.clear();
-        verify(mMockSatelliteModemInterface, never()).sendSatelliteDatagram(
-                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+            // Send datagram one more time
+            reset(mMockSatelliteModemInterface);
+            mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramType, mDatagram,
+                    true, mIntegerConsumer);
+            processAllMessages();
+            moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
+            processAllMessages();
 
-        moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
-        processAllMessages();
-        verify(mMockDatagramController, times(3)).pushDemoModeDatagram(
-                anyInt(), any(SatelliteDatagram.class));
-        verify(mMockDatagramController, times(3)).pollPendingSatelliteDatagrams(anyInt(), any());
+            waitForIntegerConsumerResult(1);
+            assertEquals(SATELLITE_RESULT_SUCCESS, (int) mIntegerConsumerResult.get(0));
+            mIntegerConsumerResult.clear();
+            verify(mMockSatelliteModemInterface, never()).sendSatelliteDatagram(
+                    any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+
+            moveTimeForward(TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE);
+            processAllMessages();
+            verify(mMockDatagramController, times(3)).pushDemoModeDatagram(
+                    anyInt(), any(SatelliteDatagram.class));
+            verify(mMockDatagramController, times(3)).pollPendingSatelliteDatagrams(anyInt(),
+                    any());
+        }
 
         mDatagramDispatcherUT.setDemoMode(false);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
@@ -735,8 +785,9 @@
         private long mLong = SATELLITE_ALIGN_TIMEOUT;
 
         TestDatagramDispatcher(@NonNull Context context, @NonNull Looper looper,
+                @NonNull FeatureFlags featureFlags,
                 @NonNull DatagramController datagramController) {
-            super(context, looper, datagramController);
+            super(context, looper, featureFlags, datagramController);
         }
 
         @Override
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 361c638..947661b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.clearInvocations;
@@ -57,6 +58,7 @@
 
 import com.android.internal.telephony.IVoidConsumer;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
 
@@ -125,9 +127,11 @@
         replaceInstance(SessionMetricsStats.class, "sInstance", null,
                 mMockSessionMetricsStats);
 
-        mDatagramReceiverUT = DatagramReceiver.make(mContext, Looper.myLooper(),
+        when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
+        mDatagramReceiverUT = DatagramReceiver.make(mContext, Looper.myLooper(), mFeatureFlags,
                 mMockDatagramController);
         mTestDemoModeDatagramReceiver = new TestDatagramReceiver(mContext, Looper.myLooper(),
+                mFeatureFlags,
                 mMockDatagramController);
         mSatelliteDatagramListenerHandler = new DatagramReceiver.SatelliteDatagramListenerHandler(
                 Looper.myLooper(), SUB_ID);
@@ -170,7 +174,7 @@
         }).when(mMockSatelliteModemInterface).pollPendingSatelliteDatagrams(any(Message.class));
         doReturn(true).when(mMockDatagramController)
                 .needsWaitingForSatelliteConnected(eq(SatelliteManager.DATAGRAM_TYPE_UNKNOWN));
-        when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(anyBoolean()))
                 .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
         mResultListener.clear();
 
@@ -181,7 +185,7 @@
         mInOrder.verify(mMockDatagramController).updateReceiveStatus(eq(SUB_ID),
                 eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT), eq(0),
                 eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
-        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(eq(false));
         verifyZeroInteractions(mMockSatelliteModemInterface);
         assertTrue(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
 
@@ -210,7 +214,7 @@
         processAllMessages();
         mInOrder.verify(mMockDatagramController)
                 .needsWaitingForSatelliteConnected(eq(SatelliteManager.DATAGRAM_TYPE_UNKNOWN));
-        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(eq(false));
         verifyZeroInteractions(mMockSatelliteModemInterface);
         assertTrue(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
 
@@ -372,6 +376,7 @@
         mTestDemoModeDatagramReceiver.setDemoMode(true);
         mTestDemoModeDatagramReceiver.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
         mTestDemoModeDatagramReceiver.setDeviceAlignedWithSatellite(false);
+        when(mMockDatagramController.waitForAligningToSatellite(false)).thenReturn(true);
         when(mMockDatagramController.popDemoModeDatagram()).thenReturn(mDatagram);
         mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
         processAllMessages();
@@ -488,8 +493,9 @@
         private long mLong =  SATELLITE_ALIGN_TIMEOUT;
 
         TestDatagramReceiver(@NonNull Context context, @NonNull Looper looper,
+                @NonNull FeatureFlags featureFlags,
                 @NonNull DatagramController datagramController) {
-            super(context, looper, datagramController);
+            super(context, looper, featureFlags, datagramController);
         }
 
         @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
new file mode 100644
index 0000000..319e39f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.satellite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NtnSignalStrength;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+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.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for DemoSimulator
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DemoSimulatorTest extends TelephonyTest {
+    private static final String TAG = "DemoSimulatorTest";
+    private static final long TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS = 200L;
+    private static final long TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS = 300L;
+    private static final String STATE_POWER_OFF = "PowerOffState";
+    private static final String STATE_NOT_CONNECTED = "NotConnectedState";
+    private static final String STATE_CONNECTED = "ConnectedState";
+
+    private TestDemoSimulator mTestDemoSimulator;
+    @Mock private ISatelliteListener mISatelliteListener;
+
+    @Mock private SatelliteController mMockSatelliteController;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockSatelliteController.isDemoModeEnabled()).thenReturn(true);
+        when(mMockSatelliteController.getDemoPointingAlignedDurationMillis()).thenReturn(
+                TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS);
+        when(mMockSatelliteController.getDemoPointingNotAlignedDurationMillis()).thenReturn(
+                TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS);
+
+        mTestDemoSimulator = new TestDemoSimulator(mContext, Looper.myLooper(),
+                mMockSatelliteController);
+        mTestDemoSimulator.setSatelliteListener(mISatelliteListener);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testInitialState() {
+        assertNotNull(mTestDemoSimulator);
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    @Test
+    public void testStateTransition() {
+        // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+        moveToConnectedState();
+
+        // Device is not aligned with satellite. EVENT_DEVICE_NOT_ALIGNED timer should start
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+
+        // After timeout, DemoSimulator should move to NOT_CONNECTED state.
+        moveTimeForward(TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS);
+        processAllMessages();
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is OFF. DemoSimulator should move to POWER_OFF state.
+        mTestDemoSimulator.onSatelliteModeOff();
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    @Test
+    public void testNotConnectedState_enter() throws Exception {
+        clearInvocations(mISatelliteListener);
+
+        // State transitions: POWER_OFF -> NOT_CONNECTED
+        moveToNotConnectedState();
+
+        verify(mISatelliteListener).onSatelliteModemStateChanged(
+                SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
+                NtnSignalStrength.class);
+        verify(mISatelliteListener).onNtnSignalStrengthChanged(ntnSignalStrength.capture());
+        assertEquals(0, ntnSignalStrength.getValue().signalStrengthLevel);
+    }
+
+    @Test
+    public void testNotConnectedState() {
+        // State transitions: POWER_OFF -> NOT_CONNECTED
+        moveToNotConnectedState();
+
+        // Device is aligned with satellite. EVENT_DEVICE_ALIGNED timer should start.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+
+        // Device is not aligned with satellite. EVENT_DEVICE_ALIGNED messages should be removed.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+        processAllMessages();
+        assertFalse(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is OFF. DemoSimulator should move to POWER_OFF state.
+        mTestDemoSimulator.onSatelliteModeOff();
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    @Test
+    public void testConnectedState_enter() throws Exception {
+        clearInvocations(mISatelliteListener);
+
+        // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+        moveToConnectedState();
+
+        verify(mISatelliteListener).onSatelliteModemStateChanged(
+                SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        verify(mISatelliteListener).onSatelliteModemStateChanged(
+                SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+        ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
+                NtnSignalStrength.class);
+        verify(mISatelliteListener, times(2))
+                .onNtnSignalStrengthChanged(ntnSignalStrength.capture());
+        NtnSignalStrength ntnSignalStrengthOnConnected = ntnSignalStrength.getAllValues().get(1);
+        assertEquals(2, ntnSignalStrengthOnConnected.signalStrengthLevel);
+    }
+
+    @Test
+    public void testConnectedState() {
+        // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+        moveToConnectedState();
+
+        // Device is not aligned with satellite. EVENT_DEVICE_NOT_ALIGNED timer should start
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+
+        // Device is aligned with satellite before timeout.
+        // EVENT_DEVICE_NOT_ALIGNED messages should be removed.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+        processAllMessages();
+        assertFalse(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+        assertEquals(STATE_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is off. DemoSimulator should move to POWER_OFF state
+        mTestDemoSimulator.onSatelliteModeOff();
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    private void moveToNotConnectedState() {
+        // DemoSimulator will initially be in POWER_OFF state.
+        assertNotNull(mTestDemoSimulator);
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is ON. DemoSimulator should move to NOT_CONNECTED state.
+        mTestDemoSimulator.onSatelliteModeOn();
+        processAllMessages();
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    private void moveToConnectedState() {
+        // DemoSimulator will initially be in POWER_OFF state.
+        assertNotNull(mTestDemoSimulator);
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is ON. DemoSimulator should move to NOT_CONNECTED state.
+        mTestDemoSimulator.onSatelliteModeOn();
+        processAllMessages();
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Device is aligned with satellite. EVENT_DEVICE_ALIGNED timer should start.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+
+        // After timeout, DemoSimulator should move to CONNECTED state.
+        moveTimeForward(TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS);
+        processAllMessages();
+        assertEquals(STATE_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    private static class TestDemoSimulator extends DemoSimulator {
+
+        TestDemoSimulator(@NonNull Context context, @NonNull Looper looper,
+                @NonNull SatelliteController satelliteController) {
+            super(context, looper, satelliteController);
+        }
+
+        String getCurrentStateName() {
+            return getCurrentState().getName();
+        }
+
+        boolean isDeviceAlignedTimerStarted() {
+            return hasMessages(EVENT_DEVICE_ALIGNED);
+        }
+
+        boolean isDeviceNotAlignedTimerStarted() {
+            return hasMessages(EVENT_DEVICE_NOT_ALIGNED);
+        }
+    }
+}
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 c1f3a88..36d32fe 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
@@ -100,12 +100,13 @@
         super.setUp(getClass().getSimpleName());
         MockitoAnnotations.initMocks(this);
         logd(TAG + " Setup!");
+        when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         mInOrderForPointingUi = inOrder(mContext);
         replaceInstance(SatelliteModemInterface.class, "sInstance", null,
                 mMockSatelliteModemInterface);
         replaceInstance(SatelliteController.class, "sInstance", null,
                 mMockSatelliteController);
-        mPointingAppController = new PointingAppController(mContext);
+        mPointingAppController = new PointingAppController(mContext, mFeatureFlags);
         mContextFixture.putResource(R.string.config_pointing_ui_package,
                 KEY_POINTING_UI_PACKAGE_NAME);
         mContextFixture.putResource(R.string.config_pointing_ui_class,
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 1484b47..da40c32 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -103,6 +103,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellSignalStrength;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -201,7 +202,9 @@
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private TelephonyConfigUpdateInstallReceiver mMockTelephonyConfigUpdateInstallReceiver;
     @Mock private SatelliteConfigParser mMockConfigParser;
+    @Mock private CellSignalStrength mCellSignalStrength;
     @Mock private SatelliteConfig mMockConfig;
+    @Mock private DemoSimulator mMockDemoSimulator;
 
     private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0);
     private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() {
@@ -475,6 +478,7 @@
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mPhone2});
         replaceInstance(TelephonyConfigUpdateInstallReceiver.class, "sReceiverAdaptorInstance",
                 null, mMockTelephonyConfigUpdateInstallReceiver);
+        replaceInstance(DemoSimulator.class, "sInstance", null, mMockDemoSimulator);
 
         mServiceState2 = Mockito.mock(ServiceState.class);
         when(mPhone.getServiceState()).thenReturn(mServiceState);
@@ -782,14 +786,17 @@
         // Successfully enable satellite
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                 mIIntegerConsumer);
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+        assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
         verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(true));
@@ -800,6 +807,7 @@
 
         // Successfully disable satellite when radio is turned off.
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
         setRadioPower(false);
         mSatelliteControllerUT.onCellularRadioPowerOffRequested();
@@ -808,6 +816,7 @@
         processAllMessages();
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+        assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_FALSE, mSatelliteControllerUT.satelliteModeSettingValue);
         verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(false));
@@ -833,6 +842,7 @@
         mIIntegerConsumerResults.clear();
         clearInvocations(mMockPointingAppController);
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false,
                 SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
@@ -844,11 +854,13 @@
         verify(mMockPointingAppController, never()).startPointingUI(anyBoolean(), anyBoolean(),
                 anyBoolean());
         assertFalse(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+        assertFalse(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementFailCount();
 
         // Successfully enable satellite when radio is on.
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                 mIIntegerConsumer);
@@ -857,6 +869,7 @@
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         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));
@@ -1304,6 +1317,7 @@
                 .registerForSatelliteModemStateChanged(callback);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
 
         errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
                 SUB_ID, callback);
@@ -1324,7 +1338,7 @@
                 .unregisterForSatelliteModemStateChanged(callback);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
-
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         mSatelliteControllerUT.unregisterForModemStateChanged(SUB_ID, callback);
         verify(mMockSatelliteSessionController).unregisterForSatelliteModemStateChanged(callback);
     }
@@ -2250,6 +2264,7 @@
                 SATELLITE_MODEM_STATE_CONNECTED);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         clearInvocations(mMockSatelliteSessionController);
         clearInvocations(mMockDatagramController);
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
@@ -2622,6 +2637,11 @@
                     /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
             );
         }
+        doReturn(mSignalStrength).when(mPhone).getSignalStrength();
+        doReturn(mSignalStrength).when(mPhone2).getSignalStrength();
+        List<CellSignalStrength> cellSignalStrengthList = new ArrayList<>();
+        cellSignalStrengthList.add(mCellSignalStrength);
+        doReturn(cellSignalStrengthList).when(mSignalStrength).getCellSignalStrengths();
         processAllMessages();
         mSatelliteControllerUT.elapsedRealtime = 0;
         assertFalse(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
@@ -3446,6 +3466,7 @@
 
         // startSendingNtnSignalStrength should be invoked when satellite is enabled
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                 mIIntegerConsumer);
@@ -3454,6 +3475,7 @@
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+        assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
         verify(mMockSatelliteModemInterface, times(1)).startSendingNtnSignalStrength(
@@ -3505,6 +3527,7 @@
         reset(mMockSatelliteModemInterface);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                 mIIntegerConsumer);
@@ -3513,6 +3536,7 @@
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+        assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
         verify(mMockSatelliteModemInterface, times(1)).startSendingNtnSignalStrength(
@@ -4467,6 +4491,7 @@
         public boolean allRadiosDisabled = true;
         public long elapsedRealtime = 0;
         public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE;
+        public boolean setSettingsKeyToAllowDeviceRotationCalled = false;
 
         TestSatelliteController(
                 Context context, Looper looper, @NonNull FeatureFlags featureFlags) {
@@ -4487,6 +4512,12 @@
         }
 
         @Override
+        protected void setSettingsKeyToAllowDeviceRotation(int val) {
+            logd("setSettingsKeyToAllowDeviceRotation: val=" + val);
+            setSettingsKeyToAllowDeviceRotationCalled = true;
+        }
+
+        @Override
         protected boolean areAllRadiosDisabled() {
             return allRadiosDisabled;
         }
@@ -4502,5 +4533,9 @@
         protected long getElapsedRealtime() {
             return elapsedRealtime;
         }
+
+        void setSatelliteSessionController(SatelliteSessionController satelliteSessionController) {
+            mSatelliteSessionController = satelliteSessionController;
+        }
     }
 }
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 f6131ea..78763d1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -45,6 +45,7 @@
 import android.testing.TestableLooper;
 
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -105,11 +106,12 @@
         Resources resources = mContext.getResources();
         when(resources.getInteger(anyInt())).thenReturn(TEST_SATELLITE_TIMEOUT_MILLIS);
 
+        when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
         mSatelliteModemInterface = new TestSatelliteModemInterface(
-                mContext, mMockSatelliteController, Looper.myLooper());
+                mContext, mMockSatelliteController, Looper.myLooper(), mFeatureFlags);
         mTestSatelliteSessionController = new TestSatelliteSessionController(mContext,
-                Looper.myLooper(), true, mSatelliteModemInterface);
+                Looper.myLooper(), mFeatureFlags, true, mSatelliteModemInterface);
         processAllMessages();
 
         mTestSatelliteModemStateCallback = new TestSatelliteModemStateCallback();
@@ -131,7 +133,7 @@
          * state.
          */
         TestSatelliteSessionController sessionController1 = new TestSatelliteSessionController(
-                mContext, Looper.myLooper(), false, mSatelliteModemInterface);
+                mContext, Looper.myLooper(), mFeatureFlags, false, mSatelliteModemInterface);
         assertNotNull(sessionController1);
         processAllMessages();
         assertEquals(STATE_UNAVAILABLE, sessionController1.getCurrentStateName());
@@ -140,7 +142,7 @@
          * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
          */
         TestSatelliteSessionController sessionController2 = new TestSatelliteSessionController(
-                mContext, Looper.myLooper(), true, mSatelliteModemInterface);
+                mContext, Looper.myLooper(), mFeatureFlags, true, mSatelliteModemInterface);
         assertNotNull(sessionController2);
         processAllMessages();
         assertEquals(STATE_POWER_OFF, sessionController2.getCurrentStateName());
@@ -153,7 +155,7 @@
          * state.
          */
         TestSatelliteSessionController sessionController = new TestSatelliteSessionController(
-                mContext, Looper.myLooper(), false, mSatelliteModemInterface);
+                mContext, Looper.myLooper(), mFeatureFlags, false, mSatelliteModemInterface);
         assertNotNull(sessionController);
         processAllMessages();
         assertEquals(STATE_UNAVAILABLE, sessionController.getCurrentStateName());
@@ -1117,8 +1119,9 @@
         private int mErrorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
         TestSatelliteModemInterface(@NonNull Context context,
-                SatelliteController satelliteController, @NonNull Looper looper) {
-            super(context, satelliteController, looper);
+                SatelliteController satelliteController, @NonNull Looper looper,
+                @NonNull FeatureFlags featureFlags) {
+            super(context, satelliteController, looper, featureFlags);
             mExponentialBackoff.stop();
         }
 
@@ -1161,9 +1164,10 @@
     }
 
     private static class TestSatelliteSessionController extends SatelliteSessionController {
-        TestSatelliteSessionController(Context context, Looper looper, boolean isSatelliteSupported,
+        TestSatelliteSessionController(Context context, Looper looper, FeatureFlags featureFlags,
+                boolean isSatelliteSupported,
                 SatelliteModemInterface satelliteModemInterface) {
-            super(context, looper, isSatelliteSupported, satelliteModemInterface);
+            super(context, looper, featureFlags, isSatelliteSupported, satelliteModemInterface);
         }
 
         String getCurrentStateName() {