Merge "Avoid NPE in RadioConfigProxy." into main
diff --git a/Android.bp b/Android.bp
index da32208..a6e526c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -82,14 +82,14 @@
         "android.hardware.radio-V1.4-java",
         "android.hardware.radio-V1.5-java",
         "android.hardware.radio-V1.6-java",
-        "android.hardware.radio.config-V3-java",
-        "android.hardware.radio.data-V3-java",
-        "android.hardware.radio.ims-V2-java",
-        "android.hardware.radio.messaging-V3-java",
-        "android.hardware.radio.modem-V3-java",
-        "android.hardware.radio.network-V3-java",
-        "android.hardware.radio.sim-V3-java",
-        "android.hardware.radio.voice-V3-java",
+        "android.hardware.radio.config-V4-java",
+        "android.hardware.radio.data-V4-java",
+        "android.hardware.radio.ims-V3-java",
+        "android.hardware.radio.messaging-V4-java",
+        "android.hardware.radio.modem-V4-java",
+        "android.hardware.radio.network-V4-java",
+        "android.hardware.radio.sim-V4-java",
+        "android.hardware.radio.voice-V4-java",
         "voip-common",
         "ims-common",
         "unsupportedappusage",
diff --git a/flags/calling.aconfig b/flags/calling.aconfig
index 906abfc..27d3518 100644
--- a/flags/calling.aconfig
+++ b/flags/calling.aconfig
@@ -69,4 +69,16 @@
     namespace: "telephony"
     description: "Used to notify the emergency callback mode for call/SMS to other applications."
     bug:"359064059"
+    is_exported: true
 }
+
+# OWNER=sewookseo TARGET=25Q2
+flag {
+    name: "pass_copied_call_state_list"
+    namespace: "telephony"
+    description: "To prevent passing the TelephonyRegistry's original instance to listeners in the same process"
+    bug:"379126049"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/flags/data.aconfig b/flags/data.aconfig
index ccd5db4..4a90c10 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -121,21 +121,11 @@
   }
 }
 
-# OWNER=jackyu TARGET=25Q1
-flag {
-  name: "sim_disabled_graceful_tear_down"
-  namespace: "telephony"
-  description: "Gracefully tear down the networks when SIM is disabled."
-  bug: "362372940"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-# OWNER=TBD TARGET=TBD
+# OWNER=jackyu TARGET=25Q2
 flag {
   name: "oem_paid_private"
   namespace: "telephony"
   description: "Support OEM_PAID and OEM_PRIVATE networks"
   bug: "366194627"
+  is_exported: true
 }
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index 4ce7508..703440f 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -155,6 +155,7 @@
     namespace: "telephony"
     description: "Used to expose SMS related hidden APIs for SMS over IMS to public API."
     bug:"359721349"
+    is_exported: true
 }
 
 # OWNER=jhyoon TARGET=25Q2
@@ -163,4 +164,5 @@
     namespace: "telephony"
     description: "This flag controls the type of API regarding MmTelFeature, either hidden or system type."
     bug:"359721349"
+    is_exported: true
 }
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 03d1f4e..ec7b8fa 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -230,3 +230,19 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=jackyu TARGET=25Q2
+flag {
+    name: "deprecate_cdma"
+    namespace: "telephony"
+    description: "Deprecate CDMA and NV APIS"
+    bug: "379356026"
+}
+
+# OWNER=jackyu TARGET=25Q2
+flag {
+    name: "cleanup_cdma"
+    namespace: "telephony"
+    description: "Disable CDMA and NV backing code"
+    bug: "379356026"
+}
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
index 8662572..ee4570e 100644
--- a/flags/satellite.aconfig
+++ b/flags/satellite.aconfig
@@ -41,6 +41,7 @@
     namespace: "telephony"
     description: "This flag enables satellite carrier roaming to nb iot ntn."
     bug:"348253735"
+    is_exported: true
 }
 
 # OWNER=tnd TARGET=24Q4
@@ -78,4 +79,5 @@
   namespace: "telephony"
   description: "Introduce SatelliteManager APIs for carrier apps to monitor satellite state change"
   bug: "357638490"
+  is_exported: true
 }
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index aea9bd0..0522a3c 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -88,4 +88,5 @@
   namespace: "telephony"
   description: "Provide APIs to retrieve the status and recurrence rule info on a subscription plan"
   bug: "357272015"
+  is_exported: true
 }
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index abe4296..8561dac 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -49,6 +49,7 @@
     namespace: "telephony"
     description: "This flag controls the visibility of the setCarrierRestrictionStatus API in carrierRestrictionRules class."
     bug:"342411308"
+    is_exported: true
 }
 
 # OWNER=arunvoddu TARGET=24Q4
@@ -87,4 +88,41 @@
     namespace: "telephony"
     description: "This flag controls the type of API that retrieves ISIM records, either hidden or system type."
     bug:"359721349"
+    is_exported: true
+}
+
+# OWNER=jinjeong TARGET=25Q2
+flag {
+    name: "carrier_id_from_carrier_identifier"
+    namespace: "telephony"
+    description: "This flag controls to get a carrier id using a carrier identifier."
+    bug:"378778278"
+    is_exported: true
+}
+
+# OWNER=arunvoddu TARGET=25Q2
+flag {
+    name: "force_imsi_certificate_delete"
+    namespace: "telephony"
+    description: "This flag controls the IMSI certificate delete with out any condition."
+    bug:"235296888"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=jinjeong TARGET=25Q2
+flag {
+    name: "get_group_id_level2"
+    namespace: "telephony"
+    description: "This flag controls to get a group id level2."
+    bug:"381171540"
+}
+
+# OWNER=jinjeong TARGET=25Q2
+flag {
+    name: "action_sim_preference_settings"
+    namespace: "telephony"
+    description: "This flag controls to launch sim preference page in Setting"
+    bug:"381319469"
 }
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 8364c0a..206770d 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -273,21 +273,25 @@
 
     /**
      * Resets the Carrier Keys in the database. This involves 2 steps:
-     *  1. Delete the keys from the database.
-     *  2. Send an intent to download new Certificates.
-     * @param context Context
-     * @param mPhoneId phoneId
+     * 1. Delete the keys from the database.
+     * 2. Send an intent to download new Certificates.
      *
+     * @param context       Context
+     * @param mPhoneId      phoneId
+     * @param forceResetAll to skip the check of the RESET_CARRIER_KEY_RATE_LIMIT.
      */
-    public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId) {
-        Log.i(LOG_TAG, "resetting carrier key");
+    public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId,
+            boolean forceResetAll) {
+        Log.i(LOG_TAG, "resetting carrier key, forceResetAll = " +forceResetAll);
         // Check rate limit.
         long now = System.currentTimeMillis();
-        if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) {
-            Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded");
-            return;
+        if (!forceResetAll) {
+            if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) {
+                Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded");
+                return;
+            }
+            mLastAccessResetCarrierKey = now;
         }
-        mLastAccessResetCarrierKey = now;
 
         int subId = SubscriptionManager.getSubscriptionId(mPhoneId);
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index ee7447c..1b19c99 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -2952,4 +2952,39 @@
      * @param h Handler to be removed from the registrant list.
      */
     default void unregisterForSecurityAlgorithmUpdates(Handler h) {}
+
+    /**
+     * Set the non-terrestrial PLMN with lower priority than terrestrial networks.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+     *                        supported by user subscription.
+     * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+     *                             PLMNs that are not supported by the carrier and make sure not to
+     *                             attach to them.
+     * @param result Callback message to receive the result.
+     */
+    default void setSatellitePlmn(int simSlot,
+            @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
+            Message result) {}
+
+    /**
+     * Enable or disable satellite in the cellular modem associated with a carrier.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite.
+     * @param result Callback message to receive the result.
+     */
+    default void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled,
+            Message result) {}
+
+    /**
+     * Check whether satellite is enabled in the cellular modem associated with a carrier.
+     *
+     * @param simSlot Indicates the SIM slot to which this API will be applied.
+     * @param result Callback message to receive the result.
+     */
+    default void isSatelliteEnabledForCarrier(int simSlot, Message result) {}
 }
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java
index d3f0264..bb19a31 100644
--- a/src/java/com/android/internal/telephony/DisplayInfoController.java
+++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -110,7 +110,7 @@
         mTelephonyDisplayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_UNKNOWN,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
-                false);
+                false, false, false);
         mNetworkTypeController = new NetworkTypeController(phone, this, featureFlags);
         // EVENT_UPDATE will transition from DefaultState to the current state
         // and update the TelephonyDisplayInfo based on the current state.
@@ -132,7 +132,9 @@
         TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(
                 mNetworkTypeController.getDataNetworkType(),
                 mNetworkTypeController.getOverrideNetworkType(),
-                isRoaming());
+                isRoaming(),
+                mPhone.getServiceStateTracker().getServiceState().isUsingNonTerrestrialNetwork(),
+                mNetworkTypeController.getSatelliteConstrainedData());
         if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) {
             logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to "
                     + newDisplayInfo);
diff --git a/src/java/com/android/internal/telephony/EventLogTags.logtags b/src/java/com/android/internal/telephony/EventLogTags.logtags
index b5e458b..2f30c33 100644
--- a/src/java/com/android/internal/telephony/EventLogTags.logtags
+++ b/src/java/com/android/internal/telephony/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
 
 option java_package com.android.internal.telephony;
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 6a6e4d0..5a73cae 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -2189,7 +2189,12 @@
 
     @Override
     public void resetCarrierKeysForImsiEncryption() {
-        mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId);
+        mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId, false);
+    }
+
+    @Override
+    public void resetCarrierKeysForImsiEncryption(boolean forceResetAll) {
+        mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId, forceResetAll);
     }
 
     @Override
@@ -3770,6 +3775,13 @@
                         && disclosure != null) {
                     mIdentifierDisclosureNotifier.addDisclosure(mContext, getSubId(), disclosure);
                 }
+                if (mFeatureFlags.cellularIdentifierDisclosureIndications()
+                        && mIdentifierDisclosureNotifier != null
+                        && disclosure != null) {
+                    logd("EVENT_CELL_IDENTIFIER_DISCLOSURE for non-Safety Center listeners "
+                            + "phoneId = " + getPhoneId());
+                    mNotifier.notifyCellularIdentifierDisclosedChanged(this, disclosure);
+                }
                 break;
 
             case EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE:
@@ -3780,13 +3792,21 @@
 
             case EVENT_SECURITY_ALGORITHM_UPDATE:
                 logd("EVENT_SECURITY_ALGORITHM_UPDATE phoneId = " + getPhoneId());
+
+                ar = (AsyncResult) msg.obj;
+                SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result;
+
                 if (mFeatureFlags.enableModemCipherTransparencyUnsolEvents()
                         && mNullCipherNotifier != null) {
-                    ar = (AsyncResult) msg.obj;
-                    SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result;
                     mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getPhoneId(),
                             getSubId(), update);
                 }
+                if (mFeatureFlags.securityAlgorithmsUpdateIndications()
+                        && mNullCipherNotifier != null) {
+                    logd("EVENT_SECURITY_ALGORITHM_UPDATE for non-Safety Center listeners "
+                              + "phoneId = " + getPhoneId());
+                    mNotifier.notifySecurityAlgorithmsChanged(this, update);
+                }
                 break;
 
             case EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE:
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index b4a37b3..ccf066f 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -551,6 +551,36 @@
         }
     }
 
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSatellitePlmnResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSatelliteEnabledForCarrierResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param isEnabled Indicates whether satellite is enabled for carrier or not.
+     */
+    public void isSatelliteEnabledForCarrierResponse(RadioResponseInfo responseInfo,
+            boolean isEnabled) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
+            }
+            mRil.processResponseDone(rr, responseInfo, isEnabled);
+        }
+    }
+
     @Override
     public String getInterfaceHash() {
         return IRadioNetworkResponse.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 081a5c8..b12a3df 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -22,7 +22,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
@@ -233,6 +239,72 @@
     private boolean mDoesPccListIndicateIdle = false;
 
     private boolean mInVoiceCall = false;
+    private boolean mIsSatelliteConstrainedData = false;
+    private boolean mIsSatelliteNetworkCallbackRegistered = false;
+    private ConnectivityManager mConnectivityManager;
+
+    private final ConnectivityManager.NetworkCallback mNetworkCallback =
+            new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onAvailable(Network network) {
+                    log("On Available: " + network);
+                    if (network != null) {
+                        if (mConnectivityManager != null) {
+                            NetworkCapabilities capabilities =
+                                    mConnectivityManager.getNetworkCapabilities(network);
+                            updateBandwidthConstrainedStatus(capabilities);
+                        } else {
+                            log("network is null");
+                        }
+                    }
+                }
+
+                @Override
+                public void onCapabilitiesChanged(Network network,
+                        NetworkCapabilities networkCapabilities) {
+                    log("onCapabilitiesChanged: " + network);
+                    if (network != null) {
+                        updateBandwidthConstrainedStatus(networkCapabilities);
+                    } else {
+                        log("network is null");
+                    }
+                }
+
+                @Override
+                public void onLost(Network network) {
+                    log("Network Lost");
+                    if (mIsSatelliteConstrainedData) {
+                        mIsSatelliteConstrainedData = false;
+                        mDisplayInfoController.updateTelephonyDisplayInfo();
+                    }
+                }
+            };
+
+    private boolean isBandwidthConstrainedCapabilitySupported(NetworkCapabilities
+            capabilities) {
+        // TODO (b/382002908: Remove try catch exception for
+        //  NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED & replace datautils with
+        //  NetworkCapabilities on api availability at mainline module)
+        try {
+            return capabilities.hasTransport(
+                    NetworkCapabilities.TRANSPORT_SATELLITE) &&
+                    !capabilities.hasCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+        } catch (Exception ignored) {
+            log("NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED not supported ");
+            return false;
+        }
+    }
+
+    private void updateBandwidthConstrainedStatus(NetworkCapabilities capabilities) {
+        if (capabilities != null) {
+            mIsSatelliteConstrainedData
+                    = isBandwidthConstrainedCapabilitySupported(capabilities);
+            log("satellite constrained data status : " + mIsSatelliteConstrainedData);
+            mDisplayInfoController.updateTelephonyDisplayInfo();
+        } else {
+            log("capabilities is null");
+        }
+    }
 
     /**
      * NetworkTypeController constructor.
@@ -266,9 +338,42 @@
         mServiceState = mPhone.getServiceStateTracker().getServiceState();
         mPhysicalChannelConfigs = mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
 
+        if(mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            registerForSatelliteNetwork();
+        }
+
         sendMessage(EVENT_INITIALIZE);
     }
 
+    public synchronized void registerForSatelliteNetwork() {
+        if (!mIsSatelliteNetworkCallbackRegistered) {
+            mIsSatelliteNetworkCallbackRegistered = true;
+            HandlerThread handlerThread = new HandlerThread("SatelliteDataUsageThread");
+            handlerThread.start();
+            Handler handler = new Handler(handlerThread.getLooper());
+
+            NetworkRequest.Builder builder = new NetworkRequest.Builder();
+            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+            // TODO (b/382002908: Remove try catch exception for
+            //  NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED & replace datautils with
+            //  NetworkCapabilities on api availability at mainline module)
+            try {
+                builder.removeCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+            } catch (Exception ignored) {
+                log("NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED not supported ");
+            }
+            mConnectivityManager =
+                    (ConnectivityManager) mPhone.getContext()
+                            .getSystemService(Context.CONNECTIVITY_SERVICE);
+            if (mConnectivityManager != null) {
+                mConnectivityManager.registerBestMatchingNetworkCallback(
+                        builder.build(), mNetworkCallback, handler);
+            } else {
+                loge("network callback not registered");
+            }
+        }
+    }
+
     /**
      * @return The current override network type, used to create TelephonyDisplayInfo in
      * DisplayInfoController.
@@ -289,6 +394,15 @@
     }
 
     /**
+     * @return satellite bandwidth constrained connection status, used to create
+     * TelephonyDisplayInfo in DisplayInfoController.
+     *
+     */
+    public boolean getSatelliteConstrainedData() {
+       return mIsSatelliteConstrainedData;
+    }
+
+    /**
      * @return {@code true} if either the primary or secondary 5G icon timers are active,
      * and {@code false} if neither are.
      */
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index ab9be76..5998d46 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -4076,6 +4076,16 @@
     }
 
     /**
+     * Resets the Carrier Keys in the database. This involves 2 steps:
+     * 1. Delete the keys from the database.
+     * 2. Send an intent to download new Certificates.
+     *
+     * @param forceResetAll : Force delete the downloaded key if any.
+     */
+    public void resetCarrierKeysForImsiEncryption(boolean forceResetAll) {
+    }
+
+    /**
      * Return if UT capability of ImsPhone is enabled or not
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index c40193e..9801542 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -659,6 +659,20 @@
                 });
     }
 
+    /**
+     * Return GroupIdLevel2 for the subscriber
+     */
+    public String getGroupIdLevel2ForSubscriber(int subId, String callingPackage,
+            String callingFeatureId) {
+        return callPhoneMethodForSubIdWithPrivilegedCheck(subId,
+                "getGroupIdLevel2", (phone)-> {
+                    enforceTelephonyFeatureWithException(callingPackage,
+                            PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                            "getGroupIdLevel2ForSubscriber");
+                    return phone.getGroupIdLevel2();
+                });
+    }
+
     /** Below are utility methods that abstracts the flow that many public methods use:
      *  1. Check permission: pass, throw exception, or fails (returns false).
      *  2. clearCallingIdentity.
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 34ac832..1bb9984 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -181,6 +181,9 @@
     /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_2_3 = new HalVersion(2, 3);
 
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_2_4 = new HalVersion(2, 4);
+
     // Hal version
     private final Map<Integer, HalVersion> mHalVersion = new HashMap<>();
 
@@ -5352,6 +5355,102 @@
             });
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setSatellitePlmn(int simSlot, @NonNull List<String> carrierPlmnList,
+            @NonNull List<String> allSatellitePlmnList, Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest(
+                "setSatellitePlmn",
+                networkProxy,
+                result,
+                RADIO_HAL_VERSION_2_4)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SATELLITE_PLMN, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " simSlot=" + simSlot + " carrierPlmnList=" + carrierPlmnList
+                    + " allSatellitePlmnList=" + allSatellitePlmnList);
+        }
+
+        radioServiceInvokeHelper(
+                HAL_SERVICE_NETWORK,
+                rr,
+                "setSatellitePlmn",
+                () -> {
+                    networkProxy.setSatellitePlmn(rr.mSerial, simSlot, carrierPlmnList,
+                            allSatellitePlmnList);
+                });
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled,
+            Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest(
+                "setSatelliteEnabledForCarrier",
+                networkProxy,
+                result,
+                RADIO_HAL_VERSION_2_4)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " simSlot=" + simSlot + " satelliteEnabled=" + satelliteEnabled);
+        }
+
+        radioServiceInvokeHelper(
+                HAL_SERVICE_NETWORK,
+                rr,
+                "setSatelliteEnabledForCarrier",
+                () -> {
+                    networkProxy.setSatelliteEnabledForCarrier(rr.mSerial, simSlot,
+                            satelliteEnabled);
+                });
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void isSatelliteEnabledForCarrier(int simSlot, Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest(
+                "isSatelliteEnabledForCarrier",
+                networkProxy,
+                result,
+                RADIO_HAL_VERSION_2_4)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " simSlot=" + simSlot);
+        }
+
+        radioServiceInvokeHelper(
+                HAL_SERVICE_NETWORK, rr, "isSatelliteEnabledForCarrier", () -> {
+                    networkProxy.isSatelliteEnabledForCarrier(rr.mSerial, simSlot);
+                });
+    }
+
+
     //***** Private Methods
     /**
      * This is a helper function to be called when an indication callback is called for any radio
@@ -6216,6 +6315,7 @@
             case 2: return RADIO_HAL_VERSION_2_1;
             case 3: return RADIO_HAL_VERSION_2_2;
             case 4: return RADIO_HAL_VERSION_2_3;
+            case 5: return RADIO_HAL_VERSION_2_4;
             default: return RADIO_HAL_VERSION_UNKNOWN;
         }
     }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index cbf2330..a6d9803 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -121,6 +121,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_N1_MODE_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_VONR_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_CALL_FAIL_CAUSE;
@@ -180,6 +181,8 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_RADIO_CAPABILITY;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SATELLITE_PLMN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
@@ -394,6 +397,7 @@
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.uicc.PortUtils;
 import com.android.internal.telephony.uicc.SimPhonebookRecord;
+import com.android.internal.telephony.uicc.SimTypeInfo;
 import com.android.telephony.Rlog;
 
 import java.io.ByteArrayInputStream;
@@ -1685,6 +1689,9 @@
         if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0) {
             raf |= android.hardware.radio.RadioAccessFamily.NR;
         }
+        if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NB_IOT_NTN) != 0) {
+            raf |= android.hardware.radio.RadioAccessFamily.NB_IOT_NTN;
+        }
         return (raf == 0) ? android.hardware.radio.RadioAccessFamily.UNKNOWN : raf;
     }
 
@@ -5288,6 +5295,12 @@
                 return "IS_SECURITY_ALGORITHMS_UPDATED_ENABLED";
             case RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT:
                 return "GET_SIMULTANEOUS_CALLING_SUPPORT";
+            case RIL_REQUEST_SET_SATELLITE_PLMN:
+                return "SET_SATELLITE_PLMN";
+            case RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER:
+                return "SET_SATELLITE_ENABLED_FOR_CARRIER";
+            case RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER:
+                return "IS_SATELLITE_ENABLED_FOR_CARRIER";
             default:
                 return "<unknown request " + request + ">";
         }
@@ -5870,6 +5883,23 @@
                 securityAlgorithmUpdate.isUnprotectedEmergency);
     }
 
+    /** Convert an AIDL-based SimTypeInfo to its Java wrapper. */
+    public static ArrayList<SimTypeInfo> convertAidlSimTypeInfo(
+            android.hardware.radio.config.SimTypeInfo[] simTypeInfos) {
+        ArrayList<SimTypeInfo> response = new ArrayList<>();
+        if (simTypeInfos == null) {
+            loge("convertAidlSimTypeInfo received NULL simTypeInfos");
+            return response;
+        }
+        for (android.hardware.radio.config.SimTypeInfo simTypeInfo : simTypeInfos) {
+            SimTypeInfo info = new SimTypeInfo();
+            info.mSupportedSimTypes = simTypeInfo.supportedSimTypes;
+            info.setCurrentSimType(simTypeInfo.currentSimType);
+            response.add(info);
+        }
+        return response;
+    }
+
     private static void logd(String log) {
         Rlog.d("RILUtils", log);
     }
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java b/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java
index 0a41b11..6142fc2 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java
@@ -21,10 +21,10 @@
 import android.telephony.PhoneCapability;
 
 import com.android.internal.telephony.uicc.IccSlotStatus;
+import com.android.internal.telephony.uicc.SimTypeInfo;
 import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Set;
 
 /**
@@ -47,8 +47,7 @@
      */
     @Override
     public void getHalDeviceCapabilitiesResponse(
-            android.hardware.radio.RadioResponseInfo info,
-            boolean modemReducedFeatureSet1) throws RemoteException {
+            RadioResponseInfo info, boolean modemReducedFeatureSet1) throws RemoteException {
         // convert hal device capabilities to RadioInterfaceCapabilities
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
@@ -71,8 +70,7 @@
      */
     @Override
     public void getNumOfLiveModemsResponse(
-            android.hardware.radio.RadioResponseInfo info, byte numOfLiveModems)
-            throws RemoteException {
+            RadioResponseInfo info, byte numOfLiveModems) throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
             if (info.error == android.hardware.radio.RadioError.NONE) {
@@ -93,9 +91,8 @@
      */
     @Override
     public void getPhoneCapabilityResponse(
-            android.hardware.radio.RadioResponseInfo info,
-            android.hardware.radio.config.PhoneCapability phoneCapability)
-            throws RemoteException {
+            RadioResponseInfo info,
+            android.hardware.radio.config.PhoneCapability phoneCapability) throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
             PhoneCapability ret = RILUtils.convertHalPhoneCapability(
@@ -118,9 +115,7 @@
      */
     @Override
     public void getSimultaneousCallingSupportResponse(
-            android.hardware.radio.RadioResponseInfo info,
-            int[] enabledLogicalSlots)
-            throws RemoteException {
+            RadioResponseInfo info, int[] enabledLogicalSlots) throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
             ArrayList<Integer> ret = RILUtils.primitiveArrayToArrayList(enabledLogicalSlots);
@@ -142,7 +137,7 @@
      */
     @Override
     public void getSimSlotsStatusResponse(
-            android.hardware.radio.RadioResponseInfo info,
+            RadioResponseInfo info,
             android.hardware.radio.config.SimSlotStatus[] slotStatus)
             throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
@@ -166,8 +161,7 @@
      * Currently this is being used as the callback for RadioConfig.setNumOfLiveModems() method
      */
     @Override
-    public void setNumOfLiveModemsResponse(
-            android.hardware.radio.RadioResponseInfo info) throws RemoteException {
+    public void setNumOfLiveModemsResponse(RadioResponseInfo info) throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
             if (info.error == android.hardware.radio.RadioError.NONE) {
@@ -187,8 +181,7 @@
      * Response function for IRadioConfig.setPreferredDataModem().
      */
     @Override
-    public void setPreferredDataModemResponse(
-            android.hardware.radio.RadioResponseInfo info) throws RemoteException {
+    public void setPreferredDataModemResponse(RadioResponseInfo info) throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
             if (info.error == android.hardware.radio.RadioError.NONE) {
@@ -208,8 +201,7 @@
      * Response function for IRadioConfig.setSimSlotsMapping().
      */
     @Override
-    public void setSimSlotsMappingResponse(
-            android.hardware.radio.RadioResponseInfo info) throws RemoteException {
+    public void setSimSlotsMappingResponse(RadioResponseInfo info) throws RemoteException {
         RILRequest rr = mRadioConfig.processResponse(info);
         if (rr != null) {
             if (info.error == android.hardware.radio.RadioError.NONE) {
@@ -225,6 +217,48 @@
         }
     }
 
+    /**
+     * Response function for IRadioConfig.getSimTypeInfo().
+     */
+    @Override
+    public void getSimTypeInfoResponse(
+            RadioResponseInfo info,
+            android.hardware.radio.config.SimTypeInfo[] simTypeInfo) throws RemoteException {
+        RILRequest rr = mRadioConfig.processResponse(info);
+        if (rr != null) {
+            ArrayList<SimTypeInfo> ret = RILUtils.convertAidlSimTypeInfo(simTypeInfo);
+            if (info.error == android.hardware.radio.RadioError.NONE) {
+                // send response
+                RadioResponse.sendMessageResponse(rr.mResult, ret);
+                logd(rr, RILUtils.requestToString(rr.mRequest) + " " + ret.toString());
+            } else {
+                rr.onError(info.error, null);
+                loge(rr, RILUtils.requestToString(rr.mRequest) + " error " + info.error);
+            }
+        } else {
+            loge("getSimTypeInfoResponse: Error " + info.toString());
+        }
+    }
+
+    /**
+     * Response function for IRadioConfig.setSimTypeResponse().
+     */
+    @Override
+    public void setSimTypeResponse(RadioResponseInfo info) throws RemoteException {
+        RILRequest rr = mRadioConfig.processResponse(info);
+        if (rr != null) {
+            if (info.error == android.hardware.radio.RadioError.NONE) {
+                // send response
+                RadioResponse.sendMessageResponse(rr.mResult, null);
+                logd(rr, RILUtils.requestToString(rr.mRequest));
+            } else {
+                rr.onError(info.error, null);
+                loge(rr, RILUtils.requestToString(rr.mRequest) + " error " + info.error);
+            }
+        } else {
+            loge("setSimTypeResponse: Error " + info.toString());
+        }
+    }
     private static void logd(String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
index 4acc71a..12e6c90 100644
--- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java
+++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -979,4 +979,60 @@
         }
         // Only supported on AIDL.
     }
+
+   /**
+     * Set the non-terrestrial PLMN with lower priority than terrestrial networks.
+     *
+     * @param serial Serial number of request.
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+     *                        supported by user subscription.
+     * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+     *                             PLMNs that are not supported by the carrier and make sure not to
+     *                             attach to them.
+     */
+    public void setSatellitePlmn(int serial, int simSlot, List<String> carrierPlmnList,
+            List<String> allSatellitePlmnList) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            String[] carrierPlmnArray = carrierPlmnList.toArray(new String[0]);
+            String[] allSatellitePlmnArray = allSatellitePlmnList.toArray(new String[0]);
+            mNetworkProxy.setSatellitePlmn(serial, simSlot, carrierPlmnArray,
+                    allSatellitePlmnArray);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Enable or disable satellite in the cellular modem associated with a carrier.
+     *
+     * @param serial Serial number of request.
+     * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
+     *                this information to determine the relevant carrier.
+     * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite.
+     */
+    public void setSatelliteEnabledForCarrier(int serial, int simSlot,
+            boolean satelliteEnabled) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.setSatelliteEnabledForCarrier(serial, simSlot, satelliteEnabled);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Check whether satellite is enabled in the cellular modem associated with a carrier.
+     *
+     * @param serial Serial number of request.
+     * @param simSlot Indicates the SIM slot to which this API will be applied.
+     */
+    public void isSatelliteEnabledForCarrier(int serial, int simSlot)
+            throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.isSatelliteEnabledForCarrier(serial, simSlot);
+        }
+        // Only supported on AIDL.
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 871cabc..88b9958 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -825,6 +825,9 @@
 
         SmsResponse smsResponse = new SmsResponse(messageRef, null /* ackPdu */, NO_ERROR_CODE,
                 tracker.mMessageId);
+        if (Flags.temporaryFailuresInCarrierMessagingService()) {
+            tracker.mResultCodeFromCarrierMessagingService = result;
+        }
 
         switch (result) {
             case CarrierMessagingService.SEND_STATUS_OK:
@@ -836,10 +839,34 @@
                                                           smsResponse,
                                                           null /* exception*/)));
                 break;
-            case CarrierMessagingService.SEND_STATUS_ERROR:
-                Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService"
-                        + " failed. "
-                        + SmsController.formatCrossStackMessageId(tracker.mMessageId));
+            case CarrierMessagingService.SEND_STATUS_ERROR: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NULL_PDU: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NO_SERVICE: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE: // fall through
+            case CarrierMessagingService
+                    .SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED: // fall through
+            case CarrierMessagingService
+                    .SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_REJECT: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_ARGUMENTS: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_STATE: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMS_FORMAT: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_ERROR: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_ENCODING_ERROR: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_CANCELLED: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED: // fall through
+            case CarrierMessagingService
+                    .SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY: // fall through
+            case CarrierMessagingService.SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED: // fall through
+                Rlog.d(
+                        TAG,
+                        "processSendSmsResponse: Sending SMS by CarrierMessagingService"
+                                + " failed. "
+                                + SmsController.formatCrossStackMessageId(tracker.mMessageId));
                 sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
                         new AsyncResult(tracker, smsResponse,
                                 new CommandException(CommandException.Error.GENERIC_FAILURE))));
@@ -858,6 +885,55 @@
         }
     }
 
+    private int toSmsManagerResultForSendSms(int carrierMessagingServiceResult) {
+        switch (carrierMessagingServiceResult) {
+            case CarrierMessagingService.SEND_STATUS_OK:
+                return Activity.RESULT_OK;
+            case CarrierMessagingService.SEND_STATUS_ERROR:
+                return SmsManager.RESULT_RIL_GENERIC_ERROR;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE:
+                return SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NULL_PDU:
+                return SmsManager.RESULT_ERROR_NULL_PDU;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NO_SERVICE:
+                return SmsManager.RESULT_ERROR_NO_SERVICE;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED:
+                return SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE:
+                return SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED:
+                return SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED:
+                return SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+            case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_REJECT:
+                return SmsManager.RESULT_NETWORK_REJECT;
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_ARGUMENTS:
+                return SmsManager.RESULT_INVALID_ARGUMENTS;
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_STATE:
+                return SmsManager.RESULT_INVALID_STATE;
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMS_FORMAT:
+                return SmsManager.RESULT_INVALID_SMS_FORMAT;
+            case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_ERROR:
+                return SmsManager.RESULT_NETWORK_ERROR;
+            case CarrierMessagingService.SEND_STATUS_RESULT_ENCODING_ERROR:
+                return SmsManager.RESULT_ENCODING_ERROR;
+            case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS:
+                return SmsManager.RESULT_INVALID_SMSC_ADDRESS;
+            case CarrierMessagingService.SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED:
+                return SmsManager.RESULT_OPERATION_NOT_ALLOWED;
+            case CarrierMessagingService.SEND_STATUS_RESULT_CANCELLED:
+                return SmsManager.RESULT_CANCELLED;
+            case CarrierMessagingService.SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED:
+                return SmsManager.RESULT_REQUEST_NOT_SUPPORTED;
+            case CarrierMessagingService.SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY:
+                return SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY;
+            case CarrierMessagingService.SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED:
+                return SmsManager.RESULT_SMS_SEND_RETRY_FAILED;
+            default:
+                return SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+        }
+    }
+
     /**
      * Use the carrier messaging service to send a multipart text SMS.
      */
@@ -1084,10 +1160,20 @@
                         + SmsController.formatCrossStackMessageId(tracker.mMessageId));
             }
 
-            int ss = mPhone.getServiceState().getState();
-            int error = rilErrorToSmsManagerResult(
-                    ((CommandException) (ar.exception)).getCommandError(), tracker);
+            int error;
+            if (Flags.temporaryFailuresInCarrierMessagingService()
+                    && tracker.mResultCodeFromCarrierMessagingService
+                            != CarrierMessagingService.SEND_STATUS_OK) {
+                error =
+                        toSmsManagerResultForSendSms(
+                                tracker.mResultCodeFromCarrierMessagingService);
+            } else {
+                error =
+                        rilErrorToSmsManagerResult(
+                                ((CommandException) (ar.exception)).getCommandError(), tracker);
+            }
 
+            int ss = mPhone.getServiceState().getState();
             if (tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
                 // This is retry after failure over IMS but voice is not available.
                 // Set retry to max allowed, so no retry is sent and cause
@@ -2489,6 +2575,9 @@
 
         public final long mMessageId;
 
+        // A CarrierMessagingService result code to be returned to the caller.
+        public int mResultCodeFromCarrierMessagingService;
+
         private Boolean mIsFromDefaultSmsApplication;
 
         private int mCarrierId;
@@ -2533,6 +2622,7 @@
             mCarrierId = carrierId;
             mSkipShortCodeDestAddrCheck = skipShortCodeDestAddrCheck;
             mUniqueMessageId = uniqueMessageId;
+            mResultCodeFromCarrierMessagingService = CarrierMessagingService.SEND_STATUS_OK;
         }
 
         @VisibleForTesting
@@ -2552,6 +2642,7 @@
             mCarrierId = 0;
             mSkipShortCodeDestAddrCheck = false;
             mUniqueMessageId = 0;
+            mResultCodeFromCarrierMessagingService = CarrierMessagingService.SEND_STATUS_OK;
         }
 
         public HashMap<String, Object> getData() {
@@ -2647,6 +2738,14 @@
         }
 
         /**
+         * Returns the flag specifying whether any part of this {@link SmsTracker} failed to send
+         * or not.
+         */
+        protected boolean isAnyPartFailed() {
+            return mAnyPartFailed != null && mAnyPartFailed.get();
+        }
+
+        /**
          * Persist a sent SMS if required:
          * 1. It is a text message
          * 2. SmsApplication tells us to persist: sent from apps that are not default-SMS app or
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 58f3490..91aac26 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -1234,7 +1234,8 @@
             boolean isOverIms, boolean isLastSmsPart, boolean success) {
         notifySmsSentToEmergencyStateTracker(tracker.mDestAddress,
             tracker.mMessageId, isOverIms, isLastSmsPart, success);
-        notifySmsSentToDatagramDispatcher(tracker.mUniqueMessageId, isLastSmsPart, success);
+        notifySmsSentToDatagramDispatcher(tracker.mUniqueMessageId,
+                tracker.isSinglePartOrLastPart(), success && !tracker.isAnyPartFailed());
     }
 
     /**
@@ -1256,9 +1257,10 @@
 
     private void notifySmsSentToDatagramDispatcher(
             long messageId, boolean isLastSmsPart, boolean success) {
-        if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
+        if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)
+                && isLastSmsPart) {
             DatagramDispatcher.getInstance().onSendSmsDone(
-                    mPhone.getSubId(), messageId, isLastSmsPart, success);
+                    mPhone.getSubId(), messageId, success);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index 40c0081..f5eae91 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -225,8 +225,6 @@
         SIM_LOADED(true),
         /** SIM is removed. */
         SIM_REMOVAL(true),
-        /** SIM is disabled. */
-        SIM_DISABLED(true),
         /** Data profiles changed. */
         DATA_PROFILES_CHANGED(true),
         /** When service state changes.(For now only considering data RAT and data registration). */
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 9e432e1..d5bc741 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -3543,6 +3543,15 @@
     }
 
     /**
+     * Called when SIM is absent.
+     */
+    private void onSimAbsent() {
+        log("onSimAbsent");
+        sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+                DataEvaluationReason.SIM_REMOVAL));
+    }
+
+    /**
      * Called when SIM state changes.
      *
      * @param simState SIM state. (Note this is mixed with card state and application state.)
@@ -3550,22 +3559,13 @@
     private void onSimStateChanged(@SimState int simState) {
         log("onSimStateChanged: state=" + TelephonyManager.simStateToString(simState));
         if (mSimState != simState) {
+            mSimState = simState;
             if (simState == TelephonyManager.SIM_STATE_ABSENT) {
-                log("onSimStateChanged: SIM absent.");
-                sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
-                        DataEvaluationReason.SIM_REMOVAL));
-            } else if (simState == TelephonyManager.SIM_STATE_NOT_READY
-                    && mSimState == TelephonyManager.SIM_STATE_LOADED) {
-                if (mFeatureFlags.simDisabledGracefulTearDown()) {
-                    log("onSimStateChanged: SIM disabled.");
-                    sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
-                            DataEvaluationReason.SIM_DISABLED));
-                }
+                onSimAbsent();
             } else if (simState == TelephonyManager.SIM_STATE_LOADED) {
                 sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
                         DataEvaluationReason.SIM_LOADED));
             }
-            mSimState = simState;
             mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                     () -> callback.onSimStateChanged(mSimState)));
         }
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index 48cc7cb..b3e8095 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -109,8 +109,6 @@
      * from the modem.
      */
     private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1 * 1000;
-    @VisibleForTesting
-    public static final int DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS = 3 * 1000;
     /** Default value for if Emergency Callback Mode is supported. */
     private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true;
     /** Default Emergency Callback Mode exit timeout value. */
@@ -142,6 +140,7 @@
     private final CarrierConfigManager mConfigManager;
     private final Handler mHandler;
     private final boolean mIsSuplDdsSwitchRequiredForEmergencyCall;
+    private final int mWaitForInServiceTimeoutMs;
     private final PowerManager.WakeLock mWakeLock;
     private RadioOnHelper mRadioOnHelper;
     @EmergencyConstants.EmergencyMode
@@ -474,10 +473,10 @@
      * @param featureFlags                            The telephony feature flags.
      */
     public static void make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall,
-            @NonNull FeatureFlags featureFlags) {
+            int waitForInServiceTimeout, @NonNull FeatureFlags featureFlags) {
         if (INSTANCE == null) {
             INSTANCE = new EmergencyStateTracker(context, Looper.myLooper(),
-                    isSuplDdsSwitchRequiredForEmergencyCall, featureFlags);
+                    isSuplDdsSwitchRequiredForEmergencyCall, waitForInServiceTimeout, featureFlags);
         }
     }
 
@@ -497,11 +496,13 @@
      * Initializes EmergencyStateTracker.
      */
     private EmergencyStateTracker(Context context, Looper looper,
-            boolean isSuplDdsSwitchRequiredForEmergencyCall, @NonNull FeatureFlags featureFlags) {
+            boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout,
+            @NonNull FeatureFlags featureFlags) {
         mEcmExitTimeoutMs = DEFAULT_ECM_EXIT_TIMEOUT_MS;
         mContext = context;
         mHandler = new MyHandler(looper);
         mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall;
+        mWaitForInServiceTimeoutMs = waitForInServiceTimeout;
         mFeatureFlags = featureFlags;
         PowerManager pm = context.getSystemService(PowerManager.class);
         mWakeLock = (pm != null) ? pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -534,6 +535,10 @@
      * @param looper                                  The {@link Looper} of the application.
      * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for
      *                                                emergency call.
+     * @param waitForInServiceTimeout                 The timeout duration how long does it wait for
+     *                                                modem to get in-service state when emergency
+     *                                                call is dialed in airplane mode before
+     *                                                starting the emergency call.
      * @param phoneFactoryProxy                       The {@link PhoneFactoryProxy} to be injected.
      * @param phoneSwitcherProxy                      The {@link PhoneSwitcherProxy} to be injected.
      * @param telephonyManagerProxy                   The {@link TelephonyManagerProxy} to be
@@ -543,12 +548,14 @@
      */
     @VisibleForTesting
     public EmergencyStateTracker(Context context, Looper looper,
-            boolean isSuplDdsSwitchRequiredForEmergencyCall, PhoneFactoryProxy phoneFactoryProxy,
-            PhoneSwitcherProxy phoneSwitcherProxy, TelephonyManagerProxy telephonyManagerProxy,
-            RadioOnHelper radioOnHelper, long ecmExitTimeoutMs, FeatureFlags featureFlags) {
+            boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout,
+            PhoneFactoryProxy phoneFactoryProxy, PhoneSwitcherProxy phoneSwitcherProxy,
+            TelephonyManagerProxy telephonyManagerProxy, RadioOnHelper radioOnHelper,
+            long ecmExitTimeoutMs, FeatureFlags featureFlags) {
         mContext = context;
         mHandler = new MyHandler(looper);
         mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall;
+        mWaitForInServiceTimeoutMs = waitForInServiceTimeout;
         mPhoneFactoryProxy = phoneFactoryProxy;
         mPhoneSwitcherProxy = phoneSwitcherProxy;
         mTelephonyManagerProxy = telephonyManagerProxy;
@@ -1689,8 +1696,7 @@
 
             final Phone phoneForEmergency = phone;
             final android.telecom.Connection expectedConnection = mOngoingConnection;
-            final int waitForInServiceTimeout =
-                    needToTurnOnRadio ? DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS : 0;
+            final int waitForInServiceTimeout = needToTurnOnRadio ? mWaitForInServiceTimeoutMs : 0;
             Rlog.i(TAG, "turnOnRadioAndSwitchDds: timeout=" + waitForInServiceTimeout);
             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
                 @Override
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 9f74890..fdd0c0a 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -177,7 +177,6 @@
 
     /** 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. */
@@ -710,76 +709,75 @@
 
     /** Adds a new {@link SatelliteController} to the storage. */
     public synchronized void addSatelliteControllerStats(SatelliteController stats) {
-        // SatelliteController is a single data point
-        SatelliteController[] atomArray = mAtoms.satelliteController;
-        if (atomArray == null || atomArray.length == 0) {
-            atomArray = new SatelliteController[] {new SatelliteController()};
+        // find existing satellite controller atom with same carrier ID.
+        SatelliteController existingStats = find(stats);
+        if (existingStats != null) {
+            existingStats.countOfSatelliteServiceEnablementsSuccess
+                    += stats.countOfSatelliteServiceEnablementsSuccess;
+            existingStats.countOfSatelliteServiceEnablementsFail
+                    += stats.countOfSatelliteServiceEnablementsFail;
+            existingStats.countOfOutgoingDatagramSuccess
+                    += stats.countOfOutgoingDatagramSuccess;
+            existingStats.countOfOutgoingDatagramFail
+                    += stats.countOfOutgoingDatagramFail;
+            existingStats.countOfIncomingDatagramSuccess
+                    += stats.countOfIncomingDatagramSuccess;
+            existingStats.countOfIncomingDatagramFail
+                    += stats.countOfIncomingDatagramFail;
+            existingStats.countOfDatagramTypeSosSmsSuccess
+                    += stats.countOfDatagramTypeSosSmsSuccess;
+            existingStats.countOfDatagramTypeSosSmsFail
+                    += stats.countOfDatagramTypeSosSmsFail;
+            existingStats.countOfDatagramTypeLocationSharingSuccess
+                    += stats.countOfDatagramTypeLocationSharingSuccess;
+            existingStats.countOfDatagramTypeLocationSharingFail
+                    += stats.countOfDatagramTypeLocationSharingFail;
+            existingStats.countOfProvisionSuccess
+                    += stats.countOfProvisionSuccess;
+            existingStats.countOfProvisionFail
+                    += stats.countOfProvisionFail;
+            existingStats.countOfDeprovisionSuccess
+                    += stats.countOfDeprovisionSuccess;
+            existingStats.countOfDeprovisionFail
+                    += stats.countOfDeprovisionFail;
+            existingStats.totalServiceUptimeSec
+                    += stats.totalServiceUptimeSec;
+            existingStats.totalBatteryConsumptionPercent
+                    += stats.totalBatteryConsumptionPercent;
+            existingStats.totalBatteryChargedTimeSec
+                    += stats.totalBatteryChargedTimeSec;
+            existingStats.countOfDemoModeSatelliteServiceEnablementsSuccess
+                    += stats.countOfDemoModeSatelliteServiceEnablementsSuccess;
+            existingStats.countOfDemoModeSatelliteServiceEnablementsFail
+                    += stats.countOfDemoModeSatelliteServiceEnablementsFail;
+            existingStats.countOfDemoModeOutgoingDatagramSuccess
+                    += stats.countOfDemoModeOutgoingDatagramSuccess;
+            existingStats.countOfDemoModeOutgoingDatagramFail
+                    += stats.countOfDemoModeOutgoingDatagramFail;
+            existingStats.countOfDemoModeIncomingDatagramSuccess
+                    += stats.countOfDemoModeIncomingDatagramSuccess;
+            existingStats.countOfDemoModeIncomingDatagramFail
+                    += stats.countOfDemoModeIncomingDatagramFail;
+            existingStats.countOfDatagramTypeKeepAliveSuccess
+                    += stats.countOfDatagramTypeKeepAliveSuccess;
+            existingStats.countOfDatagramTypeKeepAliveFail
+                    += stats.countOfDatagramTypeKeepAliveFail;
+            existingStats.countOfAllowedSatelliteAccess += stats.countOfAllowedSatelliteAccess;
+            existingStats.countOfDisallowedSatelliteAccess
+                    += stats.countOfDisallowedSatelliteAccess;
+            existingStats.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail;
+
+            existingStats.isProvisioned = stats.isProvisioned;
+
+            existingStats.countOfSatelliteAllowedStateChangedEvents
+                    += stats.countOfSatelliteAllowedStateChangedEvents;
+            existingStats.countOfSuccessfulLocationQueries +=
+                    stats.countOfSuccessfulLocationQueries;
+            existingStats.countOfFailedLocationQueries += stats.countOfFailedLocationQueries;
+        } else {
+            mAtoms.satelliteController = insertAtRandomPlace(mAtoms.satelliteController, stats,
+                    mMaxNumSatelliteStats);
         }
-
-        SatelliteController atom = atomArray[0];
-        atom.countOfSatelliteServiceEnablementsSuccess
-                += stats.countOfSatelliteServiceEnablementsSuccess;
-        atom.countOfSatelliteServiceEnablementsFail
-                += stats.countOfSatelliteServiceEnablementsFail;
-        atom.countOfOutgoingDatagramSuccess
-                += stats.countOfOutgoingDatagramSuccess;
-        atom.countOfOutgoingDatagramFail
-                += stats.countOfOutgoingDatagramFail;
-        atom.countOfIncomingDatagramSuccess
-                += stats.countOfIncomingDatagramSuccess;
-        atom.countOfIncomingDatagramFail
-                += stats.countOfIncomingDatagramFail;
-        atom.countOfDatagramTypeSosSmsSuccess
-                += stats.countOfDatagramTypeSosSmsSuccess;
-        atom.countOfDatagramTypeSosSmsFail
-                += stats.countOfDatagramTypeSosSmsFail;
-        atom.countOfDatagramTypeLocationSharingSuccess
-                += stats.countOfDatagramTypeLocationSharingSuccess;
-        atom.countOfDatagramTypeLocationSharingFail
-                += stats.countOfDatagramTypeLocationSharingFail;
-        atom.countOfProvisionSuccess
-                += stats.countOfProvisionSuccess;
-        atom.countOfProvisionFail
-                += stats.countOfProvisionFail;
-        atom.countOfDeprovisionSuccess
-                += stats.countOfDeprovisionSuccess;
-        atom.countOfDeprovisionFail
-                += stats.countOfDeprovisionFail;
-        atom.totalServiceUptimeSec
-                += stats.totalServiceUptimeSec;
-        atom.totalBatteryConsumptionPercent
-                += stats.totalBatteryConsumptionPercent;
-        atom.totalBatteryChargedTimeSec
-                += stats.totalBatteryChargedTimeSec;
-        atom.countOfDemoModeSatelliteServiceEnablementsSuccess
-                += stats.countOfDemoModeSatelliteServiceEnablementsSuccess;
-        atom.countOfDemoModeSatelliteServiceEnablementsFail
-                += stats.countOfDemoModeSatelliteServiceEnablementsFail;
-        atom.countOfDemoModeOutgoingDatagramSuccess
-                += stats.countOfDemoModeOutgoingDatagramSuccess;
-        atom.countOfDemoModeOutgoingDatagramFail
-                += stats.countOfDemoModeOutgoingDatagramFail;
-        atom.countOfDemoModeIncomingDatagramSuccess
-                += stats.countOfDemoModeIncomingDatagramSuccess;
-        atom.countOfDemoModeIncomingDatagramFail
-                += stats.countOfDemoModeIncomingDatagramFail;
-        atom.countOfDatagramTypeKeepAliveSuccess
-                += stats.countOfDatagramTypeKeepAliveSuccess;
-        atom.countOfDatagramTypeKeepAliveFail
-                += stats.countOfDatagramTypeKeepAliveFail;
-        atom.countOfAllowedSatelliteAccess += stats.countOfAllowedSatelliteAccess;
-        atom.countOfDisallowedSatelliteAccess += stats.countOfDisallowedSatelliteAccess;
-        atom.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail;
-
-        atom.isProvisioned = stats.isProvisioned;
-        atom.carrierId = stats.carrierId;
-
-        atom.countOfSatelliteAllowedStateChangedEvents
-                += stats.countOfSatelliteAllowedStateChangedEvents;
-        atom.countOfSuccessfulLocationQueries += stats.countOfSuccessfulLocationQueries;
-        atom.countOfFailedLocationQueries += stats.countOfFailedLocationQueries;
-
-        mAtoms.satelliteController = atomArray;
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
     }
 
@@ -1825,7 +1823,7 @@
             atoms.outgoingShortCodeSms = sanitizeAtoms(atoms.outgoingShortCodeSms,
                     OutgoingShortCodeSms.class, mMaxOutgoingShortCodeSms);
             atoms.satelliteController = sanitizeAtoms(atoms.satelliteController,
-                            SatelliteController.class, mMaxNumSatelliteControllerStats);
+                            SatelliteController.class, mMaxNumSatelliteStats);
             atoms.satelliteSession = sanitizeAtoms(atoms.satelliteSession,
                     SatelliteSession.class, mMaxNumSatelliteStats);
             atoms.satelliteIncomingDatagram = sanitizeAtoms(atoms.satelliteIncomingDatagram,
@@ -1848,7 +1846,7 @@
                     mMaxNumSatelliteStats);
             atoms.carrierRoamingSatelliteControllerStats = sanitizeAtoms(
                     atoms.carrierRoamingSatelliteControllerStats,
-                    CarrierRoamingSatelliteControllerStats.class, mMaxNumSatelliteControllerStats);
+                    CarrierRoamingSatelliteControllerStats.class, mMaxNumSatelliteStats);
             atoms.satelliteEntitlement = sanitizeAtoms(atoms.satelliteEntitlement,
                     SatelliteEntitlement.class, mMaxNumSatelliteStats);
             atoms.satelliteConfigUpdater = sanitizeAtoms(atoms.satelliteConfigUpdater,
@@ -2390,6 +2388,19 @@
     }
 
     /**
+     * Returns SatelliteController atom that has same carrier_id value or
+     * {@code null} if does not exist.
+     */
+    private @Nullable SatelliteController find(SatelliteController key) {
+        for (SatelliteController stats : mAtoms.satelliteController) {
+            if (stats.carrierId == key.carrierId) {
+                return stats;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns CarrierRoamingSatelliteControllerStats atom that has same carrier_id value or
      * {@code null} if does not exist.
      */
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index 9f6edf2..acd3fd1 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
@@ -419,6 +420,15 @@
                     && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED) {
                 return false;
             }
+            boolean allowCheckMessageInNotConnected =
+                    mContext.getResources().getBoolean(
+                            R.bool.config_satellite_allow_check_message_in_not_connected);
+            if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS
+                    && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED
+                    && allowCheckMessageInNotConnected
+                    && mFeatureFlags.carrierRoamingNbIotNtn()) {
+                return false;
+            }
             if (mSatelltieModemState != SATELLITE_MODEM_STATE_CONNECTED
                     && mSatelltieModemState != SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING) {
                 return true;
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index d6b1a70..113c3ee 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -406,10 +406,9 @@
                 SomeArgs args = (SomeArgs) msg.obj;
                 int subId = (int) args.arg1;
                 long messageId = (long) args.arg2;
-                boolean isLastPartSms = (boolean) args.arg3;
-                boolean success = (boolean) args.arg4;
+                boolean success = (boolean) args.arg3;
                 try {
-                    handleEventSendSmsDone(subId, messageId, isLastPartSms, success);
+                    handleEventSendSmsDone(subId, messageId, success);
                 } finally {
                     args.recycle();
                 }
@@ -754,6 +753,8 @@
 
     private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument,
             @NonNull @SatelliteManager.SatelliteResult int resultCode) {
+        long datagramTransmissionTime = argument.datagramStartTime > 0
+                ? (System.currentTimeMillis() - argument.datagramStartTime) : 0;
         SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics(
                 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder()
                         .setDatagramType(argument.datagramType)
@@ -761,15 +762,15 @@
                         .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes())
                         /* In case pending datagram has not been attempted to send to modem
                         interface. transfer time will be 0. */
-                        .setDatagramTransferTimeMillis(argument.datagramStartTime > 0
-                                ? (System.currentTimeMillis() - argument.datagramStartTime) : 0)
+                        .setDatagramTransferTimeMillis(datagramTransmissionTime)
                         .setIsDemoMode(mIsDemoMode)
                         .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId())
                         .build());
         if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType,
                     mIsDemoMode);
-            mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType);
+            mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType,
+                    datagramTransmissionTime);
         } else {
             mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType,
                     mIsDemoMode);
@@ -1182,15 +1183,13 @@
      * Sending MO SMS is completed.
      * @param subId subscription ID
      * @param messageId message ID of MO SMS
-     * @param isLastSmsPart whether this is the last sms part of MO SMS
      * @param success boolean specifying whether MO SMS is successfully sent or not.
      */
-    public void onSendSmsDone(int subId, long messageId, boolean isLastSmsPart, boolean success) {
+    public void onSendSmsDone(int subId, long messageId, boolean success) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = subId;
         args.arg2 = messageId;
-        args.arg3 = isLastSmsPart;
-        args.arg4 = success;
+        args.arg3 = success;
         sendMessage(obtainMessage(EVENT_SEND_SMS_DONE, args));
     }
 
@@ -1231,29 +1230,32 @@
         pendingSmsMap.clear();
     }
 
-    private void handleEventSendSmsDone(
-            int subId, long messageId, boolean isLastPartSms, boolean success) {
+    private void handleEventSendSmsDone(int subId, long messageId, boolean success) {
         synchronized (mLock) {
-            mSendingInProgress = false;
             PendingRequest pendingSms = mPendingSmsMap.remove(messageId);
-            int datagramType = pendingSms != null && pendingSms.isMtSmsPolling
+            if (pendingSms == null) {
+                // Just return, the SMS is not sent by DatagramDispatcher such as Data SMS
+                plogd("handleEventSendSmsDone there is no pendingSms for messageId=" + messageId);
+                return;
+            }
+
+            mSendingInProgress = false;
+            int datagramType = pendingSms.isMtSmsPolling
                     ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS  : DATAGRAM_TYPE_SMS;
 
             plogd("handleEventSendSmsDone subId=" + subId + " messageId=" + messageId
-                    + " isLastPartSms=" + isLastPartSms + " success=" + success
-                    + " datagramType=" + datagramType);
+                    + " success=" + success + " datagramType=" + datagramType);
 
             if (success) {
-                if (isLastPartSms) {
-                    // Update send status only after all parts of the SMS are sent
-                    mDatagramController.updateSendStatus(subId, datagramType,
-                            SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
-                            getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
-                }
+                // Update send status
+                mDatagramController.updateSendStatus(subId, datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+                        getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
                 if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS) {
                     startMtSmsPollingThrottle();
                 }
             } else {
+                // Update send status
                 mDatagramController.updateSendStatus(subId, datagramType,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
                         getPendingMessagesCount(), SATELLITE_RESULT_NETWORK_ERROR);
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index a921b89..75771d0 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -376,6 +376,10 @@
                         });
                     }
 
+                    // Send the captured data about incoming datagram to metric
+                    sInstance.reportMetrics(satelliteDatagram,
+                            SatelliteManager.SATELLITE_RESULT_SUCCESS);
+
                     if (pendingCount <= 0) {
                         sInstance.mDatagramController.updateReceiveStatus(mSubId,
                                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
@@ -393,10 +397,6 @@
                                 internalCallback::accept);
                         sInstance.pollPendingSatelliteDatagramsInternal(mSubId, callback);
                     }
-
-                    // Send the captured data about incoming datagram to metric
-                    sInstance.reportMetrics(satelliteDatagram,
-                            SatelliteManager.SATELLITE_RESULT_SUCCESS);
                     break;
                 }
 
@@ -748,8 +748,8 @@
                         (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT);
             }
             datagramTransferTime = (System.currentTimeMillis() - mDatagramTransferStartTime);
-            mDatagramTransferStartTime = 0;
         }
+        mDatagramTransferStartTime = 0;
 
         SatelliteStats.getInstance().onSatelliteIncomingDatagramMetrics(
                 new SatelliteStats.SatelliteIncomingDatagramParams.Builder()
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 65b4770..c1f03bb 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -30,18 +30,21 @@
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE;
 import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT;
 import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_DISPLAY_NAME_STRING;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL;
-import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
-import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY;
 import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
 import static android.telephony.SubscriptionManager.isValidSubscriptionId;
@@ -133,6 +136,7 @@
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteSupportedStateCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.ISelectedNbIotSatelliteSubscriptionCallback;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
@@ -141,6 +145,7 @@
 import android.telephony.satellite.SatelliteSubscriberInfo;
 import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
 import android.telephony.satellite.SatelliteSubscriptionInfo;
+import android.telephony.satellite.SystemSelectionSpecifier;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -299,6 +304,9 @@
     private static final int EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED = 55;
     private static final int EVENT_SET_NETWORK_SELECTION_AUTO_DONE = 56;
     private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 57;
+    private static final int CMD_UPDATE_SYSTEM_SELECTION_CHANNELS = 58;
+    private static final int EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE = 59;
+    private static final int EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED = 60;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -423,9 +431,16 @@
      */
     private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback>
             mTerrestrialNetworkAvailableChangedListeners = new ConcurrentHashMap<>();
-    private final Object mIsSatelliteSupportedLock = new Object();
+    /**
+     * Map key: binder of the callback, value: callback to receive selected NB IOT satellite
+     * subscription changed
+     */
+    private final ConcurrentHashMap<IBinder, ISelectedNbIotSatelliteSubscriptionCallback>
+            mSelectedNbIotSatelliteSubscriptionChangedListeners = new ConcurrentHashMap<>();
+
+    protected final Object mIsSatelliteSupportedLock = new Object();
     @GuardedBy("mIsSatelliteSupportedLock")
-    private Boolean mIsSatelliteSupported = null;
+    protected Boolean mIsSatelliteSupported = null;
     private boolean mIsDemoModeEnabled = false;
     private boolean mIsEmergency = false;
     private final Object mIsSatelliteEnabledLock = new Object();
@@ -481,6 +496,13 @@
      * {@code true} for enabled and {@code false} for disabled. */
     @NonNull private final Map<Integer, Boolean> mIsSatelliteAttachEnabledForCarrierArrayPerSub =
             new HashMap<>();
+    /** Key: subId, value: (key: Regional satellite config Id string, value: Integer
+     * arrays of earfcns in the corresponding regions.)
+     */
+    @GuardedBy("mRegionalSatelliteEarfcnsLock")
+    @NonNull private final Map<Integer, Map<String, Set<Integer>>>
+            mRegionalSatelliteEarfcns = new HashMap<>();
+    @NonNull private final Object mRegionalSatelliteEarfcnsLock = new Object();
     @NonNull private final FeatureFlags mFeatureFlags;
     @NonNull private final Object mSatelliteConnectedLock = new Object();
     /** Key: Subscription ID; Value: Last satellite connected time */
@@ -557,6 +579,21 @@
      * carrierPlmnList. */
     @GuardedBy("mSupportedSatelliteServicesLock")
     private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
+    /** Key Subscription ID, value : map to plmn info with related data plan. */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    SparseArray<Map<String, Integer>> mEntitlementDataPlanMapPerCarrier = new SparseArray<>();
+    /** Key Subscription ID, value : map to plmn info with related service type. */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    SparseArray<Map<String, List<Integer>>> mEntitlementServiceTypeMapPerCarrier =
+            new SparseArray<>();
+    /** Key Subscription ID, value : map to plmn info with related service policy for data service */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    SparseArray<Map<String, Integer>> mEntitlementDataServicePolicyMapPerCarrier =
+            new SparseArray<>();
+    /** Key Subscription ID, value : map to plmn info with related service policy for voice service */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    SparseArray<Map<String, Integer>> mEntitlementVoiceServicePolicyMapPerCarrier =
+            new SparseArray<>();
     private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0);
     // key : subscriberId, value : provisioned or not.
     @GuardedBy("mSatelliteTokenProvisionedLock")
@@ -637,6 +674,15 @@
     private final Object mNtnSmsSupportedByMessagesAppLock = new Object();
     @GuardedBy("mNtnSmsSupportedByMessagesAppLock")
     private Boolean mNtnSmsSupportedByMessagesApp = null;
+
+    private final Object mSatelliteModemStateLock = new Object();
+    @GuardedBy("mSatelliteModemStateLock")
+    @SatelliteManager.SatelliteModemState
+    private int mSatelliteModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
+
+    // Data Plan types at entitlement for the plmn allowed
+    public static final int SATELLITE_DATA_PLAN_METERED = 0;
+    public static final int SATELLITE_DATA_PLAN_UNMETERED = 1;
     private BroadcastReceiver
             mDefaultSmsSubscriptionChangedBroadcastReceiver = new BroadcastReceiver() {
                 @Override
@@ -706,12 +752,13 @@
      *               (e.g., class name and method name)
      */
     public void incrementResultReceiverCount(String caller) {
-        if (mFeatureFlags.geofenceEnhancementForBetterUx()) {
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
             synchronized (mResultReceiverTotalCountLock) {
                 mResultReceiverTotalCount++;
                 logd("[incrementResultReceiverCount] : " + caller
                         + " | ResultReceiver total count= " + mResultReceiverTotalCount);
-                mResultReceiverCountPerMethodMap.compute(caller, (k, v) -> v == null ? 1 : v + 1);
+                mResultReceiverCountPerMethodMap.compute(caller,
+                        (k, v) -> v == null ? 1 : v + 1);
 
                 if (mResultReceiverTotalCount > RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD) {
                     loge("[mResultReceiverTotalCount] is exceeds limits : "
@@ -725,7 +772,7 @@
                 }
             }
         } else {
-            logd("[incrementResultReceiverCount]: geofenceEnhancementForBetterUx is not enabled");
+            logd("[incrementResultReceiverCount]: carrierRoamingNbIotNtn is not enabled");
         }
     }
 
@@ -737,7 +784,7 @@
      *               (e.g., class name and method name)
      */
     public void decrementResultReceiverCount(String caller) {
-        if (mFeatureFlags.geofenceEnhancementForBetterUx()) {
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
             synchronized (mResultReceiverTotalCountLock) {
                 if (mResultReceiverTotalCount > 0) {
                     mResultReceiverTotalCount--;
@@ -748,7 +795,7 @@
                         (k, v) -> v > 0 ? v - 1 : v);
             }
         } else {
-            logd("[decrementResultReceiverCount]: geofenceEnhancementForBetterUx is not enabled");
+            logd("[decrementResultReceiverCount]: carrierRoamingNbIotNtn is not enabled");
         }
     }
 
@@ -1084,15 +1131,13 @@
         public void onStateChanged(int state, int 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;
-                    evaluateToSendSatelliteEnabledSuccess();
-                } else {
-                    mUwbStateEnabled = true;
-                }
-                plogd("mUwbStateEnabled: " + mUwbStateEnabled);
+            if (state == UwbManager.AdapterStateCallback.STATE_DISABLED) {
+                setUwbEnabledState(false);
+                evaluateToSendSatelliteEnabledSuccess();
+            } else {
+                setUwbEnabledState(true);
             }
+            plogd("mUwbStateEnabled: " + getUwbEnabledState());
         }
     }
 
@@ -1109,50 +1154,47 @@
                 case BluetoothAdapter.ACTION_STATE_CHANGED:
                     int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                             BluetoothAdapter.ERROR);
-                    synchronized (mRadioStateLock) {
-                        boolean currentBTStateEnabled = mBTStateEnabled;
-                        if (btState == BluetoothAdapter.STATE_OFF) {
-                            mBTStateEnabled = false;
-                            evaluateToSendSatelliteEnabledSuccess();
-                        } else if (btState == BluetoothAdapter.STATE_ON) {
-                            mBTStateEnabled = true;
-                        }
-                        if (currentBTStateEnabled != mBTStateEnabled) {
-                            plogd("mBTStateEnabled=" + mBTStateEnabled);
-                        }
+                    boolean currentBTStateEnabled = getBTEnabledState();
+                    if (btState == BluetoothAdapter.STATE_OFF) {
+                        setBTEnabledState(false);
+                        evaluateToSendSatelliteEnabledSuccess();
+                    } else if (btState == BluetoothAdapter.STATE_ON) {
+                        setBTEnabledState(true);
+                    }
+
+                    if (currentBTStateEnabled != getBTEnabledState()) {
+                        plogd("mBTStateEnabled=" + getBTEnabledState());
                     }
                     break;
 
                 case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED:
                     int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, -1);
-                    synchronized (mRadioStateLock) {
-                        boolean currentNfcStateEnabled = mNfcStateEnabled;
-                        if (nfcState == NfcAdapter.STATE_ON) {
-                            mNfcStateEnabled = true;
-                        } else if (nfcState == NfcAdapter.STATE_OFF) {
-                            mNfcStateEnabled = false;
-                            evaluateToSendSatelliteEnabledSuccess();
-                        }
-                        if (currentNfcStateEnabled != mNfcStateEnabled) {
-                            plogd("mNfcStateEnabled=" + mNfcStateEnabled);
-                        }
+                    boolean currentNfcStateEnabled = getNfcEnabledState();
+                    if (nfcState == NfcAdapter.STATE_ON) {
+                        setNfcEnabledState(true);
+                    } else if (nfcState == NfcAdapter.STATE_OFF) {
+                        setNfcEnabledState(false);
+                        evaluateToSendSatelliteEnabledSuccess();
+                    }
+
+                    if (currentNfcStateEnabled != getNfcEnabledState()) {
+                        plogd("mNfcStateEnabled=" + getNfcEnabledState());
                     }
                     break;
 
                 case WifiManager.WIFI_STATE_CHANGED_ACTION:
                     int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                             WifiManager.WIFI_STATE_UNKNOWN);
-                    synchronized (mRadioStateLock) {
-                        boolean currentWifiStateEnabled = mWifiStateEnabled;
-                        if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
-                            mWifiStateEnabled = true;
-                        } else if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
-                            mWifiStateEnabled = false;
-                            evaluateToSendSatelliteEnabledSuccess();
-                        }
-                        if (currentWifiStateEnabled != mWifiStateEnabled) {
-                            plogd("mWifiStateEnabled=" + mWifiStateEnabled);
-                        }
+                    boolean currentWifiStateEnabled = getWifiEnabledState();
+                    if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
+                        setWifiEnabledState(true);
+                    } else if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
+                        setWifiEnabledState(false);
+                        evaluateToSendSatelliteEnabledSuccess();
+                    }
+
+                    if (currentWifiStateEnabled != getWifiEnabledState()) {
+                        plogd("mWifiStateEnabled=" + getWifiEnabledState());
                     }
                     break;
                 default:
@@ -1223,6 +1265,18 @@
         }
     }
 
+    private static final class UpdateSystemSelectionChannelsArgument {
+        @NonNull List<SystemSelectionSpecifier> mSystemSelectionSpecifiers;
+        @NonNull ResultReceiver mResult;
+
+        UpdateSystemSelectionChannelsArgument(
+                @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifiers,
+                @NonNull ResultReceiver result) {
+            this.mSystemSelectionSpecifiers = systemSelectionSpecifiers;
+            this.mResult = result;
+        }
+    }
+
     /**
      * Arguments to send to SatelliteTransmissionUpdate registrants
      */
@@ -1375,11 +1429,12 @@
                         if (mNeedsSatellitePointing) {
                             mPointingAppController.removeListenerForPointingUI();
                         }
+
+                        if (!isWaitingForSatelliteModemOff()) {
+                            moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_SUCCESS);
+                        }
+
                         synchronized (mSatelliteEnabledRequestLock) {
-                            if (!mWaitingForSatelliteModemOff) {
-                                moveSatelliteToOffStateAndCleanUpResources(
-                                        SATELLITE_RESULT_SUCCESS);
-                            }
                             mWaitingForDisableSatelliteModemResponse = false;
                         }
                     }
@@ -1612,13 +1667,14 @@
                         synchronized (mNeedsSatellitePointingLock) {
                             mNeedsSatellitePointing = capabilities.isPointingRequired();
                         }
+
                         synchronized (mSatelliteCapabilitiesLock) {
                             mSatelliteCapabilities = capabilities;
-                            overrideSatelliteCapabilitiesIfApplicable();
-                            if (DBG) plogd("getSatelliteCapabilities: " + mSatelliteCapabilities);
-                            bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
-                                    mSatelliteCapabilities);
                         }
+                        overrideSatelliteCapabilitiesIfApplicable();
+                        if (DBG) plogd("getSatelliteCapabilities: " + getSatelliteCapabilities());
+                        bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
+                                getSatelliteCapabilities());
                     }
                 }
                 ((ResultReceiver) request.argument).send(error, bundle);
@@ -1678,22 +1734,21 @@
 
                 if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
                     if (mSatelliteModemInterface.isSatelliteServiceConnected()) {
-                        synchronized (mIsSatelliteSupportedLock) {
-                            if (mIsSatelliteSupported == null || !mIsSatelliteSupported) {
-                                final String caller = "SC:CMD_IS_SATELLITE_SUPPORTED";
-                                ResultReceiver receiver = new ResultReceiver(this) {
-                                    @Override
-                                    protected void onReceiveResult(
-                                            int resultCode, Bundle resultData) {
-                                        decrementResultReceiverCount(caller);
-                                        plogd("onRadioStateChanged.requestIsSatelliteSupported: "
-                                                + "resultCode=" + resultCode
-                                                + ", resultData=" + resultData);
-                                    }
-                                };
-                                sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, receiver, null);
-                                incrementResultReceiverCount(caller);
-                            }
+                        Boolean isSatelliteSupported = getIsSatelliteSupported();
+                        if (isSatelliteSupported == null || !isSatelliteSupported) {
+                            final String caller = "SC:CMD_IS_SATELLITE_SUPPORTED";
+                            ResultReceiver receiver = new ResultReceiver(this) {
+                                @Override
+                                protected void onReceiveResult(
+                                        int resultCode, Bundle resultData) {
+                                    decrementResultReceiverCount(caller);
+                                    plogd("onRadioStateChanged.requestIsSatelliteSupported: "
+                                            + "resultCode=" + resultCode
+                                            + ", resultData=" + resultData);
+                                }
+                            };
+                            sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, receiver, null);
+                            incrementResultReceiverCount(caller);
                         }
                     }
                 }
@@ -2040,6 +2095,39 @@
                 int phoneId = (int) ar.userObj;
                 updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(
                         PhoneFactory.getPhone(phoneId));
+                break;
+            }
+
+            case CMD_UPDATE_SYSTEM_SELECTION_CHANNELS: {
+                plogd("CMD_UPDATE_SYSTEM_SELECTION_CHANNELS");
+                request = (SatelliteControllerHandlerRequest) msg.obj;
+                onCompleted = obtainMessage(EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE, request);
+                mSatelliteModemInterface.updateSystemSelectionChannels(
+                        ((UpdateSystemSelectionChannelsArgument) (request.argument))
+                                .mSystemSelectionSpecifiers,
+                        onCompleted);
+                break;
+            }
+
+            case EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE: {
+                ar = (AsyncResult) msg.obj;
+                request = (SatelliteControllerHandlerRequest) ar.userObj;
+                int error =  SatelliteServiceUtils.getSatelliteError(
+                        ar, "updateSystemSelectionChannel");
+                plogd("EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE = " + error);
+                ((UpdateSystemSelectionChannelsArgument) (request.argument)).mResult.send(error,
+                        null);
+                break;
+            }
+
+            case EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED: {
+                ar = (AsyncResult) msg.obj;
+                if (ar.result == null) {
+                    loge("EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED: result is null");
+                } else {
+                    handleEventSelectedNbIotSatelliteSubscriptionChanged((int) ar.result);
+                }
+                break;
             }
 
             default:
@@ -2156,31 +2244,29 @@
          *      SATELLITE_RESULT_ERROR error
          *      4. ongoing request = enable, current request = disable: send request to modem
          */
+        Boolean isSatelliteEnabled = getIsSatelliteEnabled();
         synchronized (mSatelliteEnabledRequestLock) {
             if (mFeatureFlags.carrierRoamingNbIotNtn()) {
                 if (mSatelliteEnabledRequest != null && mNetworkSelectionModeAutoDialog != null
                         && mNetworkSelectionModeAutoDialog.isShowing()
                         && request.isEmergency && request.enableSatellite) {
-                    synchronized (mSatellitePhoneLock) {
-                        sendErrorAndReportSessionMetrics(
-                                SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE,
-                                FunctionalUtils.ignoreRemoteException(
-                                        mSatelliteEnabledRequest.callback::accept));
-                    }
+                    sendErrorAndReportSessionMetrics(
+                            SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE,
+                            FunctionalUtils.ignoreRemoteException(
+                                    mSatelliteEnabledRequest.callback::accept));
                     mSatelliteEnabledRequest = null;
                     mNetworkSelectionModeAutoDialog.dismiss();
                     mNetworkSelectionModeAutoDialog = null;
                 }
             }
             if (!isSatelliteEnabledRequestInProgress()) {
-                synchronized (mIsSatelliteEnabledLock) {
-                    if (mIsSatelliteEnabled != null && mIsSatelliteEnabled == enableSatellite) {
-                        evaluateToUpdateSatelliteEnabledAttributes(result,
-                                SatelliteManager.SATELLITE_RESULT_SUCCESS, request,
-                                mIsDemoModeEnabled, mIsEmergency);
-                        return;
-                    }
+                if (isSatelliteEnabled != null && isSatelliteEnabled == enableSatellite) {
+                    evaluateToUpdateSatelliteEnabledAttributes(result,
+                            SatelliteManager.SATELLITE_RESULT_SUCCESS, request,
+                            mIsDemoModeEnabled, mIsEmergency);
+                    return;
                 }
+
                 if (enableSatellite) {
                     mSatelliteEnabledRequest = request;
                 } else {
@@ -2399,14 +2485,13 @@
             return;
         }
 
-        synchronized (mIsSatelliteEnabledLock) {
-            if (mIsSatelliteEnabled != null) {
-                /* We have already successfully queried the satellite modem. */
-                Bundle bundle = new Bundle();
-                bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, mIsSatelliteEnabled);
-                result.send(SATELLITE_RESULT_SUCCESS, bundle);
-                return;
-            }
+        Boolean isSatelliteEnabled = getIsSatelliteEnabled();
+        if (isSatelliteEnabled != null) {
+            /* We have already successfully queried the satellite modem. */
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, isSatelliteEnabled);
+            result.send(SATELLITE_RESULT_SUCCESS, bundle);
+            return;
         }
 
         sendRequestAsync(CMD_IS_SATELLITE_ENABLED, result, null);
@@ -2542,15 +2627,16 @@
             result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
             return;
         }
-        synchronized (mIsSatelliteSupportedLock) {
-            if (mIsSatelliteSupported != null) {
-                /* We have already successfully queried the satellite modem. */
-                Bundle bundle = new Bundle();
-                bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, mIsSatelliteSupported);
-                bundle.putInt(SATELLITE_SUBSCRIPTION_ID, getSelectedSatelliteSubId());
-                result.send(SATELLITE_RESULT_SUCCESS, bundle);
-                return;
-            }
+
+        int subId = getSelectedSatelliteSubId();
+        Boolean isSatelliteSupported = getIsSatelliteSupported();
+        if (isSatelliteSupported != null) {
+            /* We have already successfully queried the satellite modem. */
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, isSatelliteSupported);
+            bundle.putInt(SATELLITE_SUBSCRIPTION_ID, subId);
+            result.send(SATELLITE_RESULT_SUCCESS, bundle);
+            return;
         }
 
         sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, result, null);
@@ -2569,15 +2655,13 @@
             return;
         }
 
-        synchronized (mSatelliteCapabilitiesLock) {
-            if (mSatelliteCapabilities != null) {
-                Bundle bundle = new Bundle();
-                overrideSatelliteCapabilitiesIfApplicable();
-                bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
-                        mSatelliteCapabilities);
-                result.send(SATELLITE_RESULT_SUCCESS, bundle);
-                return;
-            }
+        if (getSatelliteCapabilities() != null) {
+            Bundle bundle = new Bundle();
+            overrideSatelliteCapabilitiesIfApplicable();
+            bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
+                    getSatelliteCapabilities());
+            result.send(SATELLITE_RESULT_SUCCESS, bundle);
+            return;
         }
 
         sendRequestAsync(CMD_GET_SATELLITE_CAPABILITIES, result, null);
@@ -3261,6 +3345,59 @@
     }
 
     /**
+     * Registers for selected satellite subscription changed event.
+     *
+     * @param callback The callback to handle the selected satellite subscription changed event.
+     *
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
+     */
+    @SatelliteManager.SatelliteResult
+    public int registerForSelectedNbIotSatelliteSubscriptionChanged(
+            @NonNull ISelectedNbIotSatelliteSubscriptionCallback callback) {
+        if (DBG) plogd("registerForSelectedNbIotSatelliteSubscriptionChanged()");
+
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("carrierRoamingNbIotNtn flag is disabled");
+            return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+        }
+
+        int error = evaluateOemSatelliteRequestAllowed(false);
+        if (error != SATELLITE_RESULT_SUCCESS) return error;
+
+        mSelectedNbIotSatelliteSubscriptionChangedListeners.put(callback.asBinder(), callback);
+        try {
+            callback.onSelectedNbIotSatelliteSubscriptionChanged(getSelectedSatelliteSubId());
+        } catch (RemoteException ex) {
+            ploge("registerForSelectedNbIotSatelliteSubscriptionChanged: RemoteException ex="
+                    + ex);
+        }
+        return SATELLITE_RESULT_SUCCESS;
+    }
+
+    /**
+     * Unregisters for the selected satellite subscription changed event.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to {@link
+     *     #registerForSelectedNbIotSatelliteSubscriptionChanged(
+     *     ISelectedNbIotSatelliteSubscriptionCallback)}.
+     */
+    public void unregisterForSelectedNbIotSatelliteSubscriptionChanged(
+            @NonNull ISelectedNbIotSatelliteSubscriptionCallback callback) {
+        if (DBG) plogd("unregisterForSelectedNbIotSatelliteSubscriptionChanged()");
+
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+
+        int error = evaluateOemSatelliteRequestAllowed(true);
+        if (error == SATELLITE_RESULT_SUCCESS) {
+            mSelectedNbIotSatelliteSubscriptionChangedListeners.remove(callback.asBinder());
+        }
+    }
+
+    /**
      * This API can be used by only CTS to update satellite vendor service package name.
      *
      * @param servicePackageName The package name of the satellite vendor service.
@@ -3698,6 +3835,14 @@
             return new ArrayList<>();
         }
         synchronized (mSupportedSatelliteServicesLock) {
+            Map<String, List<Integer>> allowedServicesList
+                    = mEntitlementServiceTypeMapPerCarrier.get(subId);
+            if (allowedServicesList != null && allowedServicesList.containsKey(plmn)) {
+                List<Integer> allowedServiceValues = allowedServicesList.get(plmn);
+                if (allowedServiceValues != null && !allowedServiceValues.isEmpty()) {
+                    return allowedServiceValues;
+                }
+            }
             if (mSatelliteServicesSupportedByCarriers.containsKey(subId)) {
                 Map<String, Set<Integer>> supportedServices =
                         mSatelliteServicesSupportedByCarriers.get(subId);
@@ -3742,17 +3887,16 @@
             return false;
         }
 
-        synchronized (mSatelliteCapabilitiesLock) {
-            if (mSatelliteCapabilities == null) {
-                ploge("isSatelliteAttachRequired: mSatelliteCapabilities is null");
-                return false;
-            }
-            if (mSatelliteCapabilities.getSupportedRadioTechnologies().contains(
-                    SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN)) {
-                return true;
-            }
+        SatelliteCapabilities satelliteCapabilities = getSatelliteCapabilities();
+        if (satelliteCapabilities == null) {
+            ploge("isSatelliteAttachRequired: mSatelliteCapabilities is null");
             return false;
         }
+        if (satelliteCapabilities.getSupportedRadioTechnologies().contains(
+                SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN)) {
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -3907,12 +4051,9 @@
      * esos session.
      */
     public boolean shouldTurnOffCarrierSatelliteForEmergencyCall() {
-        synchronized (mSatellitePhoneLock) {
-            if (mSatellitePhone == null) return false;
-            return !mDatagramController.isEmergencyCommunicationEstablished()
-                    && getConfigForSubId(mSatellitePhone.getSubId()).getBoolean(
-                    KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL);
-        }
+        return !mDatagramController.isEmergencyCommunicationEstablished()
+                && getConfigForSubId(getSelectedSatelliteSubId()).getBoolean(
+                KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL);
     }
 
     /**
@@ -4086,10 +4227,18 @@
      * @param entitlementEnabled {@code true} Satellite service enabled
      * @param allowedPlmnList    plmn allowed list to use the satellite service
      * @param barredPlmnList    plmn barred list to pass the modem
+     * @param plmnDataPlanMap   data plan map for the plmn
+     * @param plmnServiceTypeMap available services map for the plmn
+     * @param plmnDataServicePolicyMap data service policy map for the plmn
+     * @param plmnVoiceServicePolicyMap voice service policy map for the plmn
      * @param callback           callback for accept
      */
     public void onSatelliteEntitlementStatusUpdated(int subId, boolean entitlementEnabled,
             @Nullable List<String> allowedPlmnList, @Nullable List<String> barredPlmnList,
+            @Nullable Map<String,Integer> plmnDataPlanMap,
+            @Nullable Map<String,List<Integer>> plmnServiceTypeMap,
+            @Nullable Map<String,Integer> plmnDataServicePolicyMap,
+            @Nullable Map<String,Integer> plmnVoiceServicePolicyMap,
             @Nullable IIntegerConsumer callback) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             logd("onSatelliteEntitlementStatusUpdated: carrierEnabledSatelliteFlag is not enabled");
@@ -4110,10 +4259,26 @@
         if (barredPlmnList == null) {
             barredPlmnList = new ArrayList<>();
         }
+        if (plmnDataPlanMap == null) {
+            plmnDataPlanMap = new HashMap<>();
+        }
+        if (plmnServiceTypeMap == null) {
+            plmnServiceTypeMap = new HashMap<>();
+        }
+        if (plmnDataServicePolicyMap == null) {
+            plmnDataServicePolicyMap = new HashMap<>();
+        }
+        if (plmnVoiceServicePolicyMap == null) {
+            plmnVoiceServicePolicyMap = new HashMap<>();
+        }
         logd("onSatelliteEntitlementStatusUpdated subId=" + subId + ", entitlementEnabled="
                 + entitlementEnabled + ", allowedPlmnList=["
                 + String.join(",", allowedPlmnList) + "]" + ", barredPlmnList=["
-                + String.join(",", barredPlmnList) + "]");
+                + String.join(",", barredPlmnList) + "]"
+                + ", plmnDataPlanMap =" + plmnDataPlanMap.toString()
+                + ", plmnServiceTypeMap =" + plmnServiceTypeMap.toString()
+                + ", plmnDataServicePolicyMap=" + plmnDataServicePolicyMap.toString()
+                + ", plmnVoiceServicePolicyMap=" + plmnVoiceServicePolicyMap.toString());
 
         synchronized (mSupportedSatelliteServicesLock) {
             if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) {
@@ -4138,6 +4303,10 @@
                 mMergedPlmnListPerCarrier.remove(subId);
                 mEntitlementPlmnListPerCarrier.put(subId, allowedPlmnList);
                 mEntitlementBarredPlmnListPerCarrier.put(subId, barredPlmnList);
+                mEntitlementDataPlanMapPerCarrier.put(subId, plmnDataPlanMap);
+                mEntitlementServiceTypeMapPerCarrier.put(subId, plmnServiceTypeMap);
+                mEntitlementDataServicePolicyMapPerCarrier.put(subId, plmnDataServicePolicyMap);
+                mEntitlementVoiceServicePolicyMapPerCarrier.put(subId, plmnVoiceServicePolicyMap);
                 updatePlmnListPerCarrier(subId);
                 configureSatellitePlmnForCarrier(subId);
                 mSubscriptionManagerService.setSatelliteEntitlementPlmnList(subId, allowedPlmnList);
@@ -4175,12 +4344,12 @@
      * we will retry the query one more time. Otherwise, we will return the cached result.
      */
     private Boolean isSatelliteSupportedViaOemInternal() {
-        synchronized (mIsSatelliteSupportedLock) {
-            if (mIsSatelliteSupported != null) {
-                /* We have already successfully queried the satellite modem. */
-                return mIsSatelliteSupported;
-            }
+        Boolean isSatelliteSupported = getIsSatelliteSupported();
+        if (isSatelliteSupported != null) {
+            /* We have already successfully queried the satellite modem. */
+            return isSatelliteSupported;
         }
+
         /**
          * We have not successfully checked whether the modem supports satellite service.
          * Thus, we need to retry it now.
@@ -4665,28 +4834,33 @@
     private void handleEventSatelliteModemStateChanged(
             @SatelliteManager.SatelliteModemState int state) {
         plogd("handleEventSatelliteModemStateChanged: state=" + state);
+
+        synchronized (mSatelliteModemStateLock) {
+            mSatelliteModemState = state;
+        }
+
         if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
                 || state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+            if (!isWaitingForDisableSatelliteModemResponse()) {
+                moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_SUCCESS);
+            } else {
+                notifyModemStateChangedToSessionController(
+                        SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+            }
+
             synchronized (mSatelliteEnabledRequestLock) {
-                if (!mWaitingForDisableSatelliteModemResponse) {
-                    moveSatelliteToOffStateAndCleanUpResources(
-                            SATELLITE_RESULT_SUCCESS);
-                } else {
-                    notifyModemStateChangedToSessionController(
-                            SatelliteManager.SATELLITE_MODEM_STATE_OFF);
-                }
                 mWaitingForSatelliteModemOff = false;
             }
         } else {
             if (isSatelliteEnabledOrBeingEnabled() || isSatelliteBeingDisabled()) {
                 notifyModemStateChangedToSessionController(state);
             } else {
-                // Telephony framework and modem are out of sync. We need to disable modem
+                // Telephony framework and modem are out of sync. We need to disable
                 synchronized (mSatelliteEnabledRequestLock) {
                     plogw("Satellite modem is in a bad state. Disabling satellite modem now ...");
                     Consumer<Integer> result = integer -> plogd(
                             "handleEventSatelliteModemStateChanged: disabling satellite result="
-                            + integer);
+                                    + integer);
                     mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument(
                             false /* enableSatellite */, false /* enableDemoMode */,
                             false /* isEmergency */, result);
@@ -4741,13 +4915,14 @@
 
         synchronized (mSatelliteCapabilitiesLock) {
             mSatelliteCapabilities = capabilities;
-            overrideSatelliteCapabilitiesIfApplicable();
         }
+        overrideSatelliteCapabilitiesIfApplicable();
 
+        SatelliteCapabilities satelliteCapabilities = getSatelliteCapabilities();
         List<ISatelliteCapabilitiesCallback> deadCallersList = new ArrayList<>();
         mSatelliteCapabilitiesChangedListeners.values().forEach(listener -> {
             try {
-                listener.onSatelliteCapabilitiesChanged(this.mSatelliteCapabilities);
+                listener.onSatelliteCapabilitiesChanged(satelliteCapabilities);
             } catch (RemoteException e) {
                 plogd("handleEventSatelliteCapabilitiesChanged RemoteException: " + e);
                 deadCallersList.add(listener);
@@ -4761,39 +4936,63 @@
     private void handleEventSatelliteSupportedStateChanged(boolean supported) {
         plogd("handleSatelliteSupportedStateChangedEvent: supported=" + supported);
 
-        synchronized (mIsSatelliteSupportedLock) {
-            if (mIsSatelliteSupported != null && mIsSatelliteSupported == supported) {
-                if (DBG) {
-                    plogd("current satellite support state and new supported state are matched,"
-                            + " ignore update.");
-                }
-                return;
+        Boolean isSatelliteSupported = getIsSatelliteSupported();
+        if (isSatelliteSupported != null && isSatelliteSupported == supported) {
+            if (DBG) {
+                plogd("current satellite support state and new supported state are matched,"
+                        + " ignore update.");
             }
+            return;
+        }
 
-            updateSatelliteSupportedState(supported);
+        updateSatelliteSupportedState(supported);
 
-            /* In case satellite has been reported as not support from modem, but satellite is
+        Boolean isSatelliteEnabled = getIsSatelliteEnabled();
+         /* 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) {
-                    plogd("Invoke requestSatelliteEnabled(), supported=false, "
-                            + "mIsSatelliteEnabled=true");
-                    requestSatelliteEnabled(false /* enableSatellite */, false /* enableDemoMode */,
-                            false /* isEmergency */,
-                            new IIntegerConsumer.Stub() {
-                                @Override
-                                public void accept(int result) {
-                                    plogd("handleSatelliteSupportedStateChangedEvent: request "
-                                            + "satellite disable, result=" + result);
-                                }
-                            });
+        if (!supported && isSatelliteEnabled != null && isSatelliteEnabled) {
+            plogd("Invoke requestSatelliteEnabled(), supported=false, "
+                    + "mIsSatelliteEnabled=true");
+            requestSatelliteEnabled(false /* enableSatellite */, false /* enableDemoMode */,
+                    false /* isEmergency */,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            plogd("handleSatelliteSupportedStateChangedEvent: request "
+                                    + "satellite disable, result=" + result);
+                        }
+                    });
 
-                }
-            }
+        }
+
+        synchronized (mIsSatelliteSupportedLock) {
             mIsSatelliteSupported = supported;
         }
     }
 
+    private void handleEventSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("handleEventSelectedNbIotSatelliteSubscriptionChanged: "
+                    + "carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+
+        plogd("handleEventSelectedNbIotSatelliteSubscriptionChanged: " + selectedSubId);
+
+        List<ISelectedNbIotSatelliteSubscriptionCallback> deadCallersList = new ArrayList<>();
+        mSelectedNbIotSatelliteSubscriptionChangedListeners.values().forEach(listener -> {
+            try {
+                listener.onSelectedNbIotSatelliteSubscriptionChanged(selectedSubId);
+            } catch (RemoteException e) {
+                logd("handleEventSelectedNbIotSatelliteSubscriptionChanged RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSelectedNbIotSatelliteSubscriptionChangedListeners.remove(listener.asBinder());
+        });
+    }
+
     private void notifySatelliteSupportedStateChanged(boolean supported) {
         List<ISatelliteSupportedStateCallback> deadCallersList = new ArrayList<>();
         mSatelliteSupportedStateChangedListeners.values().forEach(listener -> {
@@ -4972,9 +5171,7 @@
                     sendRequestAsync(CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES,
                             mSatelliteEnableAttributesUpdateRequest, null);
                 }
-                synchronized (mSatellitePhoneLock) {
-                    updateLastNotifiedNtnModeAndNotify(mSatellitePhone);
-                }
+                updateLastNotifiedNtnModeAndNotify(getSatellitePhone());
             }
         }
     }
@@ -5005,26 +5202,24 @@
     public void moveSatelliteToOffStateAndCleanUpResources(
             @SatelliteManager.SatelliteResult int resultCode) {
         plogd("moveSatelliteToOffStateAndCleanUpResources");
+        setDemoModeEnabled(false);
+        handlePersistentLoggingOnSessionEnd(mIsEmergency);
+        setEmergencyMode(false);
         synchronized (mIsSatelliteEnabledLock) {
-            setDemoModeEnabled(false);
-            handlePersistentLoggingOnSessionEnd(mIsEmergency);
-            setEmergencyMode(false);
             mIsSatelliteEnabled = false;
-            setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
-            setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_FALSE);
-            abortSatelliteDisableRequest(resultCode);
-            abortSatelliteEnableRequest(resultCode);
-            abortSatelliteEnableAttributesUpdateRequest(resultCode);
-            resetSatelliteEnabledRequest();
-            resetSatelliteDisabledRequest();
-            // TODO (b/361139260): Stop timer to wait for other radios off
-            updateSatelliteEnabledState(
-                    false, "moveSatelliteToOffStateAndCleanUpResources");
         }
+        setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
+        setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_FALSE);
+        abortSatelliteDisableRequest(resultCode);
+        abortSatelliteEnableRequest(resultCode);
+        abortSatelliteEnableAttributesUpdateRequest(resultCode);
+        resetSatelliteEnabledRequest();
+        resetSatelliteDisabledRequest();
+        // TODO (b/361139260): Stop timer to wait for other radios off
+        updateSatelliteEnabledState(
+                false, "moveSatelliteToOffStateAndCleanUpResources");
         selectBindingSatelliteSubscription(false);
-        synchronized (mSatellitePhoneLock) {
-            updateLastNotifiedNtnModeAndNotify(mSatellitePhone);
-        }
+        updateLastNotifiedNtnModeAndNotify(getSatellitePhone());
     }
 
     private void setDemoModeEnabled(boolean enabled) {
@@ -5200,6 +5395,13 @@
                         KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE));
     }
 
+    @NonNull
+    private Map<String, Set<Integer>> readRegionalSatelliteEarfcnsFromCarrierConfig(int subId) {
+        PersistableBundle config = getPersistableBundle(subId);
+        return SatelliteServiceUtils.parseRegionalSatelliteEarfcns(
+                config.getPersistableBundle(KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE));
+    }
+
     @NonNull private PersistableBundle getConfigForSubId(int subId) {
         PersistableBundle config = null;
         if (mCarrierConfigManager != null) {
@@ -5207,6 +5409,7 @@
                 config = mCarrierConfigManager.getConfigForSubId(subId,
                         KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
                         KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                        KEY_SATELLITE_DISPLAY_NAME_STRING,
                         KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL,
                         KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT,
                         KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
@@ -5222,8 +5425,10 @@
                         KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
                         KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
                         KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
-                        KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE,
-                        KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY
+                        KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT,
+                        KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY,
+                        KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE,
+                        KEY_SATELLITE_DATA_SUPPORT_MODE_INT
                 );
             } catch (Exception e) {
                 logw("getConfigForSubId: " + e);
@@ -5254,6 +5459,7 @@
         evaluateCarrierRoamingNtnEligibilityChange();
         sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION),
                 mEvaluateEsosProfilesPrioritizationDurationMillis);
+        updateRegionalSatelliteEarfcns(subId);
     }
 
     // imsi, msisdn, default sms subId change
@@ -5341,7 +5547,7 @@
 
     /** If the provision state per subscriberId for the cached is not exist, check the database for
      * the corresponding value and use it. */
-    private void updateSatelliteProvisionedStatePerSubscriberId() {
+    protected void updateSatelliteProvisionedStatePerSubscriberId() {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
             return;
         }
@@ -5419,7 +5625,7 @@
                 KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL);
     }
 
-    private int getCarrierRoamingNtnConnectType(int subId) {
+    public int getCarrierRoamingNtnConnectType(int subId) {
         return getConfigForSubId(subId).getInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT);
     }
 
@@ -5428,6 +5634,11 @@
                 KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT);
     }
 
+    @CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE
+    private int getCarrierSatelliteDataSupportedMode(int subId) {
+        return getConfigForSubId(subId).getInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT);
+    }
+
     /**
      * Check if satellite attach is enabled by user for the carrier associated with the
      * {@code subId}.
@@ -5700,13 +5911,12 @@
      */
     @VisibleForTesting
     protected @SatelliteManager.NTRadioTechnology int getSupportedNtnRadioTechnology() {
-        synchronized (mSatelliteCapabilitiesLock) {
-            if (mSatelliteCapabilities != null) {
-                return mSatelliteCapabilities.getSupportedRadioTechnologies()
-                        .stream().findFirst().orElse(SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN);
-            }
-            return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN;
+        SatelliteCapabilities satelliteCapabilities = getSatelliteCapabilities();
+        if (satelliteCapabilities != null) {
+            return satelliteCapabilities.getSupportedRadioTechnologies()
+                    .stream().findFirst().orElse(SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN);
         }
+        return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN;
     }
 
     /**
@@ -5870,20 +6080,20 @@
             return;
         }
 
-        boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
+        boolean eligible = isCarrierRoamingNtnEligible(getSatellitePhone());
         plogd("evaluateCarrierRoamingNtnEligibilityChange: "
                 + "isCarrierRoamingNtnEligible=" + eligible);
 
-        synchronized (mSatellitePhoneLock) {
-            if (eligible) {
-                if (shouldStartNtnEligibilityHysteresisTimer(eligible)) {
-                    startNtnEligibilityHysteresisTimer();
-                }
-            } else {
-                mNtnEligibilityHysteresisTimedOut = false;
-                stopNtnEligibilityHysteresisTimer();
-                updateLastNotifiedNtnEligibilityAndNotify(false);
+        if (eligible) {
+            if (shouldStartNtnEligibilityHysteresisTimer(eligible)) {
+                startNtnEligibilityHysteresisTimer();
             }
+        } else {
+            synchronized (mSatellitePhoneLock) {
+                mNtnEligibilityHysteresisTimedOut = false;
+            }
+            stopNtnEligibilityHysteresisTimer();
+            updateLastNotifiedNtnEligibilityAndNotify(false);
         }
     }
 
@@ -5907,20 +6117,22 @@
     }
 
     private void startNtnEligibilityHysteresisTimer() {
-        synchronized (mSatellitePhoneLock) {
-            if (mSatellitePhone == null) {
-                ploge("startNtnEligibilityHysteresisTimer: mSatellitePhone is null.");
-                return;
-            }
-
-            int subId = getSelectedSatelliteSubId();
-            long timeout = getCarrierSupportedSatelliteNotificationHysteresisTimeMillis(subId);
-            mNtnEligibilityHysteresisTimedOut = false;
-            plogd("startNtnEligibilityHysteresisTimer: sendMessageDelayed subId=" + subId
-                    + ", phoneId=" + mSatellitePhone.getPhoneId() + ", timeout=" + timeout);
-            sendMessageDelayed(obtainMessage(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT),
-                    timeout);
+        Phone satellitePhone = getSatellitePhone();
+        if (satellitePhone == null) {
+            ploge("startNtnEligibilityHysteresisTimer: mSatellitePhone is null.");
+            return;
         }
+
+        int subId = getSelectedSatelliteSubId();
+        long timeout = getCarrierSupportedSatelliteNotificationHysteresisTimeMillis(subId);
+        synchronized (mSatellitePhoneLock) {
+            mNtnEligibilityHysteresisTimedOut = false;
+        }
+        plogd("startNtnEligibilityHysteresisTimer: sendMessageDelayed subId=" + subId
+                    + ", phoneId=" + satellitePhone.getPhoneId() + ", timeout=" + timeout);
+        sendMessageDelayed(obtainMessage(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT),
+                timeout);
+
     }
 
     private void stopNtnEligibilityHysteresisTimer() {
@@ -5935,24 +6147,26 @@
             return;
         }
 
-        if (mOverrideNtnEligibility != null) {
-            mSatellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility);
+        Phone satellitePhone = getSatellitePhone();
+        if (satellitePhone == null) {
+            ploge("notifyNtnEligibility: mSatellitePhone is null");
             return;
         }
 
-        synchronized (mSatellitePhoneLock) {
-            if (mSatellitePhone == null) {
-                ploge("notifyNtnEligibility: mSatellitePhone is null");
-                return;
-            }
+        if (mOverrideNtnEligibility != null) {
+            satellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility);
+            return;
+        }
 
-            plogd("notifyNtnEligibility: phoneId=" + mSatellitePhone.getPhoneId()
+        int selectedSatelliteSubId = getSelectedSatelliteSubId();
+        synchronized (mSatellitePhoneLock) {
+            plogd("notifyNtnEligibility: phoneId=" + satellitePhone.getPhoneId()
                     + " currentNtnEligibility=" + currentNtnEligibility);
             if (mLastNotifiedNtnEligibility == null
                     || mLastNotifiedNtnEligibility != currentNtnEligibility) {
                 mLastNotifiedNtnEligibility = currentNtnEligibility;
-                mSatellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility);
-                updateSatelliteSystemNotification(getSelectedSatelliteSubId(),
+                satellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility);
+                updateSatelliteSystemNotification(selectedSatelliteSubId,
                         CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL,
                         currentNtnEligibility);
             }
@@ -6180,50 +6394,51 @@
                 + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
 
         argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT);
-        synchronized (mIsSatelliteEnabledLock) {
-            if (argument.enableSatellite) {
-                resetSatelliteEnabledRequest();
-                abortSatelliteEnableAttributesUpdateRequest(SATELLITE_RESULT_REQUEST_ABORTED);
-                synchronized (mSatelliteEnabledRequestLock) {
-                    if (mSatelliteDisabledRequest == null) {
-                        IIntegerConsumer callback = new IIntegerConsumer.Stub() {
-                            @Override
-                            public void accept(int result) {
-                                plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
-                                        + "disable satellite result=" + result);
-                            }
-                        };
-                        Consumer<Integer> result =
-                                FunctionalUtils.ignoreRemoteException(callback::accept);
-                        mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument(
-                                false, false, false, result);
-                        sendRequestAsync(CMD_SET_SATELLITE_ENABLED, mSatelliteDisabledRequest,
-                                null);
+        if (argument.enableSatellite) {
+            resetSatelliteEnabledRequest();
+            abortSatelliteEnableAttributesUpdateRequest(SATELLITE_RESULT_REQUEST_ABORTED);
+            if (getSatelliteDisabledRequest() == null) {
+                IIntegerConsumer callback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
+                                + "disable satellite result=" + result);
                     }
+                };
+                Consumer<Integer> result =
+                        FunctionalUtils.ignoreRemoteException(callback::accept);
+
+                RequestSatelliteEnabledArgument request;
+                synchronized (mSatelliteEnabledRequestLock) {
+                    mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument(
+                            false, false, false, result);
+                    request = mSatelliteDisabledRequest;
                 }
 
-                mControllerMetricsStats.reportServiceEnablementFailCount();
-                mSessionMetricsStats.setInitializationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
-                        .setSatelliteTechnology(getSupportedNtnRadioTechnology())
-                        .setInitializationProcessingTime(
-                                System.currentTimeMillis() - mSessionProcessingTimeStamp)
-                        .setIsDemoMode(mIsDemoModeEnabled)
-                        .setCarrierId(getSatelliteCarrierId())
-                        .reportSessionMetrics();
-            } else {
-                resetSatelliteDisabledRequest();
-                mControllerMetricsStats.onSatelliteDisabled();
-                mSessionMetricsStats.setTerminationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
-                        .setSatelliteTechnology(getSupportedNtnRadioTechnology())
-                        .setTerminationProcessingTime(
-                                System.currentTimeMillis() - mSessionProcessingTimeStamp)
-                        .setSessionDurationSec(calculateSessionDurationTimeSec())
-                        .reportSessionMetrics();
+                sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
             }
-            notifyEnablementFailedToSatelliteSessionController(argument.enableSatellite);
-            mSessionStartTimeStamp = 0;
-            mSessionProcessingTimeStamp = 0;
+
+            mControllerMetricsStats.reportServiceEnablementFailCount();
+            mSessionMetricsStats.setInitializationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
+                    .setSatelliteTechnology(getSupportedNtnRadioTechnology())
+                    .setInitializationProcessingTime(
+                            System.currentTimeMillis() - mSessionProcessingTimeStamp)
+                    .setIsDemoMode(mIsDemoModeEnabled)
+                    .setCarrierId(getSatelliteCarrierId())
+                    .reportSessionMetrics();
+        } else {
+            resetSatelliteDisabledRequest();
+            mControllerMetricsStats.onSatelliteDisabled();
+            mSessionMetricsStats.setTerminationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
+                    .setSatelliteTechnology(getSupportedNtnRadioTechnology())
+                    .setTerminationProcessingTime(
+                            System.currentTimeMillis() - mSessionProcessingTimeStamp)
+                    .setSessionDurationSec(calculateSessionDurationTimeSec())
+                    .reportSessionMetrics();
         }
+        notifyEnablementFailedToSatelliteSessionController(argument.enableSatellite);
+        mSessionStartTimeStamp = 0;
+        mSessionProcessingTimeStamp = 0;
     }
 
     private void handleCmdUpdateNtnSignalStrengthReporting(boolean shouldReport) {
@@ -6328,6 +6543,23 @@
         updateSatelliteSystemNotification(-1, -1,/*visible*/ false);
     }
 
+    public boolean isSatelliteSystemNotificationsEnabled(int carrierRoamingNtnConnectType) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return false;
+        }
+        if (carrierRoamingNtnConnectType
+            != CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
+            return true;
+        }
+        boolean notifySatelliteAvailabilityEnabled =
+            mContext.getResources().getBoolean(R.bool.config_satellite_should_notify_availability);
+        Boolean isSatelliteSupported = getIsSatelliteSupported();
+        if(isSatelliteSupported == null) {
+            return false;
+        }
+        return notifySatelliteAvailabilityEnabled && isSatelliteSupported;
+    }
+
     /**
      * Update the system notification to reflect the current satellite status, that's either already
      * connected OR needs to be manually enabled. The device should only display one notification
@@ -6341,6 +6573,11 @@
      */
     private void updateSatelliteSystemNotification(int subId,
             @CARRIER_ROAMING_NTN_CONNECT_TYPE int carrierRoamingNtnConnectType, boolean visible) {
+        if (!isSatelliteSystemNotificationsEnabled(carrierRoamingNtnConnectType)) {
+            plogd("updateSatelliteSystemNotification: satellite notifications are not enabled.");
+            return;
+        }
+
         plogd("updateSatelliteSystemNotification subId=" + subId + ", carrierRoamingNtnConnectType="
                 + SatelliteServiceUtils.carrierRoamingNtnConnectTypeToString(
                 carrierRoamingNtnConnectType) + ", visible=" + visible);
@@ -6992,6 +7229,29 @@
     }
 
     /**
+     * Request to get the name to display for Satellite.
+     *
+     * @param result The result receiver that returns the name to display for the satellite
+     *               or an error code if the request failed.
+     */
+    public void requestSatelliteDisplayName(@NonNull ResultReceiver result) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("requestSatelliteDisplayName: carrierRoamingNbIotNtn flag is disabled");
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
+            return;
+        }
+
+        int subId = getSelectedSatelliteSubId();
+        String displayName = getConfigForSubId(subId).getString(
+                KEY_SATELLITE_DISPLAY_NAME_STRING, "Satellite");
+
+        plogd("requestSatelliteDisplayName: " + displayName);
+        Bundle bundle = new Bundle();
+        bundle.putString(SatelliteManager.KEY_SATELLITE_DISPLAY_NAME, displayName);
+        result.send(SATELLITE_RESULT_SUCCESS, bundle);
+    }
+
+    /**
      * Request to get list of prioritized satellite tokens to be used for provision.
      *
      * @param result The result receiver, which returns the list of prioritized satellite tokens
@@ -7055,7 +7315,7 @@
                             + ", provisioned=" + provisioned);
                     list.add(new SatelliteSubscriberProvisionStatus.Builder()
                             .setSatelliteSubscriberInfo(satelliteSubscriberInfo)
-                            .setProvisionStatus(provisioned).build());
+                            .setProvisioned(provisioned).build());
                     mSubscriberIdPerSub.put(subscriberId, info.getSubscriptionId());
                 }
             }
@@ -7111,22 +7371,23 @@
             // TODO: need to check if satellite is allowed at current location for the subscription
             int subId = getSubIdFromSubscriberId(
                     status.getSatelliteSubscriberInfo().getSubscriberId());
-            if (status.getProvisionStatus() && isActiveSubId(subId)) {
+            if (status.isProvisioned() && isActiveSubId(subId)) {
                 selectedSubId = subId;
                 break;
             }
         }
 
-        synchronized (mSatelliteTokenProvisionedLock) {
-            if (selectedSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
-                    && isSatelliteSupportedViaOem()) {
-                selectedSubId = getNtnOnlySubscriptionId();
-            }
-            mSelectedSatelliteSubId = selectedSubId;
-            setSatellitePhone(selectedSubId);
+        if (selectedSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                && isSatelliteSupportedViaOem()) {
+            selectedSubId = getNtnOnlySubscriptionId();
         }
-        plogd("selectBindingSatelliteSubscription: SelectedSatelliteSubId="
-                + mSelectedSatelliteSubId);
+
+        synchronized (mSatelliteTokenProvisionedLock) {
+            mSelectedSatelliteSubId = selectedSubId;
+        }
+        setSatellitePhone(selectedSubId);
+        plogd("selectBindingSatelliteSubscription: SelectedSatelliteSubId=" + selectedSubId);
+        handleEventSelectedNbIotSatelliteSubscriptionChanged(selectedSubId);
     }
 
     private int getSubIdFromSubscriberId(String subscriberId) {
@@ -7192,6 +7453,64 @@
     }
 
     /**
+     * Request to update system selection channels.
+     *
+     * @param result The result receiver that returns if the request is successful or
+     *               an error code if the request failed.
+     */
+    public void updateSystemSelectionChannels(
+            @NonNull List<SystemSelectionSpecifier> selectionSpecifiers,
+            @NonNull ResultReceiver result) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("updateSystemSelectionChannels: "
+                    + "carrierRoamingNbIotNtn flag is disabled");
+            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            return;
+        }
+
+        sendRequestAsync(CMD_UPDATE_SYSTEM_SELECTION_CHANNELS,
+                new UpdateSystemSelectionChannelsArgument(selectionSpecifiers, result), null);
+    }
+
+    /**
+     * @param subId Subscription ID.
+     * @return The The map of earfcns with key: regional satellite config Id,
+     * value: set of earfcns in the corresponding regions associated with the {@code subId}.
+     */
+    @NonNull
+    public Map<String, Set<Integer>> getRegionalSatelliteEarfcns(int subId) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("getRegionalSatelliteEarfcns: carrierRoamingNbIotNtnFlag is disabled");
+            return new HashMap<>();
+        }
+        synchronized (mRegionalSatelliteEarfcnsLock) {
+            if (mRegionalSatelliteEarfcns.containsKey(subId)) {
+                return mRegionalSatelliteEarfcns.get(subId);
+            } else {
+                logd("getRegionalSatelliteEarfcns: Earfcns for subId: " + subId + " not found");
+                return new HashMap<>();
+            }
+        }
+    }
+
+    /**
+     * Update regional satellite earfcn information from carrier config.
+     */
+    public void updateRegionalSatelliteEarfcns(int subId) {
+        plogd("updateRegionalSatelliteEarfcns with subId " + subId);
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("updateRegionalSatelliteEarfcns: "
+                    + "carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+
+        synchronized (mRegionalSatelliteEarfcnsLock) {
+            mRegionalSatelliteEarfcns.put(subId,
+                    readRegionalSatelliteEarfcnsFromCarrierConfig(subId));
+        }
+    }
+
+    /**
      * Deliver the list of deprovisioned satellite subscriber ids.
      *
      * @param list List of deprovisioned satellite subscriber ids.
@@ -7677,18 +7996,15 @@
      * uses the value in the existed mSatelliteCapabilities.
      */
     private void overrideSatelliteCapabilitiesIfApplicable() {
-        synchronized (this.mSatellitePhoneLock) {
-            if (this.mSatellitePhone == null) {
-                return;
-            }
-        }
         int subId = getSelectedSatelliteSubId();
         PersistableBundle config = getPersistableBundle(subId);
-        if (config.containsKey(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE)) {
-            int datagramSize = config.getInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE);
+        if (config.containsKey(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT)) {
+            int datagramSize = config.getInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT);
             SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId);
             if (!(subInfo == null || subInfo.isOnlyNonTerrestrialNetwork())) {
-                this.mSatelliteCapabilities.setMaxBytesPerOutgoingDatagram(datagramSize);
+                synchronized (mSatelliteCapabilitiesLock) {
+                    this.mSatelliteCapabilities.setMaxBytesPerOutgoingDatagram(datagramSize);
+                }
             }
         }
     }
@@ -7890,7 +8206,7 @@
                 NTN_SIGNAL_STRENGTH_NONE);
 
         if (isInCarrierRoamingNbIotNtn(phone)) {
-            if (mSatelliteSessionController.isInConnectedState()) {
+            if (isInConnectedState()) {
                 synchronized (mNtnSignalsStrengthLock) {
                     carrierRoamingNtnSignalStrength = mNtnSignalStrength;
                 }
@@ -7910,6 +8226,20 @@
         return carrierRoamingNtnSignalStrength;
     }
 
+    private boolean isInConnectedState() {
+        synchronized (mSatelliteModemStateLock) {
+            switch (mSatelliteModemState) {
+                case SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED:
+                case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
+                    plogd("isInConnectedState: return true");
+                    return true;
+                default:
+                    plogd("isInConnectedState: return false");
+                    return false;
+            }
+        }
+    }
+
     protected void updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(
             @Nullable Phone phone) {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) return;
@@ -7932,15 +8262,183 @@
 
     /** Returns whether to send SMS to DatagramDispatcher or not. */
     public boolean shouldSendSmsToDatagramDispatcher(@Nullable Phone phone) {
-        if (!isInCarrierRoamingNbIotNtn(phone)) {
-            return false;
-        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (!isInCarrierRoamingNbIotNtn(phone)) {
+                return false;
+            }
 
-        if (isDemoModeEnabled()) {
-            return false;
-        }
+            if (isDemoModeEnabled()) {
+                return false;
+            }
 
-        int[] services = getSupportedServicesOnCarrierRoamingNtn(phone.getSubId());
-        return ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS);
+            int[] services = getSupportedServicesOnCarrierRoamingNtn(phone.getSubId());
+            return ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private boolean isWaitingForSatelliteModemOff() {
+        synchronized (mSatelliteEnabledRequestLock) {
+            return mWaitingForSatelliteModemOff;
+        }
+    }
+
+    @Nullable
+    private Boolean getIsSatelliteSupported() {
+        synchronized (mIsSatelliteSupportedLock) {
+            return mIsSatelliteSupported;
+        }
+    }
+
+    private boolean isWaitingForDisableSatelliteModemResponse() {
+        synchronized (mSatelliteEnabledRequestLock) {
+            return mWaitingForDisableSatelliteModemResponse;
+        }
+    }
+
+    @Nullable
+    private Boolean getIsSatelliteEnabled() {
+        synchronized (mIsSatelliteEnabledLock) {
+            return mIsSatelliteEnabled;
+        }
+    }
+
+    @Nullable
+    private RequestSatelliteEnabledArgument getSatelliteDisabledRequest() {
+        synchronized (mSatelliteEnabledRequestLock) {
+            return mSatelliteDisabledRequest;
+        }
+    }
+
+    private SatelliteCapabilities getSatelliteCapabilities() {
+        synchronized (mSatelliteCapabilitiesLock) {
+            return mSatelliteCapabilities;
+        }
+    }
+
+    private void setBTEnabledState(boolean enabled) {
+        synchronized (mRadioStateLock) {
+            mBTStateEnabled = enabled;
+        }
+    }
+
+    private boolean getBTEnabledState() {
+        synchronized (mRadioStateLock) {
+            return mBTStateEnabled;
+        }
+    }
+
+    private void setNfcEnabledState(boolean enabled) {
+        synchronized (mRadioStateLock) {
+            mNfcStateEnabled = enabled;
+        }
+    }
+
+    private boolean getNfcEnabledState() {
+        synchronized (mRadioStateLock) {
+            return mNfcStateEnabled;
+        }
+    }
+
+    private void setUwbEnabledState(boolean enabled) {
+        synchronized (mRadioStateLock) {
+            mUwbStateEnabled = enabled;
+        }
+    }
+
+    private boolean getUwbEnabledState() {
+        synchronized (mRadioStateLock) {
+            return mUwbStateEnabled;
+        }
+    }
+
+    private void setWifiEnabledState(boolean enabled) {
+        synchronized (mRadioStateLock) {
+            mWifiStateEnabled = enabled;
+        }
+    }
+
+    private boolean getWifiEnabledState() {
+        synchronized (mRadioStateLock) {
+            return mWifiStateEnabled;
+        }
+    }
+
+    /**
+     * Method to return the current data plan for the registered plmn based on entitlement
+     * provisioning information. Note: If no information at
+     * provisioning is supported this is overridden with operator carrier config information.
+     *
+     * @param subId current subscription id
+     * @param plmn current registered plmn information
+     *
+     * @return Data supported modes {@link SatelliteController#SATELLITE_DATA_PLAN_METERED}
+     */
+    public int getSatelliteDataPlanForPlmn(int subId, String plmn) {
+        if (plmn != null) {
+            synchronized (mSupportedSatelliteServicesLock) {
+                Map<String, Integer> dataplanMap = mEntitlementDataPlanMapPerCarrier.get(subId);
+                logd("data plan available for sub id:" + dataplanMap);
+                if (dataplanMap != null && dataplanMap.containsKey(plmn)) {
+                    return dataplanMap.get(plmn);
+                }
+            }
+        }
+        // TODO (Override with carrier config value when configuration defined)
+        return SATELLITE_DATA_PLAN_METERED;
+    }
+
+    /**
+     * Method to return the current satellite data service policy supported mode for the registered
+     * plmn based on entitlement provisioning information. Note: If no information at
+     * provisioning is supported this is overridden with operator carrier config information.
+     *
+     * @param subId current subscription id
+     * @param plmn current registered plmn information
+     *
+     * @return Supported modes {@link CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE}
+     */
+    public int getSatelliteDataServicePolicyForPlmn(int subId, String plmn) {
+        if (plmn != null) {
+            synchronized (mSupportedSatelliteServicesLock) {
+                Map<String, Integer> dataServicePolicy =
+                        mEntitlementDataServicePolicyMapPerCarrier.get(
+                        subId);
+                logd("data policy available for sub id:" + dataServicePolicy);
+                if (dataServicePolicy != null && dataServicePolicy.containsKey(plmn)) {
+                    return dataServicePolicy.get(plmn);
+                }
+            }
+        }
+        return getCarrierSatelliteDataSupportedMode(subId);
+    }
+
+    /**
+     * Method to return the current satellite voice service policy supported mode for the registered
+     * plmn based on entitlement provisioning information. Note: If no information at
+     * provisioning is supported this is overridden with operator carrier config information.
+     *
+     * @param subId current subscription id
+     * @param plmn current registered plmn information
+     *
+     * @return Supported modes {@link CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE}
+     */
+    public int getSatelliteVoiceServicePolicyForPlmn(int subId, String plmn) {
+        if (plmn != null) {
+            synchronized (mSupportedSatelliteServicesLock) {
+                Map<String, Integer> voiceServicePolicy =
+                        mEntitlementVoiceServicePolicyMapPerCarrier.get(
+                                subId);
+                logd("voice policy available for sub id:" + voiceServicePolicy);
+                if (voiceServicePolicy != null && voiceServicePolicy.containsKey(plmn)) {
+                    return voiceServicePolicy.get(plmn);
+                }
+            }
+        }
+        // TODO (Replace below code with related enum value, when voice service policy support mode
+        // is added)
+        return 0; // Restricted
     }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 5fa85db..5b032e6 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -41,6 +41,7 @@
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteManager.SatelliteException;
 import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
+import android.telephony.satellite.SystemSelectionSpecifier;
 import android.telephony.satellite.stub.INtnSignalStrengthConsumer;
 import android.telephony.satellite.stub.ISatellite;
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
@@ -1383,6 +1384,42 @@
         mExponentialBackoff.start();
     }
 
+    /**
+     * Request to update system selection channels
+     *
+     * @param systemSelectionSpecifiers system selection specifiers
+     * @param message The Message to send to result of the operation to.
+     */
+    public void updateSystemSelectionChannels(
+            @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifiers,
+            @Nullable Message message) {
+        plogd("updateSystemSelectionChannels: SystemSelectionSpecifier: "
+                + systemSelectionSpecifiers.toString());
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.updateSystemSelectionChannels(SatelliteServiceUtils
+                                .toSystemSelectionSpecifier(systemSelectionSpecifiers),
+                        new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                int error = SatelliteServiceUtils.fromSatelliteError(result);
+                                plogd("updateSystemSelectionChannels: " + error);
+                                Binder.withCleanCallingIdentity(() ->
+                                        sendMessageWithResult(message, null, error));
+                            }
+                        });
+            } catch (RemoteException e) {
+                ploge("updateSystemSelectionChannels: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            ploge("updateSystemSelectionChannels: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected static void sendMessageWithResult(@NonNull Message message, @Nullable Object result,
             @SatelliteManager.SatelliteResult int error) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java b/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java
index 7db9195..f101f18 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.satellite;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Data class of the satellite configuration received from the entitlement server.
  */
@@ -28,9 +31,21 @@
      * 2. "metered"
      * 3. empty string. */
     public String mDataPlanType;
+    /** Stored the Allowed Services Info. with key as service type and value as service
+     *  policy for the plmn
+     *  Possible Service Type values: "data" and "voice".
+     *  Possible Service Policy values: "constrained" and "unconstrained".
+     */
+    public Map<String,String> mAllowedServicesInfo;
 
-    public SatelliteNetworkInfo(String plmn, String dataPlanType) {
+    public SatelliteNetworkInfo(String plmn, String dataPlanType,
+            Map<String,String> allowedServicesInfo) {
         mPlmn = plmn;
         mDataPlanType = dataPlanType;
+        if (allowedServicesInfo != null) {
+            mAllowedServicesInfo = new HashMap<>(allowedServicesInfo);
+        } else {
+            mAllowedServicesInfo = new HashMap<>();
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index 182f667..e924878 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -24,6 +24,9 @@
 import static android.telephony.TelephonyManager.EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT;
 import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
 import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP;
 
 import static com.android.internal.telephony.flags.Flags.satellitePersistentLogging;
 import static com.android.internal.telephony.satellite.SatelliteController.INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
@@ -73,6 +76,7 @@
 import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.metrics.SatelliteStats;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -262,6 +266,27 @@
         return SmsApplication.getDefaultSendToApplication(mContext, false);
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean updateAndGetProvisionState() {
+        mSatelliteController.updateSatelliteProvisionedStatePerSubscriberId();
+        return isDeviceProvisioned();
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean isSatelliteAllowedByReasons() {
+        SatelliteManager satelliteManager = mContext.getSystemService(SatelliteManager.class);
+        int[] disallowedReasons = satelliteManager.getSatelliteDisallowedReasons();
+        if (Arrays.stream(disallowedReasons).anyMatch(r ->
+                (r == SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP
+                        || r == SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED
+                        || r == SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED))) {
+            plogd("isAllowedForDefaultMessageApp:false, disallowedReasons="
+                    + Arrays.toString(disallowedReasons));
+            return false;
+        }
+        return true;
+    }
+
     private void handleEmergencyCallStartedEvent(@NonNull Connection connection) {
         plogd("handleEmergencyCallStartedEvent: connection=" + connection);
         mSatelliteController.setLastEmergencyCallTime();
@@ -284,7 +309,8 @@
     }
 
     private void handleSatelliteProvisionStateChangedEvent(boolean provisioned) {
-        if (!provisioned) {
+        if (!provisioned
+                && !isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             cleanUpResources(false);
         }
     }
@@ -310,6 +336,8 @@
                 return;
             }
 
+            updateAndGetProvisionState();
+
             /*
              * The device might be connected to satellite after the emergency call started. Thus, we
              * need to do this check again so that we will have higher chance of sending the event
@@ -321,7 +349,7 @@
             boolean isCellularAvailable = SatelliteServiceUtils.isCellularAvailable();
             if (!isCellularAvailable
                     && isSatelliteAllowed()
-                    && (isDeviceProvisioned()
+                    && ((isDeviceProvisioned() && isSatelliteAllowedByReasons())
                     || isSatelliteConnectedViaCarrierWithinHysteresisTime())
                     && shouldTrackCall(mEmergencyConnection.getState())) {
                 plogd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer");
@@ -645,7 +673,7 @@
 
     @NonNull private Bundle createExtraBundleForEventDisplayEmergencyMessage(
             boolean isTestEmergencyNumber) {
-        int handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
+        int handoverType = getEmergencyCallToSatelliteHandoverType();
         Pair<String, String> oemSatelliteMessagingApp =
                 getOemEnabledSatelliteHandoverAppFromOverlayConfig(mContext);
         String packageName = oemSatelliteMessagingApp.first;
@@ -653,10 +681,8 @@
         String action = getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(mContext,
                 isTestEmergencyNumber);
 
-        if (isSatelliteConnectedViaCarrierWithinHysteresisTime()
-                || isEmergencyCallToSatelliteHandoverTypeT911Enforced()) {
+        if (handoverType == EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911) {
             ComponentName defaultSmsAppComponent = getDefaultSmsApp();
-            handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
             packageName = defaultSmsAppComponent.getPackageName();
             className = defaultSmsAppComponent.getClassName();
         }
@@ -737,7 +763,9 @@
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public int getEmergencyCallToSatelliteHandoverType() {
-        if (Flags.carrierRoamingNbIotNtn() && isDeviceProvisioned()
+        if (Flags.carrierRoamingNbIotNtn()
+                && isDeviceProvisioned()
+                && isSatelliteAllowedByReasons()
                 && isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             int satelliteSubId = mSatelliteController.getSelectedSatelliteSubId();
             return mSatelliteController.getCarrierRoamingNtnEmergencyCallToSatelliteHandoverType(
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index 3936a7e..b55c622 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -36,13 +36,16 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.AntennaPosition;
+import android.telephony.satellite.EarfcnRange;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteInfo;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
 import android.telephony.satellite.SatelliteSubscriptionInfo;
+import android.telephony.satellite.SystemSelectionSpecifier;
 import android.telephony.satellite.stub.NTRadioTechnology;
 import android.telephony.satellite.stub.SatelliteModemState;
 import android.telephony.satellite.stub.SatelliteResult;
@@ -542,6 +545,100 @@
         return mcc + mnc;
     }
 
+    @NonNull
+    private static android.telephony.satellite.stub
+            .SystemSelectionSpecifier convertSystemSelectionSpecifierToHALFormat(
+            @NonNull SystemSelectionSpecifier systemSelectionSpecifier) {
+        android.telephony.satellite.stub.SystemSelectionSpecifier convertedSpecifier =
+                new android.telephony.satellite.stub.SystemSelectionSpecifier();
+
+        convertedSpecifier.mMccMnc = systemSelectionSpecifier.getMccMnc();
+        convertedSpecifier.mBands = systemSelectionSpecifier.getBands();
+        convertedSpecifier.mEarfcs = systemSelectionSpecifier.getEarfcns();
+        SatelliteInfo[] satelliteInfos = systemSelectionSpecifier.getSatelliteInfos()
+                .toArray(new SatelliteInfo[0]);
+        android.telephony.satellite.stub.SatelliteInfo[] halSatelliteInfos =
+                new android.telephony.satellite.stub.SatelliteInfo[satelliteInfos.length];
+        for (int i = 0; i < satelliteInfos.length; i++) {
+            halSatelliteInfos[i] = new android.telephony.satellite.stub.SatelliteInfo();
+
+            halSatelliteInfos[i].id = new android.telephony.satellite.stub.UUID();
+            halSatelliteInfos[i].id.mostSigBits =
+                    satelliteInfos[i].getSatelliteId().getMostSignificantBits();
+            halSatelliteInfos[i].id.leastSigBits =
+                    satelliteInfos[i].getSatelliteId().getLeastSignificantBits();
+
+            halSatelliteInfos[i].position =
+                    new android.telephony.satellite.stub.SatellitePosition();
+            halSatelliteInfos[i].position.longitudeDegree =
+                    satelliteInfos[i].getSatellitePosition().getLongitudeDegrees();
+            halSatelliteInfos[i].position.altitudeKm =
+                    satelliteInfos[i].getSatellitePosition().getAltitudeKm();
+
+            halSatelliteInfos[i].bands = satelliteInfos[i].getBands().stream().mapToInt(
+                    Integer::intValue).toArray();
+
+            List<EarfcnRange> earfcnRangeList = satelliteInfos[i].getEarfcnRanges();
+            halSatelliteInfos[i].earfcnRanges =
+                    new android.telephony.satellite.stub.EarfcnRange[earfcnRangeList.size()];
+            for (int j = 0; j < earfcnRangeList.size(); j++) {
+                halSatelliteInfos[i].earfcnRanges[j] =
+                        new android.telephony.satellite.stub.EarfcnRange();
+                halSatelliteInfos[i].earfcnRanges[j].startEarfcn = earfcnRangeList.get(
+                        j).getStartEarfcn();
+                halSatelliteInfos[i].earfcnRanges[j].endEarfcn = earfcnRangeList.get(
+                        j).getEndEarfcn();
+            }
+        }
+        convertedSpecifier.satelliteInfos = halSatelliteInfos;
+        convertedSpecifier.tagIds = systemSelectionSpecifier.getTagIds();
+        return convertedSpecifier;
+    }
+
+    /**
+     * Convert SystemSelectionSpecifier from framework definition to service definition
+     * @param systemSelectionSpecifier The SystemSelectionSpecifier from the framework.
+     * @return The converted SystemSelectionSpecifier for the satellite service.
+     */
+    @NonNull
+    public static List<android.telephony.satellite.stub
+            .SystemSelectionSpecifier> toSystemSelectionSpecifier(
+            @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifier) {
+        return systemSelectionSpecifier.stream().map(
+                SatelliteServiceUtils::convertSystemSelectionSpecifierToHALFormat).collect(
+                Collectors.toList());
+    }
+
+    /**
+     * Expected format of the input dictionary bundle is:
+     * <ul>
+     *     <li>Key: Regional satellite config Id string.</li>
+     *     <li>Value: Integer arrays of earfcns in the corresponding regions."</li>
+     * </ul>
+     * @return The map of earfcns with key: regional satellite config Id,
+     * value: set of earfcns in the corresponding regions.
+     */
+    @NonNull
+    public static Map<String, Set<Integer>> parseRegionalSatelliteEarfcns(
+            @Nullable PersistableBundle earfcnsBundle) {
+        Map<String, Set<Integer>> earfcnsMap = new HashMap<>();
+        if (earfcnsBundle == null || earfcnsBundle.isEmpty()) {
+            logd("parseRegionalSatelliteEarfcns: earfcnsBundle is null or empty");
+            return earfcnsMap;
+        }
+
+        for (String configId : earfcnsBundle.keySet()) {
+            Set<Integer> earfcnsSet = new HashSet<>();
+            for (int earfcn : earfcnsBundle.getIntArray(configId)) {
+                earfcnsSet.add(earfcn);
+            }
+            logd("parseRegionalSatelliteEarfcns: configId = " + configId + ", earfcns ="
+                    + earfcnsSet.stream().map(String::valueOf).collect(joining(",")));
+            earfcnsMap.put(configId, earfcnsSet);
+        }
+        return earfcnsMap;
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index d1d03a0..f2f4cac 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -68,7 +68,6 @@
 import android.util.Log;
 
 import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.DeviceStateMonitor;
 import com.android.internal.telephony.ExponentialBackoff;
@@ -586,17 +585,6 @@
     }
 
     /**
-     * Get whether state machine is in connected state.
-     *
-     * @return {@code true} if state machine is in connected state and {@code false} otherwise.
-     */
-    public boolean isInConnectedState() {
-        if (DBG) plogd("isInConnectedState: getCurrentState=" + getCurrentState());
-        return getCurrentState() == mConnectedState;
-    }
-
-
-    /**
      * Release all resource.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -886,10 +874,19 @@
             stopNbIotInactivityTimer();
 
             //Enable Cellular Modem scanning
-            Message onCompleted =
+            boolean configSatelliteAllowTnScanningDuringSatelliteSession =
+                    mContext.getResources().getBoolean(
+                        R.bool.config_satellite_allow_tn_scanning_during_satellite_session);
+            if (configSatelliteAllowTnScanningDuringSatelliteSession) {
+                Message onCompleted =
                     obtainMessage(EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE);
-            mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, onCompleted);
-            if (isConcurrentTnScanningSupported()) {
+                mSatelliteModemInterface
+                    .enableCellularModemWhileSatelliteModeIsOn(true, onCompleted);
+            } else {
+                plogd("Device does not allow cellular modem scanning");
+            }
+            if (isConcurrentTnScanningSupported()
+                    || !configSatelliteAllowTnScanningDuringSatelliteSession) {
                 plogd("IDLE state is hidden from clients");
             } else {
                 notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
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 2ae8f9d..0a82b99 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -18,6 +18,7 @@
 
 import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.satellite.SatelliteManager.KEY_SESSION_STATS_V2;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.NonNull;
@@ -61,9 +62,11 @@
     private int mCountOfSatelliteNotificationDisplayed;
     private int mCountOfAutoExitDueToScreenOff;
     private int mCountOfAutoExitDueToTnNetwork;
+    private SatelliteSessionStats datagramStats;
 
     private SessionMetricsStats() {
         initializeSessionMetricsParam();
+        datagramStats = new SatelliteSessionStats();
     }
 
     /**
@@ -128,7 +131,9 @@
 
     /** Increase the count of successful outgoing datagram transmission. */
     public SessionMetricsStats addCountOfSuccessfulOutgoingDatagram(
-            @NonNull @SatelliteManager.DatagramType int datagramType) {
+            @NonNull @SatelliteManager.DatagramType int datagramType,
+            long datagramTransmissionTime) {
+        datagramStats.recordSuccessfulOutgoingDatagramStats(datagramType, datagramTransmissionTime);
         if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
             // Ignore KEEP_ALIVE messages
             return this;
@@ -145,6 +150,7 @@
     public SessionMetricsStats addCountOfFailedOutgoingDatagram(
             @NonNull @SatelliteManager.DatagramType int datagramType,
             @NonNull @SatelliteManager.SatelliteResult int resultCode) {
+        datagramStats.addCountOfUnsuccessfulUserMessages(datagramType, resultCode);
         if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
             // Ignore KEEP_ALIVE messages
             return this;
@@ -284,6 +290,7 @@
 
     /** Returns {@link SatelliteSessionStats} of the satellite service. */
     public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) {
+        Log.i(TAG, "requestSatelliteSessionStats called");
         Bundle bundle = new Bundle();
         SatelliteSessionStats sessionStats = new SatelliteSessionStats.Builder()
                 .setCountOfSuccessfulUserMessages(mShadowCountOfSuccessfulOutgoingDatagram)
@@ -296,6 +303,10 @@
                         DatagramDispatcher.getInstance().getPendingUserMessagesCount())
                 .build();
         bundle.putParcelable(SatelliteManager.KEY_SESSION_STATS, sessionStats);
+
+        // TODO b/381007377 should retrieve MessagesInQueueToBeSent count per messageType and add
+        //  to datagramStats
+        bundle.putParcelable(KEY_SESSION_STATS_V2, datagramStats);
         result.send(SATELLITE_RESULT_SUCCESS, bundle);
     }
 
@@ -310,9 +321,9 @@
     }
 
     private void initializeSessionMetricsParam() {
-        mInitializationResult = SatelliteManager.SATELLITE_RESULT_SUCCESS;
+        mInitializationResult = SATELLITE_RESULT_SUCCESS;
         mRadioTechnology = SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN;
-        mTerminationResult = SatelliteManager.SATELLITE_RESULT_SUCCESS;
+        mTerminationResult = SATELLITE_RESULT_SUCCESS;
         mInitializationProcessingTimeMillis = 0;
         mTerminationProcessingTimeMillis = 0;
         mSessionDurationSec = 0;
@@ -336,6 +347,7 @@
         mShadowCountOfFailedOutgoingDatagram = 0;
         mShadowCountOfTimedOutUserMessagesWaitingForConnection = 0;
         mShadowCountOfTimedOutUserMessagesWaitingForAck = 0;
+        datagramStats.clear();
     }
 
     private static void logd(@NonNull String log) {
diff --git a/src/java/com/android/internal/telephony/uicc/SimTypeInfo.java b/src/java/com/android/internal/telephony/uicc/SimTypeInfo.java
new file mode 100644
index 0000000..b19e42a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/SimTypeInfo.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class contains the sim type information of active physical slot ids.
+ */
+public class SimTypeInfo {
+
+    /**
+     * SimType (bit mask)
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            SimType.SIM_TYPE_UNKNOWN,
+            SimType.SIM_TYPE_PHYSICAL,
+            SimType.SIM_TYPE_ESIM,
+    })
+    public @interface SimType {
+        /** Unknown SIM */
+        int SIM_TYPE_UNKNOWN = 0;
+        /** Physical SIM (Can have eUICC capabilities) */
+        int SIM_TYPE_PHYSICAL = 1 << 0;
+        /** Embedded SIM*/
+        int SIM_TYPE_ESIM = 1 << 1;
+    }
+
+    public @SimType int mCurrentSimType = SimType.SIM_TYPE_UNKNOWN;
+    // Bitmask of the supported {@code SimType}s
+    public int mSupportedSimTypes;
+
+    /**
+     * Set the current SimType according to the input type.
+     */
+    public void setCurrentSimType(int simType) {
+        switch(simType) {
+            case android.hardware.radio.config.SimType.UNKNOWN:
+                mCurrentSimType = SimType.SIM_TYPE_UNKNOWN;
+                break;
+            case android.hardware.radio.config.SimType.PHYSICAL:
+                mCurrentSimType = SimType.SIM_TYPE_PHYSICAL;
+                break;
+            case android.hardware.radio.config.SimType.ESIM:
+                mCurrentSimType = SimType.SIM_TYPE_ESIM;
+                break;
+            default:
+                throw new RuntimeException("Unrecognized RIL_SimType: " + simType);
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SimTypeInfo {activeSimType=").append(mCurrentSimType).append(",")
+                .append("supportedSimType=").append(mSupportedSimTypes);
+        sb.append("}");
+        return sb.toString();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index f92643a..a7923cf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -1044,7 +1044,8 @@
             doReturn(new TelephonyDisplayInfo(
                     mNetworkTypeController.getDataNetworkType(),
                     mNetworkTypeController.getOverrideNetworkType(),
-                    false)).when(mDisplayInfoController).getTelephonyDisplayInfo();
+                    false, false, false))
+                    .when(mDisplayInfoController).getTelephonyDisplayInfo();
             return null;
         }).when(mDisplayInfoController).updateTelephonyDisplayInfo();
         mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index 4c42e2e..05107df 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -173,6 +173,7 @@
         rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA, false));
         rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, false));
         rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_NR, false));
+        rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_NB_IOT_NTN, false));
 
         for (Pair<Integer, Boolean> rat : rats) {
             boolean isCdma = rat.second;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 5dc4719..e5aa541 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -56,6 +56,7 @@
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation;
 import android.telephony.BarringInfo;
+import android.telephony.CallState;
 import android.telephony.CellIdentity;
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
@@ -138,9 +139,10 @@
     private long mCallbackModeDurationMillis;
     private boolean mCarrierRoamingNtnMode;
     private boolean mCarrierRoamingNtnEligible;
-    private List<Integer> mCarrierRoamingNtnAvailableServices;
+    private int[] mCarrierRoamingNtnAvailableServices;
     private NtnSignalStrength mCarrierRoamingNtnSignalStrength;
     private boolean mIsSatelliteEnabled;
+    private final List<List<CallState>> mCallStateList = new ArrayList<>();
 
     // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
     private static final Set<Integer> READ_PHONE_STATE_EVENTS;
@@ -224,7 +226,8 @@
             TelephonyCallback.EmergencyCallbackModeListener,
             TelephonyCallback.CarrierRoamingNtnModeListener,
             TelephonyCallback.SecurityAlgorithmsListener,
-            TelephonyCallback.CellularIdentifierDisclosedListener {
+            TelephonyCallback.CellularIdentifierDisclosedListener,
+            TelephonyCallback.CallAttributesListener {
         // This class isn't mockable to get invocation counts because the IBinder is null and
         // crashes the TelephonyRegistry. Make a cheesy verify(times()) alternative.
         public AtomicInteger invocationCount = new AtomicInteger(0);
@@ -354,7 +357,7 @@
         }
 
         @Override
-        public void onCarrierRoamingNtnAvailableServicesChanged(List<Integer> services) {
+        public void onCarrierRoamingNtnAvailableServicesChanged(int[] services) {
             invocationCount.incrementAndGet();
             mCarrierRoamingNtnAvailableServices = services;
         }
@@ -374,6 +377,12 @@
         public void onCellularIdentifierDisclosedChanged(CellularIdentifierDisclosure disclosure) {
             invocationCount.incrementAndGet();
         }
+
+        @Override
+        public void onCallStatesChanged(List<CallState> callStateList) {
+            invocationCount.incrementAndGet();
+            mCallStateList.add(callStateList);
+        }
     }
 
     public class MySatelliteStateChangeListener implements ISatelliteStateChangeListener {
@@ -448,6 +457,7 @@
             mPhysicalChannelConfigs = null;
         }
         mCellLocation = null;
+        mCallStateList.clear();
         super.tearDown();
     }
 
@@ -1344,7 +1354,7 @@
         TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_LTE,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
-                false);
+                false, false, false);
 
         // Notify with invalid subId on default phone. Should NOT trigger callback.
         mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo);
@@ -1371,11 +1381,11 @@
         TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_LTE,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
-                false);
+                false, false, false);
         TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_LTE,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
-                false);
+                false, false, false);
 
         // Notify with invalid subId on default phone. Should NOT trigger callback.
         mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo);
@@ -1398,11 +1408,11 @@
         TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_LTE,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
-                false);
+                false, false, false);
         TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_LTE,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
-                false);
+                false, false, false);
         TelephonyCallback telephonyCallback2 = new TelephonyCallbackWrapper() {
             @Override
             public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfoNotify) {
@@ -1768,9 +1778,38 @@
         int[] services = {3, 6};
         mTelephonyRegistry.notifyCarrierRoamingNtnAvailableServicesChanged(subId, services);
         processAllMessages();
-        int[] carrierServices = mCarrierRoamingNtnAvailableServices.stream()
-                .mapToInt(Integer::intValue).toArray();
-        assertTrue(Arrays.equals(carrierServices, services));
+        assertTrue(Arrays.equals(mCarrierRoamingNtnAvailableServices, services));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_PASS_COPIED_CALL_STATE_LIST)
+    public void testNotifyPreciseCallStateChangedInProcess() {
+        doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+        doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+
+        final int subId = 1;
+        int[] events = {TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), mTelephonyCallback.callback, events, false);
+        processAllMessages();
+
+        int[] callState = {0, 5, 0};
+        String[] imsCallId = {"0", "1", "0"};
+        int[] imsServiceType = {0, 1, 0};
+        int[] imsCallType = {0, 1, 0};
+        int[] callState2 = {0, 1, 0};
+        mTelephonyRegistry.notifyPreciseCallState(
+                /*phoneId*/ 0, subId, callState, imsCallId, imsServiceType, imsCallType);
+        mTelephonyRegistry.notifyPreciseCallState(
+                /*phoneId*/ 0, subId, callState2, imsCallId, imsServiceType, imsCallType);
+        processAllMessages();
+
+        assertEquals(2, mCallStateList.size());
+        //make sure the call state is from the first report(callState).
+        assertEquals(5, mCallStateList.get(0).getFirst().getCallState());
+        //make sure the call state is from the second report(callState2).
+        assertEquals(1, mCallStateList.get(1).getFirst().getCallState());
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 88eea32..0db881c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -811,7 +811,7 @@
         doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
                 .getRilDataRadioTechnology();
         doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false))
                 .when(mDisplayInfoController).getTelephonyDisplayInfo();
         doReturn(mPhone).when(mCT).getPhone();
         doReturn(mImsEcbm).when(mImsManager).getEcbmInterface();
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 0e2676e..d3f3050 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -101,9 +101,11 @@
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mGoodTelephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false /*roaming*/);
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false /*roaming*/,
+                false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/);
         mBadTelephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UMTS,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/);
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/,
+                false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/);
         mMockedPhoneSwitcherCallback =
                 mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
         mMockedAlarmManager = mock(AlarmManager.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
index 95cefd8..88dce51 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
@@ -126,20 +126,23 @@
         doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel();
         assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
                         TelephonyManager.NETWORK_TYPE_LTE,
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false/*isRoaming*/),
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false/*isRoaming*/,
+                        false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/),
                 signalStrength)).isEqualTo(10227);
         // Verify if entry contains any invalid negative scores, should yield 0.
         doReturn(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN).when(signalStrength).getLevel();
         assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
                         TelephonyManager.NETWORK_TYPE_LTE,
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/),
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/,
+                        false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/),
                 signalStrength))
                 .isEqualTo(0/*OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE*/);
         // Verify non-existent entry should yield -1
         doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel();
         assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
                         TelephonyManager.NETWORK_TYPE_EDGE,
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/),
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/,
+                        false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/),
                 signalStrength))
                 .isEqualTo(0/*OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE*/);
     }
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 ee713c6..3f2d6f2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -888,11 +888,8 @@
         doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
         doReturn(new SubscriptionInfoInternal.Builder().setId(1).build())
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
-
         doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
         doReturn(true).when(mFeatureFlags).satelliteInternet();
-        doReturn(true).when(mFeatureFlags).simDisabledGracefulTearDown();
-
         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
         doReturn(true).when(mMockPackageManager).hasSystemFeature(anyString());
 
@@ -2481,7 +2478,7 @@
 
         // Change data network type to NR
         doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false))
                 .when(mDisplayInfoController).getTelephonyDisplayInfo();
         dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/);
         processAllMessages();
@@ -2524,7 +2521,7 @@
 
         // Change data network type to NR
         doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false))
                 .when(mDisplayInfoController).getTelephonyDisplayInfo();
         dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/);
         processAllMessages();
@@ -2580,7 +2577,7 @@
 
         // Change data network type to NR
         doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false))
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false))
                 .when(mDisplayInfoController).getTelephonyDisplayInfo();
         dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/);
         processAllMessages();
@@ -4311,7 +4308,7 @@
     }
 
     @Test
-    public void testImsGracefulTearDownSimRemoval() throws Exception {
+    public void testImsGracefulTearDown() throws Exception {
         setImsRegistered(true);
         setRcsRegistered(true);
 
@@ -4357,52 +4354,6 @@
     }
 
     @Test
-    public void testImsGracefulTearDownSimDisabled() throws Exception {
-        setImsRegistered(true);
-        setRcsRegistered(true);
-
-        NetworkCapabilities netCaps = new NetworkCapabilities();
-        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-        netCaps.maybeMarkCapabilitiesRestricted();
-        netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE);
-
-        NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
-                ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST);
-        TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(
-                nativeNetworkRequest, mPhone, mFeatureFlags);
-
-        mDataNetworkControllerUT.addNetworkRequest(networkRequest);
-
-        processAllMessages();
-        Mockito.clearInvocations(mPhone);
-
-        // SIM disabled
-        mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
-                TelephonyManager.SIM_STATE_NOT_READY, 0).sendToTarget();
-        processAllMessages();
-
-        // Make sure data network enters disconnecting state
-        ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
-                ArgumentCaptor.forClass(PreciseDataConnectionState.class);
-        verify(mPhone).notifyDataConnection(pdcsCaptor.capture());
-        PreciseDataConnectionState pdcs = pdcsCaptor.getValue();
-        assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTING);
-
-        // IMS de-registered. Now data network is safe to be torn down.
-        Mockito.clearInvocations(mPhone);
-        setImsRegistered(false);
-        setRcsRegistered(false);
-        processAllMessages();
-
-        // All data should be disconnected.
-        verifyAllDataDisconnected();
-        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-        verify(mPhone).notifyDataConnection(pdcsCaptor.capture());
-        pdcs = pdcsCaptor.getValue();
-        assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTED);
-    }
-
-    @Test
     public void testNoGracefulTearDownForEmergencyDataNetwork() throws Exception {
         setImsRegistered(true);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index f7990b9..6e41448 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -146,7 +146,7 @@
     private AutoDataSwitchController.AutoDataSwitchControllerCallback mAutoDataSwitchCallback;
     private TelephonyDisplayInfo mTelephonyDisplayInfo = new TelephonyDisplayInfo(
             TelephonyManager.NETWORK_TYPE_NR,
-            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false);
     private SubscriptionManagerServiceCallback mSubscriptionManagerServiceCallback;
 
     @Before
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
index df14080..9253fbf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -27,7 +27,6 @@
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
-import static com.android.internal.telephony.emergency.EmergencyStateTracker.DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -106,6 +105,7 @@
     private static final String TEST_SMS_ID = "1111";
     private static final String TEST_SMS_ID_2 = "2222";
     private static final int TEST_ECM_EXIT_TIMEOUT_MS = 500;
+    private static final int TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS = 3000;
     private static final EmergencyRegistrationResult E_REG_RESULT = new EmergencyRegistrationResult(
             EUTRAN, REGISTRATION_STATE_HOME, DOMAIN_CS_PS, true, true, 0, 1, "001", "01", "US");
 
@@ -139,7 +139,8 @@
             EmergencyStateTracker.getInstance();
         });
 
-        EmergencyStateTracker.make(mContext, true, mFeatureFlags);
+        EmergencyStateTracker
+                .make(mContext, true, TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS, mFeatureFlags);
 
         assertNotNull(EmergencyStateTracker.getInstance());
     }
@@ -147,7 +148,8 @@
     @Test
     @SmallTest
     public void getInstance_returnsSameInstance() {
-        EmergencyStateTracker.make(mContext, true, mFeatureFlags);
+        EmergencyStateTracker
+                .make(mContext, true, TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS, mFeatureFlags);
         EmergencyStateTracker instance1 = EmergencyStateTracker.getInstance();
         EmergencyStateTracker instance2 = EmergencyStateTracker.getInstance();
 
@@ -184,7 +186,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
         // isOkToCall() should return true when IN_SERVICE state
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
@@ -243,7 +245,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
         // onTimeout should return true when radion on
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
@@ -292,7 +294,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
 
         // Hangup the call
         testConnection.setDisconnected(null);
@@ -330,7 +332,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
         // Verify future completes with DisconnectCause.POWER_OFF if radio not ready
         CompletableFuture<Void> unused = future.thenAccept((result) -> {
             assertEquals((Integer) result, (Integer) DisconnectCause.POWER_OFF);
@@ -3411,8 +3413,9 @@
         doNothing().when(mPhoneSwitcher).overrideDefaultDataForEmergency(
                 anyInt(), anyInt(), any());
         return new EmergencyStateTracker(mContext, mTestableLooper.getLooper(),
-                isSuplDdsSwitchRequiredForEmergencyCall, mPhoneFactoryProxy, mPhoneSwitcherProxy,
-                mTelephonyManagerProxy, mRadioOnHelper, TEST_ECM_EXIT_TIMEOUT_MS, mFeatureFlags);
+                isSuplDdsSwitchRequiredForEmergencyCall, TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS,
+                mPhoneFactoryProxy, mPhoneSwitcherProxy, mTelephonyManagerProxy, mRadioOnHelper,
+                TEST_ECM_EXIT_TIMEOUT_MS, mFeatureFlags);
     }
 
     private Phone setupTestPhoneForEmergencyCall(boolean isRoaming, boolean isRadioOn) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 639a2a3..a29de0f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -70,6 +70,7 @@
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TelephonyTestUtils;
 import com.android.internal.telephony.TestApplication;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.uicc.IsimUiccRecords;
 
@@ -377,23 +378,35 @@
                 any(ICarrierMessagingCallback.class));
     }
 
-    @Test
-    @SmallTest
-    @Ignore("b/256282780")
-    public void testSendSmsByCarrierApp() throws Exception {
+    private int sendSmsWithCarrierAppResponse(int carrierAppResultCode) throws Exception {
         mockCarrierApp();
-        mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
-                mICarrierAppMessagingService, true);
+        mockCarrierAppStubResults(carrierAppResultCode, mICarrierAppMessagingService, true);
         registerTestIntentReceiver();
 
-        PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
-                new Intent(TEST_INTENT)
-                        .setPackage(TestApplication.getAppContext().getPackageName()),
-                PendingIntent.FLAG_MUTABLE);
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(
+                        TestApplication.getAppContext(),
+                        0,
+                        new Intent(TEST_INTENT)
+                                .setPackage(TestApplication.getAppContext().getPackageName()),
+                        PendingIntent.FLAG_MUTABLE);
         mReceivedTestIntent = false;
 
-        mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
-                pendingIntent, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
+        mGsmSmsDispatcher.sendText(
+                "6501002000",
+                "121" /*scAddr*/,
+                "test sms",
+                pendingIntent,
+                null,
+                null,
+                null,
+                mCallingUserId,
+                false,
+                -1,
+                false,
+                -1,
+                false,
+                0L);
         processAllMessages();
         synchronized (mLock) {
             if (!mReceivedTestIntent) {
@@ -402,15 +415,48 @@
             }
             assertEquals(true, mReceivedTestIntent);
             int resultCode = mTestReceiver.getResultCode();
-            assertTrue("Unexpected result code: " + resultCode,
-                    resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK);
-            verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
-                    any(Message.class));
+            verify(mSimulatedCommandsVerifier, times(0))
+                    .sendSMS(anyString(), anyString(), any(Message.class));
+            return resultCode;
         }
     }
 
     @Test
     @SmallTest
+    @Ignore("b/256282780")
+    public void testSendSmsByCarrierApp() throws Exception {
+        int resultCode = sendSmsWithCarrierAppResponse(CarrierMessagingService.SEND_STATUS_OK);
+        assertTrue(
+                "Unexpected result code: " + resultCode,
+                resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK);
+    }
+
+    @Test
+    @SmallTest
+    public void testSendSmsByCarrierApp_PermanentFailure() throws Exception {
+        int resultCode = sendSmsWithCarrierAppResponse(CarrierMessagingService.SEND_STATUS_ERROR);
+        assertTrue(
+                "Unexpected result code: " + resultCode,
+                resultCode == SmsManager.RESULT_RIL_GENERIC_ERROR);
+    }
+
+    @Test
+    @SmallTest
+    public void testSendSmsByCarrierApp_FailureWithReason() throws Exception {
+        if (!Flags.temporaryFailuresInCarrierMessagingService()) {
+            return;
+        }
+        doReturn(true).when(mFeatureFlags).temporaryFailuresInCarrierMessagingService();
+        int resultCode =
+                sendSmsWithCarrierAppResponse(
+                        CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NO_SERVICE);
+        assertTrue(
+                "Unexpected result code: " + resultCode,
+                resultCode == SmsManager.RESULT_ERROR_NO_SERVICE);
+    }
+
+    @Test
+    @SmallTest
     public void testSendSmsByCarrierAppNoResponse() throws Exception {
         mockCarrierApp();
         // do not mock result, instead reduce the timeout for test
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 110dade..e6e4c30 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -4370,94 +4370,95 @@
     public void addSatelliteControllerStats_withExistingEntries() throws Exception {
         createEmptyTestFile();
         mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
-        mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController1);
-        mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController2);
+        mPersistAtomsStorage.addSatelliteControllerStats(copyOf(mSatelliteController1));
+        mPersistAtomsStorage.addSatelliteControllerStats(copyOf(mSatelliteController1));
         mPersistAtomsStorage.incTimeMillis(100L);
 
         SatelliteController expected = new SatelliteController();
         expected.countOfSatelliteServiceEnablementsSuccess =
-                mSatelliteController1.countOfSatelliteServiceEnablementsSuccess
-                        + mSatelliteController2.countOfSatelliteServiceEnablementsSuccess;
+                mSatelliteController1.countOfSatelliteServiceEnablementsSuccess * 2;
         expected.countOfSatelliteServiceEnablementsFail =
-                mSatelliteController1.countOfSatelliteServiceEnablementsFail
-                        + mSatelliteController2.countOfSatelliteServiceEnablementsFail;
+                mSatelliteController1.countOfSatelliteServiceEnablementsFail * 2;
         expected.countOfOutgoingDatagramSuccess =
-                mSatelliteController1.countOfOutgoingDatagramSuccess
-                        + mSatelliteController2.countOfOutgoingDatagramSuccess;
+                mSatelliteController1.countOfOutgoingDatagramSuccess * 2;
         expected.countOfOutgoingDatagramFail =
-                mSatelliteController1.countOfOutgoingDatagramFail
-                        + mSatelliteController2.countOfOutgoingDatagramFail;
+                mSatelliteController1.countOfOutgoingDatagramFail * 2;
         expected.countOfIncomingDatagramSuccess =
-                mSatelliteController1.countOfIncomingDatagramSuccess
-                        + mSatelliteController2.countOfIncomingDatagramSuccess;
+                mSatelliteController1.countOfIncomingDatagramSuccess * 2;
         expected.countOfIncomingDatagramFail =
-                mSatelliteController1.countOfIncomingDatagramFail
-                        + mSatelliteController2.countOfIncomingDatagramFail;
+                mSatelliteController1.countOfIncomingDatagramFail * 2;
         expected.countOfDatagramTypeSosSmsSuccess =
-                mSatelliteController1.countOfDatagramTypeSosSmsSuccess
-                        + mSatelliteController2.countOfDatagramTypeSosSmsSuccess;
+                mSatelliteController1.countOfDatagramTypeSosSmsSuccess * 2;
         expected.countOfDatagramTypeSosSmsFail =
-                mSatelliteController1.countOfDatagramTypeSosSmsFail
-                        + mSatelliteController2.countOfDatagramTypeSosSmsFail;
+                mSatelliteController1.countOfDatagramTypeSosSmsFail * 2;
         expected.countOfDatagramTypeLocationSharingSuccess =
-                mSatelliteController1.countOfDatagramTypeLocationSharingSuccess
-                        + mSatelliteController2.countOfDatagramTypeLocationSharingSuccess;
+                mSatelliteController1.countOfDatagramTypeLocationSharingSuccess * 2;
         expected.countOfDatagramTypeLocationSharingFail =
-                mSatelliteController1.countOfDatagramTypeLocationSharingFail
-                        + mSatelliteController2.countOfDatagramTypeLocationSharingFail;
+                mSatelliteController1.countOfDatagramTypeLocationSharingFail * 2;
         expected.countOfProvisionSuccess =
-                mSatelliteController1.countOfProvisionSuccess
-                        + mSatelliteController2.countOfProvisionSuccess;
+                mSatelliteController1.countOfProvisionSuccess * 2;
         expected.countOfProvisionFail =
-                mSatelliteController1.countOfProvisionFail
-                        + mSatelliteController2.countOfProvisionFail;
+                mSatelliteController1.countOfProvisionFail * 2;
         expected.countOfDeprovisionSuccess =
-                mSatelliteController1.countOfDeprovisionSuccess
-                        + mSatelliteController2.countOfDeprovisionSuccess;
+                mSatelliteController1.countOfDeprovisionSuccess * 2;
         expected.countOfDeprovisionFail =
-                mSatelliteController1.countOfDeprovisionFail
-                        + mSatelliteController2.countOfDeprovisionFail;
+                mSatelliteController1.countOfDeprovisionFail * 2;
         expected.totalServiceUptimeSec =
-                mSatelliteController1.totalServiceUptimeSec
-                        + mSatelliteController2.totalServiceUptimeSec;
+                mSatelliteController1.totalServiceUptimeSec * 2;
         expected.totalBatteryConsumptionPercent =
-                mSatelliteController1.totalBatteryConsumptionPercent
-                        + mSatelliteController2.totalBatteryConsumptionPercent;
+                mSatelliteController1.totalBatteryConsumptionPercent * 2;
         expected.totalBatteryChargedTimeSec =
-                mSatelliteController1.totalBatteryChargedTimeSec
-                        + mSatelliteController2.totalBatteryChargedTimeSec;
+                mSatelliteController1.totalBatteryChargedTimeSec * 2;
         expected.countOfDemoModeSatelliteServiceEnablementsSuccess =
-                mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsSuccess
-                        + mSatelliteController2.countOfDemoModeSatelliteServiceEnablementsSuccess;
+                mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsSuccess * 2;
         expected.countOfDemoModeSatelliteServiceEnablementsFail =
-                mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsFail
-                        + mSatelliteController2.countOfDemoModeSatelliteServiceEnablementsFail;
+                mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsFail * 2;
         expected.countOfDemoModeOutgoingDatagramSuccess =
-                mSatelliteController1.countOfDemoModeOutgoingDatagramSuccess
-                        + mSatelliteController2.countOfDemoModeOutgoingDatagramSuccess;
+                mSatelliteController1.countOfDemoModeOutgoingDatagramSuccess * 2;
         expected.countOfDemoModeOutgoingDatagramFail =
-                mSatelliteController1.countOfDemoModeOutgoingDatagramFail
-                        + mSatelliteController2.countOfDemoModeOutgoingDatagramFail;
+                mSatelliteController1.countOfDemoModeOutgoingDatagramFail * 2;
         expected.countOfDemoModeIncomingDatagramSuccess =
-                mSatelliteController1.countOfDemoModeIncomingDatagramSuccess
-                        + mSatelliteController2.countOfDemoModeIncomingDatagramSuccess;
+                mSatelliteController1.countOfDemoModeIncomingDatagramSuccess * 2;
         expected.countOfDemoModeIncomingDatagramFail =
-                mSatelliteController1.countOfDemoModeIncomingDatagramFail
-                        + mSatelliteController2.countOfDemoModeIncomingDatagramFail;
+                mSatelliteController1.countOfDemoModeIncomingDatagramFail * 2;
         expected.countOfDatagramTypeKeepAliveSuccess =
-                mSatelliteController1.countOfDatagramTypeKeepAliveSuccess
-                        + mSatelliteController2.countOfDatagramTypeKeepAliveSuccess;
+                mSatelliteController1.countOfDatagramTypeKeepAliveSuccess * 2;
         expected.countOfDatagramTypeKeepAliveFail =
-                mSatelliteController1.countOfDatagramTypeKeepAliveFail
-                        + mSatelliteController2.countOfDatagramTypeKeepAliveFail;
-        expected.isProvisioned = mSatelliteController2.isProvisioned;
-        expected.carrierId = mSatelliteController2.carrierId;
+                mSatelliteController1.countOfDatagramTypeKeepAliveFail * 2;
+        expected.isProvisioned = mSatelliteController1.isProvisioned;
+        expected.carrierId = mSatelliteController1.carrierId;
+        expected.countOfSatelliteAllowedStateChangedEvents =
+                mSatelliteController1.countOfSatelliteAllowedStateChangedEvents * 2;
+        expected.countOfSuccessfulLocationQueries =
+                mSatelliteController1.countOfSuccessfulLocationQueries * 2;
+        expected.countOfFailedLocationQueries =
+                mSatelliteController1.countOfFailedLocationQueries * 2;
 
         // Service state and service switch should be added successfully
         verifyCurrentStateSavedToFileOnce();
         SatelliteController[] output =
                 mPersistAtomsStorage.getSatelliteControllerStats(0L);
-        assertHasStats(output, expected);
+        assertHasStats(output, expected, 1);
+    }
+
+    @Test
+    public void addSatelliteControllerStats_addNewCarrierId() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController1);
+        mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController2);
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        SatelliteController expected1 = mSatelliteController1;
+        SatelliteController expected2 = mSatelliteController2;
+
+        // Service state and service switch should be added successfully
+        verifyCurrentStateSavedToFileOnce();
+        SatelliteController[] output =
+                mPersistAtomsStorage.getSatelliteControllerStats(0L);
+
+        assertHasStats(output, expected1, 1);
+        assertHasStats(output, expected2, 1);
     }
 
     @Test
@@ -5829,60 +5830,71 @@
         assertEquals(expectedCount, actualCount);
     }
 
-    private static void assertHasStats(
-            SatelliteController[] tested,
-            @Nullable SatelliteController expectedStats) {
+    private static void assertHasStats(SatelliteController[] tested,
+            @Nullable SatelliteController expectedStats, int expectedCount) {
         assertNotNull(tested);
-        assertEquals(tested[0].countOfSatelliteServiceEnablementsSuccess,
-                expectedStats.countOfSatelliteServiceEnablementsSuccess);
-        assertEquals(tested[0].countOfSatelliteServiceEnablementsFail,
-                expectedStats.countOfSatelliteServiceEnablementsFail);
-        assertEquals(tested[0].countOfOutgoingDatagramSuccess,
-                expectedStats.countOfOutgoingDatagramSuccess);
-        assertEquals(tested[0].countOfOutgoingDatagramFail,
-                expectedStats.countOfOutgoingDatagramFail);
-        assertEquals(tested[0].countOfIncomingDatagramSuccess,
-                expectedStats.countOfIncomingDatagramSuccess);
-        assertEquals(tested[0].countOfIncomingDatagramFail,
-                expectedStats.countOfIncomingDatagramFail);
-        assertEquals(tested[0].countOfDatagramTypeSosSmsSuccess,
-                expectedStats.countOfDatagramTypeSosSmsSuccess);
-        assertEquals(tested[0].countOfDatagramTypeSosSmsFail,
-                expectedStats.countOfDatagramTypeSosSmsFail);
-        assertEquals(tested[0].countOfDatagramTypeLocationSharingSuccess,
-                expectedStats.countOfDatagramTypeLocationSharingSuccess);
-        assertEquals(tested[0].countOfDatagramTypeLocationSharingFail,
-                expectedStats.countOfDatagramTypeLocationSharingFail);
-        assertEquals(tested[0].totalServiceUptimeSec,
-                expectedStats.totalServiceUptimeSec);
-        assertEquals(tested[0].totalBatteryConsumptionPercent,
-                expectedStats.totalBatteryConsumptionPercent);
-        assertEquals(tested[0].totalBatteryChargedTimeSec,
-                expectedStats.totalBatteryChargedTimeSec);
-        assertEquals(tested[0].countOfDemoModeSatelliteServiceEnablementsSuccess,
-                expectedStats.countOfDemoModeSatelliteServiceEnablementsSuccess);
-        assertEquals(tested[0].countOfDemoModeSatelliteServiceEnablementsFail,
-                expectedStats.countOfDemoModeSatelliteServiceEnablementsFail);
-        assertEquals(tested[0].countOfDemoModeOutgoingDatagramSuccess,
-                expectedStats.countOfDemoModeOutgoingDatagramSuccess);
-        assertEquals(tested[0].countOfDemoModeOutgoingDatagramFail,
-                expectedStats.countOfDemoModeOutgoingDatagramFail);
-        assertEquals(tested[0].countOfDemoModeIncomingDatagramSuccess,
-                expectedStats.countOfDemoModeIncomingDatagramSuccess);
-        assertEquals(tested[0].countOfDemoModeIncomingDatagramFail,
-                expectedStats.countOfDemoModeIncomingDatagramFail);
-        assertEquals(tested[0].countOfDatagramTypeKeepAliveSuccess,
-                expectedStats.countOfDatagramTypeKeepAliveSuccess);
-        assertEquals(tested[0].countOfDatagramTypeKeepAliveFail,
-                expectedStats.countOfDatagramTypeKeepAliveFail);
-        assertEquals(tested[0].countOfAllowedSatelliteAccess,
-                expectedStats.countOfAllowedSatelliteAccess);
-        assertEquals(tested[0].countOfDisallowedSatelliteAccess,
-                expectedStats.countOfDisallowedSatelliteAccess);
-        assertEquals(tested[0].countOfSatelliteAccessCheckFail,
-                expectedStats.countOfSatelliteAccessCheckFail);
-        assertEquals(tested[0].isProvisioned, expectedStats.isProvisioned);
-        assertEquals(tested[0].carrierId, expectedStats.carrierId);
+        int actualCount = 0;
+        int i = 0;
+        for (SatelliteController stats : tested) {
+            if (expectedStats.carrierId == stats.carrierId) {
+                assertEquals(expectedStats.countOfSatelliteServiceEnablementsSuccess,
+                        stats.countOfSatelliteServiceEnablementsSuccess);
+                assertEquals(expectedStats.countOfSatelliteServiceEnablementsFail,
+                        stats.countOfSatelliteServiceEnablementsFail);
+                assertEquals(expectedStats.countOfOutgoingDatagramSuccess,
+                        stats.countOfOutgoingDatagramSuccess);
+                assertEquals(expectedStats.countOfOutgoingDatagramFail,
+                        stats.countOfOutgoingDatagramFail);
+                assertEquals(expectedStats.countOfIncomingDatagramSuccess,
+                        stats.countOfIncomingDatagramSuccess);
+                assertEquals(expectedStats.countOfIncomingDatagramFail,
+                        stats.countOfIncomingDatagramFail);
+                assertEquals(expectedStats.countOfDatagramTypeSosSmsSuccess,
+                        stats.countOfDatagramTypeSosSmsSuccess);
+                assertEquals(expectedStats.countOfDatagramTypeSosSmsFail,
+                        stats.countOfDatagramTypeSosSmsFail);
+                assertEquals(expectedStats.countOfDatagramTypeLocationSharingSuccess,
+                        stats.countOfDatagramTypeLocationSharingSuccess);
+                assertEquals(expectedStats.countOfDatagramTypeLocationSharingFail,
+                        stats.countOfDatagramTypeLocationSharingFail);
+                assertEquals(expectedStats.totalServiceUptimeSec, stats.totalServiceUptimeSec);
+                assertEquals(expectedStats.totalBatteryConsumptionPercent,
+                        stats.totalBatteryConsumptionPercent);
+                assertEquals(expectedStats.totalBatteryChargedTimeSec,
+                        stats.totalBatteryChargedTimeSec);
+                assertEquals(expectedStats.countOfDemoModeSatelliteServiceEnablementsSuccess,
+                        stats.countOfDemoModeSatelliteServiceEnablementsSuccess);
+                assertEquals(expectedStats.countOfDemoModeSatelliteServiceEnablementsFail,
+                        stats.countOfDemoModeSatelliteServiceEnablementsFail);
+                assertEquals(expectedStats.countOfDemoModeOutgoingDatagramSuccess,
+                        stats.countOfDemoModeOutgoingDatagramSuccess);
+                assertEquals(expectedStats.countOfDemoModeOutgoingDatagramFail,
+                        stats.countOfDemoModeOutgoingDatagramFail);
+                assertEquals(expectedStats.countOfDemoModeIncomingDatagramSuccess,
+                        stats.countOfDemoModeIncomingDatagramSuccess);
+                assertEquals(expectedStats.countOfDemoModeIncomingDatagramFail,
+                        stats.countOfDemoModeIncomingDatagramFail);
+                assertEquals(expectedStats.countOfDatagramTypeKeepAliveSuccess,
+                        stats.countOfDatagramTypeKeepAliveSuccess);
+                assertEquals(expectedStats.countOfDatagramTypeKeepAliveFail,
+                        stats.countOfDatagramTypeKeepAliveFail);
+                assertEquals(expectedStats.countOfAllowedSatelliteAccess,
+                        stats.countOfAllowedSatelliteAccess);
+                assertEquals(expectedStats.countOfDisallowedSatelliteAccess,
+                        stats.countOfDisallowedSatelliteAccess);
+                assertEquals(expectedStats.countOfSatelliteAccessCheckFail,
+                        stats.countOfSatelliteAccessCheckFail);
+                assertEquals(expectedStats.isProvisioned, stats.isProvisioned);
+                assertEquals(expectedStats.countOfSatelliteAllowedStateChangedEvents,
+                        stats.countOfSatelliteAllowedStateChangedEvents);
+                assertEquals(expectedStats.countOfSuccessfulLocationQueries,
+                        stats.countOfSuccessfulLocationQueries);
+                assertEquals(expectedStats.countOfFailedLocationQueries,
+                        stats.countOfFailedLocationQueries);
+                actualCount++;
+            }
+        }
+        assertEquals(expectedCount, actualCount);
     }
 
     private static void assertHasStatsAndCount(
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 21731f0..271e0ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java
@@ -16,10 +16,12 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING;
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
@@ -44,6 +46,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.R;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
@@ -187,6 +190,76 @@
         }
     }
 
+    @Test
+    public void testNeedsWaitingForSatelliteConnected_checkMessageInNotConnected_returnsFalse()
+            throws Exception {
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mContextFixture.putBooleanResource(
+                R.bool.config_satellite_allow_check_message_in_not_connected, true);
+        mDatagramControllerUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+
+        boolean result =
+                mDatagramControllerUT.needsWaitingForSatelliteConnected(
+                        DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS);
+
+        assertFalse(result);
+    }
+
+    @Test
+    public void testNeedsWaitingForSatelliteConnected_regularSmsInNotConnected_returnsTrue()
+            throws Exception {
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mContextFixture.putBooleanResource(
+                R.bool.config_satellite_allow_check_message_in_not_connected, true);
+        mDatagramControllerUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+
+        boolean result =
+                mDatagramControllerUT.needsWaitingForSatelliteConnected(
+                        DATAGRAM_TYPE_SMS);
+
+        assertTrue(result);
+    }
+
+    @Test
+    public void
+            testNeedsWaitingForSatelliteConnected_checkMessageInNotConnected_allowCheckMessageFalse_returnsTrue()
+                    throws Exception {
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mContextFixture.putBooleanResource(
+                R.bool.config_satellite_allow_check_message_in_not_connected, false);
+        mDatagramControllerUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+
+        boolean result =
+                mDatagramControllerUT.needsWaitingForSatelliteConnected(
+                        DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS);
+
+        assertTrue(result);
+    }
+
+    @Test
+    public void
+            testNeedsWaitingForSatelliteConnected_checkMessageInNotConnected_carrierRoamingNbIotNtnFalse_returnsTrue()
+                    throws Exception {
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
+        mContextFixture.putBooleanResource(
+                R.bool.config_satellite_allow_check_message_in_not_connected, true);
+        mDatagramControllerUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+
+        boolean result =
+                mDatagramControllerUT.needsWaitingForSatelliteConnected(
+                        DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS);
+
+        assertTrue(result);
+    }
+
     private void testUpdateSendStatus(boolean isDemoMode, int datagramType, int sendState) {
         mDatagramControllerUT.setDemoMode(isDemoMode);
         clearAllInvocations();
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 1941518..dc973af 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
@@ -257,7 +258,7 @@
                             eq(SATELLITE_RESULT_SUCCESS));
             verifyNoMoreInteractions(mMockDatagramController);
             verify(mMockSessionMetricsStats, times(1))
-                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType));
+                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType), anyLong());
             verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
                     any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
@@ -398,7 +399,7 @@
                     any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
             assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
             verify(mMockSessionMetricsStats, times(1))
-                    .addCountOfSuccessfulOutgoingDatagram(anyInt());
+                    .addCountOfSuccessfulOutgoingDatagram(anyInt(), anyLong());
             clearInvocations(mMockSatelliteModemInterface);
             clearInvocations(mMockDatagramController);
             clearInvocations(mMockSessionMetricsStats);
@@ -523,7 +524,7 @@
                             eq(SATELLITE_RESULT_SUCCESS));
             assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
             verify(mMockSessionMetricsStats, times(1))
-                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType));
+                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType), anyLong());
             mDatagramDispatcherUT.setDemoMode(false);
             mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
         }
@@ -618,7 +619,7 @@
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSessionMetricsStats, times(1))
-                .addCountOfSuccessfulOutgoingDatagram(eq(DATAGRAM_TYPE2));
+                .addCountOfSuccessfulOutgoingDatagram(eq(DATAGRAM_TYPE2), anyLong());
 
         mDatagramDispatcherUT.setDemoMode(false);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
@@ -731,7 +732,7 @@
                     anyInt(), any(SatelliteDatagram.class));
             verify(mMockDatagramController).pollPendingSatelliteDatagrams(anyInt(), any());
             verify(mMockSessionMetricsStats, times(1))
-                    .addCountOfSuccessfulOutgoingDatagram(anyInt());
+                    .addCountOfSuccessfulOutgoingDatagram(anyInt(), anyLong());
 
             // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is
             // false
@@ -824,8 +825,7 @@
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId,
-                true, true);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -859,8 +859,7 @@
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId,
-                true, false);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, false);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -1043,7 +1042,7 @@
                         eq(1),
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSessionMetricsStats, times(1))
-                .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos));
+                .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos), anyLong());
         verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
                 any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
         assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
@@ -1058,8 +1057,7 @@
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId,
-                true, true);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -1110,8 +1108,7 @@
         processAllMessages();
         verifyZeroInteractions(mMockSatelliteModemInterface);
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId,
-                true, true);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true);
         processAllMessages();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
@@ -1137,7 +1134,7 @@
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSessionMetricsStats, times(1))
-                .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos));
+                .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos), anyLong());
         verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
                 any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
         assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
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 c0c6f63..6f716b5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -22,6 +22,7 @@
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
 import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION;
 import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC;
+import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT;
@@ -32,7 +33,12 @@
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
+import static android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_ALL;
+import static android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED;
+import static android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED;
 import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_DATA;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_SMS;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_VOICE;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GREAT;
@@ -81,9 +87,12 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static com.android.internal.telephony.satellite.SatelliteController.DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS;
+import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_DATA_PLAN_METERED;
+import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_DATA_PLAN_UNMETERED;
 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_FALSE;
 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_TRUE;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -150,6 +159,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.satellite.EarfcnRange;
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
@@ -157,17 +167,22 @@
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteSupportedStateCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.ISelectedNbIotSatelliteSubscriptionCallback;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteInfo;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteManager.SatelliteException;
 import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
+import android.telephony.satellite.SatellitePosition;
 import android.telephony.satellite.SatelliteSubscriberInfo;
 import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
 import android.telephony.satellite.SatelliteSubscriptionInfo;
+import android.telephony.satellite.SystemSelectionSpecifier;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.IntArray;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -205,10 +220,12 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -540,6 +557,22 @@
         }
     };
 
+    private int mQueriedSystemSelectionChannelUpdatedResultCode = SATELLITE_RESULT_SUCCESS;
+    private Semaphore mSystemSelectionChannelUpdatedSemaphore = new Semaphore(0);
+    private ResultReceiver mSystemSelectionChannelUpdatedReceiver = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mQueriedSystemSelectionChannelUpdatedResultCode = resultCode;
+            try {
+                mSystemSelectionChannelUpdatedSemaphore.release();
+            } catch (Exception ex) {
+                fail("mSystemSelectionChannelUpdatedReceiver: Got exception in releasing "
+                        + "semaphore, ex="
+                        + ex);
+            }
+        }
+    };
+
     @Rule
     public final CheckFlagsRule mCheckFlagsRule =
             DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -1702,6 +1735,7 @@
 
     @Test
     public void testRegisterForSatelliteProvisionStateChanged() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         Semaphore semaphore = new Semaphore(0);
         ISatelliteProvisionStateCallback callback =
                 new ISatelliteProvisionStateCallback.Stub() {
@@ -1753,7 +1787,6 @@
         processAllMessages();
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
-
         mSatelliteControllerUT.unregisterForSatelliteProvisionStateChanged(callback);
         semaphore.drainPermits();
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
@@ -3140,7 +3173,8 @@
         // Verify call the requestSetSatelliteEnabledForCarrier to enable the satellite when
         // satellite service is enabled by entitlement server.
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true, new ArrayList<>(),
-                new ArrayList<>(), mIIntegerConsumer);
+                new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), mIIntegerConsumer);
         processAllMessages();
 
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -3160,7 +3194,8 @@
                 .when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, new ArrayList<>(),
-                new ArrayList<>(), mIIntegerConsumer);
+                new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), mIIntegerConsumer);
         processAllMessages();
 
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -3191,7 +3226,8 @@
         List<String> entitlementPlmnList = new ArrayList<>();
         List<String> barredPlmnList = new ArrayList<>();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
-                entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
         verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
                 any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
@@ -3249,7 +3285,8 @@
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", ""}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
-                entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
         verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
                 any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
@@ -3257,7 +3294,8 @@
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "123456789"}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
-                entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
         verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
                 any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
@@ -3265,7 +3303,8 @@
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "12"}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
-                entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
         verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
                 any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
@@ -3273,7 +3312,8 @@
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "1234"}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
-                entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
         verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
                 any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
     }
@@ -3282,7 +3322,8 @@
             List<String> mergedPlmnList, List<String> overlayConfigPlmnList,
             List<String> barredPlmnList) {
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
-                entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
 
         List<String> plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(
                 SUB_ID);
@@ -3504,21 +3545,24 @@
 
         // Change SUB_ID's EntitlementStatus to true
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true, new ArrayList<>(),
-                new ArrayList<>(), mIIntegerConsumer);
+                new ArrayList<>(), new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
 
         assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID));
         assertEquals(false, satelliteEnabledPerCarrier.get(SUB_ID1));
 
         // Change SUB_ID1's EntitlementStatus to true
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID1, true, new ArrayList<>(),
-                new ArrayList<>(), mIIntegerConsumer);
+                new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), mIIntegerConsumer);
 
         assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID));
         assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1));
 
         // Change SUB_ID's EntitlementStatus to false
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, new ArrayList<>(),
-                new ArrayList<>(), mIIntegerConsumer);
+                new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(),
+                new HashMap<>(), mIIntegerConsumer);
 
         assertEquals(false, satelliteEnabledPerCarrier.get(SUB_ID));
         assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1));
@@ -3650,7 +3694,10 @@
 
     @Test
     public void testHandleEventServiceStateChanged() {
+        mContextFixture.putBooleanResource(
+            R.bool.config_satellite_should_notify_availability, true);
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT,
                 CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC);
         invokeCarrierConfigChanged();
@@ -3658,6 +3705,7 @@
         // Do nothing when the satellite is not connected
         doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork();
         sendServiceStateChangedEvent();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         processAllMessages();
         assertFalse(mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
         verify(mMockNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(), any());
@@ -3983,6 +4031,96 @@
     }
 
     @Test
+    public void testRegisterForSelectedNbIotSatelliteSubscriptionChanged_WithFeatureFlagEnabled() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        Semaphore semaphore = new Semaphore(0);
+        final int[] selectedSubIds = new int[1];
+        ISelectedNbIotSatelliteSubscriptionCallback callback =
+                new ISelectedNbIotSatelliteSubscriptionCallback.Stub() {
+                    @Override
+                    public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) {
+                        logd("onSelectedNbIotSatelliteSubscriptionChanged: selectedSubId="
+                                + selectedSubId);
+                        try {
+                            selectedSubIds[0] = selectedSubId;
+                            semaphore.release();
+                        } catch (Exception ex) {
+                            loge("onSelectedNbIotSatelliteSubscriptionChanged: Got exception in "
+                                    + "releasing semaphore, ex=" + ex);
+                        }
+                    }
+                };
+
+        int errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged(
+                callback);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
+
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged(
+                callback);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
+
+        // Register the callback and verify that the event is reported.
+        resetSatelliteControllerUT();
+        setUpResponseForRequestIsSatelliteProvisioned(true,SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged(
+                callback);
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
+        int expectedSubId = 1;
+        sendSelectedNbIotSatelliteSubscriptionChangedEvent(expectedSubId, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSelectedNbIotSatelliteSubscriptionChanged"));
+        assertEquals(expectedSubId, selectedSubIds[0]);
+
+        // Unregister the callback and verify that the event is not reported.
+        mSatelliteControllerUT.unregisterForSelectedNbIotSatelliteSubscriptionChanged(callback);
+        sendSelectedNbIotSatelliteSubscriptionChangedEvent(2, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 0, "testRegisterForSelectedNbIotSatelliteSubscriptionChanged"));
+    }
+
+    @Test
+    public void testRegisterForSelectedNbIotSatelliteSubscriptionChanged_WithFeatureFlagDisabled() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
+
+        Semaphore semaphore = new Semaphore(0);
+        final int[] selectedSubIds = new int[1];
+        ISelectedNbIotSatelliteSubscriptionCallback callback =
+                new ISelectedNbIotSatelliteSubscriptionCallback.Stub() {
+                    @Override
+                    public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) {
+                        logd("onSelectedNbIotSatelliteSubscriptionChanged: selectedSubId="
+                                + selectedSubId);
+                        try {
+                            selectedSubIds[0] = selectedSubId;
+                            semaphore.release();
+                        } catch (Exception ex) {
+                            loge("onSelectedNbIotSatelliteSubscriptionChanged: Got exception in "
+                                    + "releasing semaphore, ex=" + ex);
+                        }
+                    }
+                };
+
+        int errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged(
+                callback);
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+
+        // Verify that the event is not reported.
+        sendSelectedNbIotSatelliteSubscriptionChangedEvent(1, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 0, "testRegisterForSelectedNbIotSatelliteSubscriptionChanged"));
+
+
+    }
+
+    @Test
     public void testIsSatelliteEmergencyMessagingSupportedViaCarrier() {
         // Carrier-enabled flag is off
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false);
@@ -4145,13 +4283,17 @@
 
     @Test
     public void testNotifyNtnEligibilityHysteresisTimedOut() {
+        mContextFixture.putBooleanResource(
+            R.bool.config_satellite_should_notify_availability, true);
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
         when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         mSatelliteControllerUT.mIsApplicationSupportsP2P = true;
+        mSatelliteControllerUT.setIsSatelliteSupported(true);
         mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
-        mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
+        mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT,
+            CARRIER_ROAMING_NTN_CONNECT_MANUAL);
         mCarrierConfigBundle.putInt(
                 KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 1 * 60);
         mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, true);
@@ -4173,6 +4315,7 @@
         }
         mSatelliteControllerUT.setSatellitePhone(1);
         mSatelliteControllerUT.isSatelliteAllowedCallback = null;
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         processAllMessages();
         mSatelliteControllerUT.elapsedRealtime = 0;
         assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
@@ -4600,7 +4743,7 @@
                 any());
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
-        assertTrue(resultArray[0].getProvisionStatus());
+        assertTrue(resultArray[0].isProvisioned());
         assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
 
         // Request provisioning with SatelliteSubscriberInfo that has not been provisioned
@@ -4611,7 +4754,7 @@
 
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
-        assertTrue(resultArray[1].getProvisionStatus());
+        assertTrue(resultArray[1].isProvisioned());
         assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
 
         // Request provisioning with the same SatelliteSubscriberInfo that was previously
@@ -4626,9 +4769,9 @@
         verifyDeprovisionSatellite(inputList);
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
-        assertFalse(resultArray[1].getProvisionStatus());
+        assertFalse(resultArray[1].isProvisioned());
         assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
-        assertTrue(resultArray[0].getProvisionStatus());
+        assertTrue(resultArray[0].isProvisioned());
         assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
 
         // Request deprovision for subscriberID 1, verify that subscriberID 1 is set to deprovision.
@@ -4637,9 +4780,9 @@
         verifyDeprovisionSatellite(inputList);
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
-        assertFalse(resultArray[1].getProvisionStatus());
+        assertFalse(resultArray[1].isProvisioned());
         assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
-        assertFalse(resultArray[0].getProvisionStatus());
+        assertFalse(resultArray[0].isProvisioned());
         assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
 
         // Request provision for subscriberID 2, verify that subscriberID 2 is set to provision.
@@ -4649,9 +4792,9 @@
 
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
-        assertTrue(resultArray[1].getProvisionStatus());
+        assertTrue(resultArray[1].isProvisioned());
         assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
-        assertFalse(resultArray[0].getProvisionStatus());
+        assertFalse(resultArray[0].isProvisioned());
         assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
     }
 
@@ -5022,6 +5165,132 @@
         assertFalse(mSatelliteControllerUT.isApplicationUpdated);
     }
 
+    @Test
+    public void testUpdateSystemSelectionChannels() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        String mccmnc = "123455";
+        int[] bands1 = {200, 201, 202};
+        IntArray intArraybands1 = new IntArray(3);
+        intArraybands1.addAll(bands1);
+        int[] earfcns1 = {300, 301, 310, 311};
+        IntArray intArrayEarfcns1 = new IntArray(4);
+        intArrayEarfcns1.addAll(earfcns1);
+        String seed1 = "test-seed-satellite1";
+        UUID uuid1 = UUID.nameUUIDFromBytes(seed1.getBytes());
+        SatellitePosition satellitePosition1 = new SatellitePosition(0, 35876);
+        EarfcnRange earfcnRange1 = new EarfcnRange(301, 300);
+        EarfcnRange earfcnRange2 = new EarfcnRange(311, 310);
+        List<EarfcnRange> earfcnRangeList1 = new ArrayList<>(
+                Arrays.asList(earfcnRange1, earfcnRange2));
+        SatelliteInfo satelliteInfo1 = new SatelliteInfo(uuid1, satellitePosition1, Arrays.stream(
+                bands1).boxed().collect(Collectors.toList()), earfcnRangeList1);
+        int[] tagIds = {1, 2, 3};
+        IntArray intArrayTagIds = new IntArray(3);
+        intArrayTagIds.addAll(tagIds);
+        SystemSelectionSpecifier systemSelectionSpecifier1 = new SystemSelectionSpecifier(mccmnc,
+                intArraybands1, intArrayEarfcns1, new SatelliteInfo[]{satelliteInfo1},
+                intArrayTagIds);
+
+        setUpResponseForUpdateSystemSelectionChannels(SATELLITE_RESULT_ERROR);
+        mSatelliteControllerUT.updateSystemSelectionChannels(
+                new ArrayList<>(List.of(systemSelectionSpecifier1)),
+                mSystemSelectionChannelUpdatedReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestUpdateSystemSelectionChannelResult(1));
+        assertEquals(SATELLITE_RESULT_ERROR, mQueriedSystemSelectionChannelUpdatedResultCode);
+
+        // Verify whether callback receives expected result
+        setUpResponseForUpdateSystemSelectionChannels(SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.updateSystemSelectionChannels(
+                new ArrayList<>(List.of(systemSelectionSpecifier1)),
+                mSystemSelectionChannelUpdatedReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestUpdateSystemSelectionChannelResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSystemSelectionChannelUpdatedResultCode);
+
+        // Verify whether SatelliteModemInterface API was invoked and data is valid, when single
+        // data was provided.
+        ArgumentCaptor<List<SystemSelectionSpecifier>> systemSelectionSpecifierListCaptor =
+                ArgumentCaptor.forClass(List.class);
+        verify(mMockSatelliteModemInterface, times(2)).updateSystemSelectionChannels(
+                systemSelectionSpecifierListCaptor.capture(), any(Message.class));
+        List<SystemSelectionSpecifier> capturedList = systemSelectionSpecifierListCaptor.getValue();
+        SystemSelectionSpecifier systemSelectionSpecifier = capturedList.getFirst();
+
+        assertEquals(mccmnc, systemSelectionSpecifier.getMccMnc());
+        int[] actualBandsArray = systemSelectionSpecifier.getBands();
+        assertArrayEquals(bands1, actualBandsArray);
+        int[] actualEarfcnsArray = systemSelectionSpecifier.getEarfcns();
+        assertArrayEquals(earfcns1, actualEarfcnsArray);
+        assertArrayEquals(new SatelliteInfo[]{satelliteInfo1},
+                systemSelectionSpecifier.getSatelliteInfos().toArray(new SatelliteInfo[0]));
+        int[] actualTagIdArray = systemSelectionSpecifier.getTagIds();
+        assertArrayEquals(tagIds, actualTagIdArray);
+
+        // Verify whether SatelliteModemInterface API was invoked and data is valid, when list
+        // of data was provided.
+        int[] bands2 = {210, 211, 212};
+        IntArray intArraybands2 = new IntArray(3);
+        intArraybands2.addAll(bands2);
+        int[] earfcns2 = {320, 321, 330, 331};
+        IntArray intArrayEarfcns2 = new IntArray(4);
+        intArrayEarfcns2.addAll(earfcns2);
+        String seed2 = "test-seed-satellite2";
+        UUID uuid2 = UUID.nameUUIDFromBytes(seed2.getBytes());
+        SatellitePosition satellitePosition2 = new SatellitePosition(120, 35876);
+        EarfcnRange earfcnRange3 = new EarfcnRange(321, 320);
+        EarfcnRange earfcnRange4 = new EarfcnRange(331, 330);
+        List<EarfcnRange> earfcnRangeList2 = new ArrayList<>(
+                Arrays.asList(earfcnRange3, earfcnRange4));
+        SatelliteInfo satelliteInfo2 = new SatelliteInfo(uuid2, satellitePosition2, Arrays.stream(
+                bands1).boxed().collect(Collectors.toList()), earfcnRangeList2);
+        SystemSelectionSpecifier systemSelectionSpecifier2 = new SystemSelectionSpecifier(mccmnc,
+                intArraybands2, intArrayEarfcns2, new SatelliteInfo[]{satelliteInfo2},
+                intArrayTagIds);
+
+        // Verify whether callback receives expected result
+        setUpResponseForUpdateSystemSelectionChannels(SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.updateSystemSelectionChannels(
+                new ArrayList<>(List.of(systemSelectionSpecifier1, systemSelectionSpecifier2)),
+                mSystemSelectionChannelUpdatedReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestUpdateSystemSelectionChannelResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSystemSelectionChannelUpdatedResultCode);
+
+        // Verify whether SatelliteModemInterface API was invoked and data is valid,
+        verify(mMockSatelliteModemInterface, times(3)).updateSystemSelectionChannels(
+                systemSelectionSpecifierListCaptor.capture(), any(Message.class));
+        capturedList = systemSelectionSpecifierListCaptor.getValue();
+        SystemSelectionSpecifier capturedSystemSelectionSpecifier1 = capturedList.getFirst();
+        SystemSelectionSpecifier capturedSystemSelectionSpecifier2 = capturedList.get(1);
+
+        // Verify first SystemSelectionSpecifier
+        assertEquals(mccmnc, systemSelectionSpecifier.getMccMnc());
+        actualBandsArray = capturedSystemSelectionSpecifier1.getBands();
+        assertArrayEquals(bands1, actualBandsArray);
+        actualEarfcnsArray = capturedSystemSelectionSpecifier1.getEarfcns();
+        assertArrayEquals(earfcns1, actualEarfcnsArray);
+        assertArrayEquals(new SatelliteInfo[]{satelliteInfo1},
+                capturedSystemSelectionSpecifier1.getSatelliteInfos().toArray(
+                        new SatelliteInfo[0]));
+        actualTagIdArray = capturedSystemSelectionSpecifier1.getTagIds();
+        assertArrayEquals(tagIds, actualTagIdArray);
+
+        // Verify second SystemSelectionSpecifier
+        assertEquals(mccmnc, systemSelectionSpecifier.getMccMnc());
+        actualBandsArray = capturedSystemSelectionSpecifier2.getBands();
+        assertArrayEquals(bands2, actualBandsArray);
+        actualEarfcnsArray = capturedSystemSelectionSpecifier2.getEarfcns();
+        assertArrayEquals(earfcns2, actualEarfcnsArray);
+        assertArrayEquals(new SatelliteInfo[]{satelliteInfo2},
+                capturedSystemSelectionSpecifier2.getSatelliteInfos().toArray(
+                        new SatelliteInfo[0]));
+        actualTagIdArray = capturedSystemSelectionSpecifier2.getTagIds();
+        assertArrayEquals(tagIds, actualTagIdArray);
+    }
+
     private void verifyProvisionStatusPerSubscriberIdGetFromDb(boolean provision) {
         doReturn(provision).when(
                 mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram(anyInt());
@@ -5042,7 +5311,7 @@
         assertEquals(SATELLITE_RESULT_SUCCESS,
                 mRequestSatelliteSubscriberProvisionStatusResultCode);
         assertEquals(provision,
-                mRequestSatelliteSubscriberProvisionStatusResultList.get(0).getProvisionStatus());
+                mRequestSatelliteSubscriberProvisionStatusResultList.get(0).isProvisioned());
     }
 
     private void setComponentName() {
@@ -5336,6 +5605,19 @@
         }).when(mMockSatelliteModemInterface).stopSendingNtnSignalStrength(any(Message.class));
     }
 
+    private void setUpResponseForUpdateSystemSelectionChannels(
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
+                ? null : new SatelliteException(error);
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[1];
+            AsyncResult.forMessage(message, null, exception);
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).updateSystemSelectionChannels(anyList(),
+                any(Message.class));
+    }
+
     private boolean waitForRequestIsSatelliteSupportedResult(int expectedNumberOfEvents) {
         for (int i = 0; i < expectedNumberOfEvents; i++) {
             try {
@@ -5459,6 +5741,24 @@
         return true;
     }
 
+    private boolean waitForRequestUpdateSystemSelectionChannelResult(int expectedNumberOfEvents) {
+        for (int i = 0; i < expectedNumberOfEvents; i++) {
+            try {
+                if (!mSystemSelectionChannelUpdatedSemaphore.tryAcquire(TIMEOUT,
+                        TimeUnit.MILLISECONDS)) {
+                    logd("Timeout to receive "
+                            + "updateSystemSelectionChannel()"
+                            + " callback");
+                    return false;
+                }
+            } catch (Exception ex) {
+                logd("updateSystemSelectionChannel: Got exception=" + ex);
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void verifySatelliteSupported(boolean supported, int expectedErrorCode) {
         mSatelliteSupportSemaphore.drainPermits();
         mSatelliteControllerUT.requestIsSatelliteSupported(mSatelliteSupportReceiver);
@@ -5586,6 +5886,14 @@
         msg.sendToTarget();
     }
 
+    private void sendSelectedNbIotSatelliteSubscriptionChangedEvent(int selectedSubId,
+            Throwable exception) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                60 /* EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED */);
+        msg.obj = new AsyncResult(null, selectedSubId, exception);
+        msg.sendToTarget();
+    }
+
     private void setRadioPower(boolean on) {
         mSimulatedCommands.setRadioPower(on, false, false, null);
     }
@@ -5911,6 +6219,12 @@
             }
         }
 
+        void setIsSatelliteSupported(@Nullable Boolean isSatelliteSupported) {
+            synchronized (mIsSatelliteSupportedLock) {
+                mIsSatelliteSupported = isSatelliteSupported;
+            }
+        }
+
         public boolean isRadioOn() {
             synchronized (mIsRadioOnLock) {
                 return mIsRadioOn;
@@ -5969,14 +6283,14 @@
         final String callerSC =  "SC:ResultReceiver";
         final String callerSAC =  "SAC:ResultReceiver";
 
-        doReturn(false).when(mFeatureFlags).geofenceEnhancementForBetterUx();
+        doReturn(false).when(mFeatureFlags).carrierRoamingNbIotNtn();
 
         mSatelliteControllerUT.incrementResultReceiverCount(callerSC);
         assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount());
         mSatelliteControllerUT.decrementResultReceiverCount(callerSC);
         assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount());
 
-        doReturn(true).when(mFeatureFlags).geofenceEnhancementForBetterUx();
+        doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn();
 
         mSatelliteControllerUT.incrementResultReceiverCount(callerSC);
         assertEquals(1, mSatelliteControllerUT.getResultReceiverTotalCount());
@@ -6051,4 +6365,226 @@
             return isProvisioned != null ? isProvisioned : false;
         }
     }
+
+    @Test
+    public void testGetSatelliteDataPlanForPlmn_WithEntitlement() throws Exception {
+        logd("testGetSatelliteDataPlanForPlmn_WithEntitlement");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList = new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        List<String> entitlementPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00103", "00104"})
+                        .toList();
+        List<String> barredPlmnList = new ArrayList<>();
+        Map<String, Integer> dataPlanListMap = Map.of(
+                "00101", SATELLITE_DATA_PLAN_METERED,
+                "00103", SATELLITE_DATA_PLAN_UNMETERED);
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, barredPlmnList, dataPlanListMap, new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
+
+        int dataPlanForPlmn;
+        dataPlanForPlmn = mSatelliteControllerUT.getSatelliteDataPlanForPlmn(SUB_ID, "00101");
+        assertEquals(SATELLITE_DATA_PLAN_METERED, dataPlanForPlmn);
+
+        dataPlanForPlmn = mSatelliteControllerUT.getSatelliteDataPlanForPlmn(SUB_ID, "00103");
+        assertEquals(SATELLITE_DATA_PLAN_UNMETERED, dataPlanForPlmn);
+    }
+
+    @Test
+    public void testGetSatelliteDataPlanForPlmn_WithoutEntitlement() throws Exception {
+        logd("testGetSatelliteDataPlanForPlmn_WithoutEntitlement");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList = new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        List<String> entitlementPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00103", "00104"})
+                        .toList();
+        List<String> barredPlmnList = new ArrayList<>();
+        Map<String, Integer> dataPlanListMap = new HashMap<>();
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, barredPlmnList, dataPlanListMap, new HashMap<>(),
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
+
+        int dataPlanForPlmn = mSatelliteControllerUT.getSatelliteDataPlanForPlmn(SUB_ID, "00101");
+        assertEquals(SATELLITE_DATA_PLAN_METERED, dataPlanForPlmn);
+    }
+
+    @Test
+    public void TestGetSupportedSatelliteServicesForPlmn_WithEntitlement() throws Exception {
+        logd("TestGetSupportedSatelliteServicesForPlmn_WithEntitlement");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList = new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        List<String> entitlementPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00103", "00104"})
+                        .toList();
+        List<String> barredPlmnList = new ArrayList<>();
+        Map<String, List<Integer>> serviceTypeListMap = Map.of(
+                "00101", List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_SMS),
+                "00102", List.of(SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS),
+                "00103", List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS));
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), serviceTypeListMap,
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
+
+        List<Integer> allowedServiceForPlmn;
+        allowedServiceForPlmn = mSatelliteControllerUT
+                .getSupportedSatelliteServicesForPlmn(SUB_ID, "00101");
+        assertEquals(List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_SMS), allowedServiceForPlmn);
+
+        allowedServiceForPlmn = mSatelliteControllerUT
+                .getSupportedSatelliteServicesForPlmn(SUB_ID, "00102");
+        assertEquals(List.of(SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS), allowedServiceForPlmn);
+
+        allowedServiceForPlmn = mSatelliteControllerUT
+                .getSupportedSatelliteServicesForPlmn(SUB_ID, "00103");
+        assertEquals(List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS),
+                allowedServiceForPlmn);
+    }
+
+    @Test
+    public void TestGetSupportedSatelliteServicesForPlmn_WithoutEntitlement() throws Exception {
+        logd("TestGetSupportedSatelliteServicesForPlmn_WithoutAllowedServices");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList = new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        List<String> entitlementPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00103", "00104"})
+                        .toList();
+        List<String> barredPlmnList = new ArrayList<>();
+        Map<String, Integer> dataPlanListMap =  new HashMap<>();
+        Map<String, List<Integer>> allowedServiceListMap = new HashMap<>();
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, barredPlmnList, dataPlanListMap, allowedServiceListMap,
+                new HashMap<>(), new HashMap<>(), mIIntegerConsumer);
+
+        // Verify whether the carrier config plmn list is returned with conditions below
+        // the config data plmn list : empty
+        // the carrier config plmn list : exist with services {{2}}
+        setConfigData(new ArrayList<>());
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+        PersistableBundle carrierSupportedSatelliteServicesPerProvider =
+                new PersistableBundle();
+        List<String> carrierConfigPlmnList = List.of("00101");
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                carrierConfigPlmnList.get(0), new int[]{2});
+        mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                carrierSupportedSatelliteServicesPerProvider);
+        invokeCarrierConfigChanged();
+
+        List<Integer> servicesPerPlmn;
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00101");
+        assertEquals(Arrays.asList(2).stream().sorted().toList(),
+                servicesPerPlmn.stream().sorted().toList());
+    }
+
+    @Test
+    public void testGetSupportedSatelliteDataModeForPlmn_WithEntitlement() throws Exception {
+        logd("testGetSupportedSatelliteDataModeForPlmn_WithEntitlement");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList = new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        List<String> entitlementPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00103", "00104"})
+                        .toList();
+        List<String> barredPlmnList = new ArrayList<>();
+        Map<String, Integer> dataServicePolicyMap = Map.of(
+                "00101", SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED,
+                "00102", SATELLITE_DATA_SUPPORT_ALL
+        );
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                dataServicePolicyMap, new HashMap<>(), mIIntegerConsumer);
+
+        int dataSupportModeForPlmn;
+        dataSupportModeForPlmn = mSatelliteControllerUT
+                .getSatelliteDataServicePolicyForPlmn(SUB_ID, "00101");
+        assertEquals(SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED, dataSupportModeForPlmn);
+
+        dataSupportModeForPlmn = mSatelliteControllerUT
+                .getSatelliteDataServicePolicyForPlmn(SUB_ID, "00102");
+        assertEquals(SATELLITE_DATA_SUPPORT_ALL, dataSupportModeForPlmn);
+
+    }
+
+    @Test
+    public void testGetSupportedSatelliteDataModeForPlmn_WithoutEntitlement() throws Exception {
+        logd("testGetSupportedSatelliteDataModeForPlmn_WithoutEntitlement");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList = new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        List<String> entitlementPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00103", "00104"})
+                        .toList();
+        List<String> barredPlmnList = new ArrayList<>();
+        Map<String, Integer> dataServicePolicyMap = new HashMap<>();
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(),
+                dataServicePolicyMap, new HashMap<>(), mIIntegerConsumer);
+
+        mCarrierConfigBundle.putInt(
+                CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
+                SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED);
+        int dataSupportModeForPlmn = mSatelliteControllerUT
+                .getSatelliteDataServicePolicyForPlmn(SUB_ID, "00101");
+        assertEquals(SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED, dataSupportModeForPlmn);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index 56d5731..529088b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -373,6 +373,7 @@
 
     @Test
     public void testSatelliteProvisionStateChangedBeforeTimeout() {
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
 
@@ -393,6 +394,7 @@
         assertFalse(mTestSOSMessageRecommender.isDialerNotified());
         reset(mMockSatelliteStats);
 
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
         mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -983,6 +985,8 @@
         private ComponentName mSmsAppComponent = new ComponentName(
                 DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS);
         private boolean mIsDialerNotified;
+        private boolean mProvisionState = true;
+        private boolean mSatelliteAllowedByReasons = true;
 
         /**
          * Create an instance of SatelliteSOSMessageRecommender.
@@ -1017,6 +1021,16 @@
             mIsDialerNotified = isDialerNotified;
         }
 
+        @Override
+        protected boolean updateAndGetProvisionState() {
+            return mProvisionState;
+        }
+
+        @Override
+        protected boolean isSatelliteAllowedByReasons() {
+            return mSatelliteAllowedByReasons;
+        }
+
         public boolean isTimerStarted() {
             return hasMessages(EVENT_TIME_OUT);
         }
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 8c1ae50..0d11fed 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -150,7 +150,9 @@
         when(resources.getBoolean(
                  R.bool.config_satellite_modem_support_concurrent_tn_scanning))
             .thenReturn(false);
-
+        when(resources.getBoolean(
+                 R.bool.config_satellite_allow_tn_scanning_during_satellite_session))
+            .thenReturn(true);
         when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(