Merge "Add registering_seconds and unregistered_seconds to ImsRegistrationStats" into main
diff --git a/flags/data.aconfig b/flags/data.aconfig
index c769bcc..260d983 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -1,6 +1,13 @@
 package: "com.android.internal.telephony.flags"
 
 flag {
+  name: "unthrottle_check_transport"
+  namespace: "telephony"
+  description: "Check transport when unthrottle."
+  bug: "303922311"
+}
+
+flag {
   name: "relax_ho_teardown"
   namespace: "telephony"
   description: "Relax handover tear down if the device is currently in voice call."
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 2b5ab2b..87acccc 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -13,4 +13,11 @@
   namespace: "telephony"
   description: "Whether to log MMS/SMS database access info and report anomaly when getting exception."
   bug: "275225402"
+}
+
+flag {
+  name: "enable_wps_check_api_flag"
+  namespace: "telephony"
+  description: "Enable system api isWpsCallNumber. Its an utility api to check if the dialed number is for Wireless Priority Service call."
+  bug: "304272356"
 }
\ No newline at end of file
diff --git a/flags/network.aconfig b/flags/network.aconfig
index 0c7873e..299a2a0 100644
--- a/flags/network.aconfig
+++ b/flags/network.aconfig
@@ -6,3 +6,10 @@
     description: "enabling this flag allows KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY to control N1 mode enablement"
     bug:"302033535"
 }
+
+flag {
+  name: "hide_roaming_icon"
+  namespace: "telephony"
+  description: "Allow carriers to hide the roaming (R) icon when roaming."
+  bug: "301467052"
+}
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index ecc6208..7bdf2ff 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -22,6 +22,7 @@
 import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
 import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 
+import android.annotation.NonNull;
 import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -47,6 +48,7 @@
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
 
@@ -92,10 +94,14 @@
     private static final int NR_NSA_TRACKING_INDICATIONS_ALWAYS_ON = 2;
 
     private final Phone mPhone;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     private final LocalLog mLocalLog = new LocalLog(64);
 
     private final RegistrantList mPhysicalChannelConfigRegistrants = new RegistrantList();
+    private final RegistrantList mSignalStrengthReportDecisionCallbackRegistrants =
+            new RegistrantList();
 
     private final NetworkRequest mWifiNetworkRequest =
             new NetworkRequest.Builder()
@@ -269,8 +275,9 @@
      *
      * @param phone Phone object
      */
-    public DeviceStateMonitor(Phone phone) {
+    public DeviceStateMonitor(Phone phone, @NonNull FeatureFlags featureFlags) {
         mPhone = phone;
+        mFeatureFlags = featureFlags;
         DisplayManager dm = (DisplayManager) phone.getContext().getSystemService(
                 Context.DISPLAY_SERVICE);
         dm.registerDisplayListener(mDisplayListener, null);
@@ -602,6 +609,15 @@
             // use a null message since we don't care of receiving response
             mPhone.mCi.getBarringInfo(null);
         }
+
+        // Determine whether to notify registrants about the non-terrestrial signal strength change.
+        if (mFeatureFlags.oemEnabledSatelliteFlag()) {
+            if (shouldEnableSignalStrengthReports()) {
+                mSignalStrengthReportDecisionCallbackRegistrants.notifyResult(true);
+            } else {
+                mSignalStrengthReportDecisionCallbackRegistrants.notifyResult(false);
+            }
+        }
     }
 
     /**
@@ -778,6 +794,33 @@
     }
 
     /**
+     * Register a callback to decide whether signal strength should be notified or not.
+     * @param h Handler to notify
+     * @param what msg.what when the message is delivered
+     * @param obj AsyncResult.userObj when the message is delivered
+     */
+    public void registerForSignalStrengthReportDecision(Handler h, int what, Object obj) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            Rlog.d(TAG, "oemEnabledSatelliteFlag is disabled");
+            return;
+        }
+        Registrant r = new Registrant(h, what, obj);
+        mSignalStrengthReportDecisionCallbackRegistrants.add(r);
+    }
+
+    /**
+     * Register a callback to decide whether signal strength should be notified or not.
+     * @param h Handler to notify
+     */
+    public void unregisterForSignalStrengthReportDecision(Handler h) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            Rlog.d(TAG, "oemEnabledSatelliteFlag is disabled");
+            return;
+        }
+        mSignalStrengthReportDecisionCallbackRegistrants.remove(h);
+    }
+
+    /**
      * @param msg Debug message
      * @param logIntoLocalLog True if log into the local log
      */
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java
index 567331d..945b640 100644
--- a/src/java/com/android/internal/telephony/DisplayInfoController.java
+++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -19,9 +19,11 @@
 import android.annotation.NonNull;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.telephony.AnomalyReporter;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
@@ -29,6 +31,7 @@
 import android.util.LocalLog;
 import android.util.Pair;
 
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -66,34 +69,55 @@
 
     /** Event for service state changed (roaming). */
     private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+    /** Event for carrier config changed. */
+    private static final int EVENT_CARRIER_CONFIG_CHANGED = 2;
 
-    private final Phone mPhone;
-    private final NetworkTypeController mNetworkTypeController;
-    private final RegistrantList mTelephonyDisplayInfoChangedRegistrants = new RegistrantList();
-    private @NonNull TelephonyDisplayInfo mTelephonyDisplayInfo;
-    private @NonNull ServiceState mServiceState;
+    @NonNull private final Phone mPhone;
+    @NonNull private final NetworkTypeController mNetworkTypeController;
+    @NonNull private final RegistrantList mTelephonyDisplayInfoChangedRegistrants =
+            new RegistrantList();
+    @NonNull private final FeatureFlags mFeatureFlags;
+    @NonNull private TelephonyDisplayInfo mTelephonyDisplayInfo;
+    @NonNull private ServiceState mServiceState;
+    @NonNull private PersistableBundle mConfigs;
 
-    public DisplayInfoController(Phone phone) {
+    public DisplayInfoController(@NonNull Phone phone, @NonNull FeatureFlags featureFlags) {
         mPhone = phone;
+        mFeatureFlags = featureFlags;
         mLogTag = "DIC-" + mPhone.getPhoneId();
+        mServiceState = mPhone.getServiceStateTracker().getServiceState();
+        mConfigs = new PersistableBundle();
+        try {
+            mConfigs = mPhone.getContext().getSystemService(CarrierConfigManager.class)
+                    .getConfigForSubId(mPhone.getSubId(),
+                            CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL);
+        } catch (Exception ignored) {
+            // CarrierConfigLoader might not be available yet.
+            // Once it's available, configs will be updated through the listener.
+        }
+        mPhone.getServiceStateTracker()
+                .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+        mPhone.getContext().getSystemService(CarrierConfigManager.class)
+                .registerCarrierConfigChangeListener(Runnable::run,
+                        (slotIndex, subId, carrierId, specificCarrierId) -> {
+                            if (slotIndex == mPhone.getPhoneId()) {
+                                obtainMessage(EVENT_CARRIER_CONFIG_CHANGED).sendToTarget();
+                            }
+                        });
         mTelephonyDisplayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                false);
         mNetworkTypeController = new NetworkTypeController(phone, this);
+        // EVENT_UPDATE will transition from DefaultState to the current state
+        // and update the TelephonyDisplayInfo based on the current state.
         mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
-
-        mServiceState = mPhone.getServiceStateTracker().getServiceState();
-        post(() -> {
-            mPhone.getServiceStateTracker()
-                    .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
-            updateTelephonyDisplayInfo();
-        });
     }
 
     /**
      * @return the current TelephonyDisplayInfo
      */
-    public @NonNull TelephonyDisplayInfo getTelephonyDisplayInfo() {
+    @NonNull public TelephonyDisplayInfo getTelephonyDisplayInfo() {
         return mTelephonyDisplayInfo;
     }
 
@@ -105,7 +129,7 @@
         TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(
                 mNetworkTypeController.getDataNetworkType(),
                 mNetworkTypeController.getOverrideNetworkType(),
-                mServiceState.getRoaming());
+                isRoaming());
         if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) {
             logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to "
                     + newDisplayInfo);
@@ -117,6 +141,24 @@
     }
 
     /**
+     * Determine the roaming status for icon display only.
+     * If this is {@code true}, the roaming indicator will be shown, and if this is {@code false},
+     * the roaming indicator will not be shown.
+     * To get the actual roaming status, use {@link ServiceState#getRoaming()} instead.
+     *
+     * @return Whether the device is considered roaming for display purposes.
+     */
+    private boolean isRoaming() {
+        boolean roaming = mServiceState.getRoaming();
+        if (roaming && mFeatureFlags.hideRoamingIcon()
+                && !mConfigs.getBoolean(CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL)) {
+            logl("Override roaming for display due to carrier configs.");
+            roaming = false;
+        }
+        return roaming;
+    }
+
+    /**
      * Validate the display info and trigger anomaly report if needed.
      *
      * @param displayInfo The display info to validate.
@@ -175,6 +217,13 @@
                 log("ServiceState updated, isRoaming=" + mServiceState.getRoaming());
                 updateTelephonyDisplayInfo();
                 break;
+            case EVENT_CARRIER_CONFIG_CHANGED:
+                mConfigs = mPhone.getContext().getSystemService(CarrierConfigManager.class)
+                        .getConfigForSubId(mPhone.getSubId(),
+                                CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL);
+                log("Carrier configs updated: " + mConfigs);
+                updateTelephonyDisplayInfo();
+                break;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 05bfe36..6f30fb5 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -359,12 +359,13 @@
                 .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
                         this, this.mCi);
         mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
-                .makeDeviceStateMonitor(this);
+                .makeDeviceStateMonitor(this, mFeatureFlags);
 
         // DisplayInfoController creates an OverrideNetworkTypeController, which uses
         // DeviceStateMonitor so needs to be crated after it is instantiated.
         mDisplayInfoController = mTelephonyComponentFactory.inject(
-                DisplayInfoController.class.getName()).makeDisplayInfoController(this);
+                DisplayInfoController.class.getName())
+                .makeDisplayInfoController(this, featureFlags);
 
         mDataNetworkController = mTelephonyComponentFactory.inject(
                 DataNetworkController.class.getName())
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 6b5b266..50cf844 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -727,6 +727,28 @@
     }
 
     /**
+     * Determines if the carrier prefers to use an in service sim for a normal routed emergency
+     * call.
+     * @return true when carrier config
+     * {@link CarrierConfigManager#KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL}
+     * is true.
+     */
+    public boolean shouldPreferInServiceSimForNormalRoutedEmergencyCall() {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle b = configManager.getConfigForSubId(getSubId(), CarrierConfigManager
+                .KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL);
+        if (b != null) {
+            return b.getBoolean(CarrierConfigManager
+                            .KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL,
+                    false);
+        } else {
+            // Default value set in CarrierConfigManager
+            return false;
+        }
+    }
+
+    /**
      * When overridden the derived class needs to call
      * super.handleMessage(msg) so this method has a
      * a chance to process the message.
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 2816e2b..c2c29f8 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -983,8 +983,11 @@
         dpi.persistent = dp.isPersistent();
         dpi.preferred = dp.isPreferred();
         dpi.alwaysOn = false;
+        dpi.infrastructureBitmap = android.hardware.radio.data.DataProfileInfo
+                .INFRASTRUCTURE_CELLULAR;
         if (dp.getApnSetting() != null) {
             dpi.alwaysOn = dp.getApnSetting().isAlwaysOn();
+            dpi.infrastructureBitmap = dp.getApnSetting().getInfrastructureBitmask();
         }
         dpi.trafficDescriptor = convertToHalTrafficDescriptorAidl(dp.getTrafficDescriptor());
 
@@ -1022,6 +1025,7 @@
                 .setRoamingProtocol(dpi.roamingProtocol)
                 .setUser(dpi.user)
                 .setAlwaysOn(dpi.alwaysOn)
+                .setInfrastructureBitmask(dpi.infrastructureBitmap)
                 .build();
 
         TrafficDescriptor td;
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index b756675..8b832dd 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -406,8 +406,12 @@
         return new AppSmsManager(context);
     }
 
-    public DeviceStateMonitor makeDeviceStateMonitor(Phone phone) {
-        return new DeviceStateMonitor(phone);
+    /**
+     * Create a DeviceStateMonitor.
+     */
+    public DeviceStateMonitor makeDeviceStateMonitor(Phone phone,
+            @NonNull FeatureFlags featureFlags) {
+        return new DeviceStateMonitor(phone, featureFlags);
     }
 
     /**
@@ -448,8 +452,8 @@
     /**
      * Create a new DisplayInfoController.
      */
-    public DisplayInfoController makeDisplayInfoController(Phone phone) {
-        return new DisplayInfoController(phone);
+    public DisplayInfoController makeDisplayInfoController(Phone phone, FeatureFlags featureFlags) {
+        return new DisplayInfoController(phone, featureFlags);
     }
 
     /**
@@ -498,15 +502,17 @@
      * @param dataServiceManager Data service manager instance.
      * @param looper The looper to be used by the handler. Currently the handler thread is the phone
      * process's main thread.
+     * @param featureFlags Feature flags controlling which feature is enabled.     *
      * @param callback Callback for passing events back to data network controller.
      * @return The data profile manager instance.
      */
     public @NonNull DataProfileManager makeDataProfileManager(@NonNull Phone phone,
             @NonNull DataNetworkController dataNetworkController,
             @NonNull DataServiceManager dataServiceManager, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull DataProfileManager.DataProfileManagerCallback callback) {
         return new DataProfileManager(phone, dataNetworkController, dataServiceManager, looper,
-                callback);
+                featureFlags, callback);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 6b410ce..f3bb6c1 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -3308,7 +3308,8 @@
                 && !mAttachedNetworkRequestList.isEmpty()) {
             TelephonyNetworkRequest networkRequest = mAttachedNetworkRequestList.get(0);
             DataProfile dataProfile = mDataNetworkController.getDataProfileManager()
-                    .getDataProfileForNetworkRequest(networkRequest, targetNetworkType, false);
+                    .getDataProfileForNetworkRequest(networkRequest, targetNetworkType,
+                            mPhone.getServiceState().isUsingNonTerrestrialNetwork(), false);
             // Some carriers have different profiles between cellular and IWLAN. We need to
             // dynamically switch profile, but only when those profiles have same APN name.
             if (dataProfile != null && dataProfile.getApnSetting() != null
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 3073f49..5bdef27 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -879,6 +879,7 @@
                 DataProfileManager.class.getName())
                 .makeDataProfileManager(mPhone, this, mDataServiceManagers
                                 .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), looper,
+                        mFeatureFlags,
                         new DataProfileManagerCallback(this::post) {
                             @Override
                             public void onDataProfilesChanged() {
@@ -899,7 +900,7 @@
                     }
                 });
         mDataRetryManager = new DataRetryManager(mPhone, this,
-                mDataServiceManagers, looper,
+                mDataServiceManagers, looper, mFeatureFlags,
                 new DataRetryManagerCallback(this::post) {
                     @Override
                     public void onDataNetworkSetupRetry(
@@ -1328,6 +1329,7 @@
                 DataProfile candidate = mDataProfileManager
                         .getDataProfileForNetworkRequest(requestList.getFirst(),
                                 TelephonyManager.NETWORK_TYPE_IWLAN,
+                                mServiceState.isUsingNonTerrestrialNetwork(),
                                 false/*ignorePermanentFailure*/);
                 if (candidate != null && !dataNetwork.getDataProfile().equals(candidate)) {
                     logv("But skipped because found better data profile " + candidate
@@ -1491,7 +1493,8 @@
         if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
             evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST);
             evaluation.setCandidateDataProfile(mDataProfileManager.getDataProfileForNetworkRequest(
-                    networkRequest, getDataNetworkType(transport), true));
+                    networkRequest, getDataNetworkType(transport),
+                    mServiceState.isUsingNonTerrestrialNetwork(), true));
             networkRequest.setEvaluation(evaluation);
             log(evaluation.toString());
             return evaluation;
@@ -1647,6 +1650,7 @@
         }
         DataProfile dataProfile = mDataProfileManager
                 .getDataProfileForNetworkRequest(networkRequest, networkType,
+                        mServiceState.isUsingNonTerrestrialNetwork(),
                         // If the evaluation is due to environmental changes, then we should ignore
                         // the permanent failure reached earlier.
                         reason.isConditionBased());
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 23029e3..273dc8b 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -48,6 +48,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -113,6 +114,9 @@
     /** SIM state. */
     private @SimState int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
 
+    /** Feature flags controlling which feature is enabled. */
+    private final @NonNull FeatureFlags mFeatureFlags;
+
     /**
      * Data profile manager callback. This should be only used by {@link DataNetworkController}.
      */
@@ -140,15 +144,18 @@
      * @param dataServiceManager WWAN data service manager.
      * @param looper The looper to be used by the handler. Currently the handler thread is the
      * phone process's main thread.
+     * @param featureFlags Feature flags controlling which feature is enabled.
      * @param callback Data profile manager callback.
      */
     public DataProfileManager(@NonNull Phone phone,
             @NonNull DataNetworkController dataNetworkController,
             @NonNull DataServiceManager dataServiceManager, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags,
             @NonNull DataProfileManagerCallback callback) {
         super(looper);
         mPhone = phone;
         mLogTag = "DPM-" + mPhone.getPhoneId();
+        mFeatureFlags = featureFlags;
         mDataNetworkController = dataNetworkController;
         mWwanDataServiceManager = dataServiceManager;
         mDataConfigManager = dataNetworkController.getDataConfigManager();
@@ -626,17 +633,18 @@
      *
      * @param networkRequest The network request.
      * @param networkType The current data network type.
+     * @param isNtn {@code true} if the device is currently attached to non-terrestrial network.
      * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}.
      * This should be set to true for condition-based retry/setup.
      * @return The data profile. {@code null} if can't find any satisfiable data profile.
      */
     public @Nullable DataProfile getDataProfileForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
-            boolean ignorePermanentFailure) {
+            boolean isNtn, boolean ignorePermanentFailure) {
         ApnSetting apnSetting = null;
         if (networkRequest.hasAttribute(TelephonyNetworkRequest
                 .CAPABILITY_ATTRIBUTE_APN_SETTING)) {
-            apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType,
+            apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType, isNtn,
                     ignorePermanentFailure);
         }
 
@@ -696,32 +704,56 @@
      *
      * @param networkRequest The network request.
      * @param networkType The current data network type.
+     * @param isNtn {@code true} if the device is currently attached to non-terrestrial network.
      * @param ignorePermanentFailure {@code true} to ignore {@link ApnSetting#getPermanentFailed()}.
      * This should be set to true for condition-based retry/setup.
      * @return The APN setting. {@code null} if can't find any satisfiable data profile.
      */
     private @Nullable ApnSetting getApnSettingForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
-            boolean ignorePermanentFailure) {
+            boolean isNtn, boolean ignorePermanentFailure) {
         if (!networkRequest.hasAttribute(
                 TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_APN_SETTING)) {
             loge("Network request does not have APN setting attribute.");
             return null;
         }
 
-        // If the preferred data profile can be used, always use it if it can satisfy the network
-        // request with current network type (even though it's been marked as permanent failed.)
-        if (mPreferredDataProfile != null
-                && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
-                && mPreferredDataProfile.getApnSetting() != null
-                && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)) {
-            if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
-                    .getPermanentFailed()) {
-                return mPreferredDataProfile.getApnSetting();
+        if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            // If the preferred data profile can be used, always use it if it can satisfy the
+            // network request with current network type (even though it's been marked as permanent
+            // failed.)
+            if (mPreferredDataProfile != null
+                    && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
+                    && mPreferredDataProfile.getApnSetting() != null
+                    && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)
+                    && ((isNtn && mPreferredDataProfile.getApnSetting().isForInfrastructure(
+                            ApnSetting.INFRASTRUCTURE_SATELLITE))
+                    || (!isNtn && mPreferredDataProfile.getApnSetting().isForInfrastructure(
+                            ApnSetting.INFRASTRUCTURE_CELLULAR)))) {
+                if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
+                        .getPermanentFailed()) {
+                    return mPreferredDataProfile.getApnSetting();
+                }
+                log("The preferred data profile is permanently failed. Only condition based "
+                        + "retry can happen.");
+                return null;
             }
-            log("The preferred data profile is permanently failed. Only condition based retry "
-                    + "can happen.");
-            return null;
+        } else {
+            // If the preferred data profile can be used, always use it if it can satisfy the
+            // network request with current network type (even though it's been marked as permanent
+            // failed.)
+            if (mPreferredDataProfile != null
+                    && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
+                    && mPreferredDataProfile.getApnSetting() != null
+                    && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)) {
+                if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
+                        .getPermanentFailed()) {
+                    return mPreferredDataProfile.getApnSetting();
+                }
+                log("The preferred data profile is permanently failed. Only condition based "
+                        + "retry can happen.");
+                return null;
+            }
         }
 
         // Filter out the data profile that can't satisfy the request.
@@ -743,8 +775,22 @@
 
         // Check if the remaining data profiles can used in current data network type.
         dataProfiles = dataProfiles.stream()
-                .filter(dp -> dp.getApnSetting() != null
-                        && dp.getApnSetting().canSupportNetworkType(networkType))
+                .filter((dp) -> {
+                    if (dp.getApnSetting() == null) return false;
+                    if (!dp.getApnSetting().canSupportNetworkType(networkType)) return false;
+                    if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
+                        if (isNtn && !dp.getApnSetting().isForInfrastructure(
+                                ApnSetting.INFRASTRUCTURE_SATELLITE)) {
+                            return false;
+                        }
+                        if (!isNtn && !dp.getApnSetting().isForInfrastructure(
+                                ApnSetting.INFRASTRUCTURE_CELLULAR)) {
+                            return false;
+                        }
+                    }
+
+                    return true;
+                })
                 .collect(Collectors.toList());
         if (dataProfiles.size() == 0) {
             log("Can't find any data profile for network type "
@@ -818,7 +864,8 @@
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                         .build(), mPhone);
-        return getDataProfileForNetworkRequest(networkRequest, networkType, true) != null;
+        return getDataProfileForNetworkRequest(networkRequest, networkType,
+                mPhone.getServiceState().isUsingNonTerrestrialNetwork(), true) != null;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index 4465fbb..754400e 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -51,6 +51,7 @@
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -141,6 +142,9 @@
     /** The phone instance. */
     private final @NonNull Phone mPhone;
 
+    /** Featureflags. */
+    private final @NonNull FeatureFlags mFlags;
+
     /** The RIL instance. */
     private final @NonNull CommandsInterface mRil;
 
@@ -952,10 +956,12 @@
     public DataRetryManager(@NonNull Phone phone,
             @NonNull DataNetworkController dataNetworkController,
             @NonNull SparseArray<DataServiceManager> dataServiceManagers,
-            @NonNull Looper looper, @NonNull DataRetryManagerCallback dataRetryManagerCallback) {
+            @NonNull Looper looper, @NonNull FeatureFlags flags,
+            @NonNull DataRetryManagerCallback dataRetryManagerCallback) {
         super(looper);
         mPhone = phone;
         mRil = phone.mCi;
+        mFlags = flags;
         mLogTag = "DRM-" + mPhone.getPhoneId();
         mDataRetryManagerCallbacks.add(dataRetryManagerCallback);
 
@@ -1480,9 +1486,12 @@
         DataThrottlingEntry entry = new DataThrottlingEntry(dataProfile, networkRequestList,
                 dataNetwork, transport, retryType, expirationTime);
         // Remove previous entry that contains the same data profile. Therefore it should always
-        // contain at maximum all the distinct data profiles of the current subscription.
+        // contain at maximu all the distinct data profiles of the current subscription times each
+        // transport.
         mDataThrottlingEntries.removeIf(
-                throttlingEntry -> dataProfile.equals(throttlingEntry.dataProfile));
+                throttlingEntry -> dataProfile.equals(throttlingEntry.dataProfile)
+                        && (!mFlags.unthrottleCheckTransport()
+                        || throttlingEntry.transport == transport));
 
         if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) {
             // If we don't see the anomaly report after U release, we should remove this check for
@@ -1544,7 +1553,8 @@
             // in DataProfileInfo.aidl), so we need to get the equivalent data profile from data
             // profile manager.
             Stream<DataThrottlingEntry> stream = mDataThrottlingEntries.stream();
-            stream = stream.filter(entry -> entry.expirationTimeMillis > now);
+            stream = stream.filter(entry -> entry.expirationTimeMillis > now
+                    && (!mFlags.unthrottleCheckTransport() || entry.transport == transport));
             if (dataProfile.getApnSetting() != null) {
                 stream = stream
                         .filter(entry -> entry.dataProfile.getApnSetting() != null)
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index fc0c4f1..259b482 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -90,6 +90,8 @@
                 return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH;
             case "PRIORITIZE_LATENCY":
                 return NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
+            case "RCS":
+                return NetworkCapabilities.NET_CAPABILITY_RCS;
             default:
                 return -1;
         }
@@ -254,6 +256,8 @@
                 return ApnSetting.TYPE_VSIM;
             case NetworkCapabilities.NET_CAPABILITY_BIP:
                 return ApnSetting.TYPE_BIP;
+            case NetworkCapabilities.NET_CAPABILITY_RCS:
+                return ApnSetting.TYPE_RCS;
             default:
                 return ApnSetting.TYPE_NONE;
         }
@@ -295,6 +299,8 @@
                 return NetworkCapabilities.NET_CAPABILITY_VSIM;
             case ApnSetting.TYPE_ENTERPRISE:
                 return NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+            case ApnSetting.TYPE_RCS:
+                return NetworkCapabilities.NET_CAPABILITY_RCS;
             default:
                 return -1;
         }
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index ab759e9..36e6587 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -1546,7 +1546,7 @@
         // If validation feature is not supported, set it directly. Otherwise,
         // start validation on the subscription first.
         if (!mValidator.isValidationFeatureSupported()) {
-            setAutoSelectedDataSubIdInternal(subIdToValidate);
+            setAutoSelectedDataSubIdInternal(subId);
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
             return;
         }
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
index b334b89..2668302 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -130,7 +130,9 @@
             new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY,
                     CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID),
             new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH,
-                    CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID)
+                    CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_OS_APP_ID),
+            new SimpleImmutableEntry<>(NetworkCapabilities.NET_CAPABILITY_RCS,
+                CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN)
     );
 
     /** The phone instance. */
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
index 9a75b43..5ef8b8a 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -21,7 +21,6 @@
 import android.os.AsyncResult;
 import android.os.CancellationSignal;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
@@ -44,8 +43,6 @@
 import java.io.PrintWriter;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
 import java.util.function.Consumer;
 
 
@@ -74,42 +71,63 @@
     private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback {
         @Override
         public void onCreated(@NonNull DomainSelector selector) {
-            mDomainSelector = selector;
-            DomainSelectionConnection.this.onCreated();
+            synchronized (mLock) {
+                mDomainSelector = selector;
+                if (mDisposed) {
+                    mDomainSelector.cancelSelection();
+                    return;
+                }
+                DomainSelectionConnection.this.onCreated();
+            }
         }
 
         @Override
         public void onWlanSelected(boolean useEmergencyPdn) {
-            DomainSelectionConnection.this.onWlanSelected(useEmergencyPdn);
+            synchronized (mLock) {
+                if (mDisposed) return;
+                DomainSelectionConnection.this.onWlanSelected(useEmergencyPdn);
+            }
         }
 
         @Override
         public @NonNull WwanSelectorCallback onWwanSelected() {
-            if (mWwanSelectorCallback == null) {
-                mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
+            synchronized (mLock) {
+                if (mWwanSelectorCallback == null) {
+                    mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
+                }
+                if (mDisposed) {
+                    return mWwanSelectorCallback;
+                }
+                DomainSelectionConnection.this.onWwanSelected();
+                return mWwanSelectorCallback;
             }
-            DomainSelectionConnection.this.onWwanSelected();
-            return mWwanSelectorCallback;
         }
 
         @Override
         public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) {
-            if (mWwanSelectorCallback == null) {
-                mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
+            synchronized (mLock) {
+                if (mDisposed) return;
+                if (mWwanSelectorCallback == null) {
+                    mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
+                }
+                initHandler();
+                mHandler.post(() -> {
+                    synchronized (mLock) {
+                        if (mDisposed) return;
+                        DomainSelectionConnection.this.onWwanSelected();
+                        consumer.accept(mWwanSelectorCallback);
+                    }
+                });
             }
-            if (mWwanSelectedExecutor == null) {
-                mWwanSelectedExecutor = Executors.newSingleThreadExecutor();
-            }
-            mWwanSelectedExecutor.execute(() -> {
-                DomainSelectionConnection.this.onWwanSelected();
-                consumer.accept(mWwanSelectorCallback);
-            });
         }
 
         @Override
         public void onSelectionTerminated(int cause) {
-            DomainSelectionConnection.this.onSelectionTerminated(cause);
-            dispose();
+            synchronized (mLock) {
+                if (mDisposed) return;
+                DomainSelectionConnection.this.onSelectionTerminated(cause);
+                dispose();
+            }
         }
     }
 
@@ -120,22 +138,38 @@
         public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
                 @EmergencyScanType int scanType, @NonNull CancellationSignal signal,
                 @NonNull Consumer<EmergencyRegResult> consumer) {
-            if (signal != null) signal.setOnCancelListener(this);
-            mResultCallback = consumer;
-            initHandler();
-            DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
-                    preferredNetworks.stream().mapToInt(Integer::intValue).toArray(), scanType);
+            synchronized (mLock) {
+                if (mDisposed) return;
+                if (signal != null) signal.setOnCancelListener(this);
+                mResultCallback = consumer;
+                initHandler();
+                mHandler.post(() -> {
+                    synchronized (mLock) {
+                        DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
+                                preferredNetworks.stream().mapToInt(Integer::intValue).toArray(),
+                                scanType);
+                    }
+                });
+            }
         }
 
         @Override
         public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
                 boolean useEmergencyPdn) {
-            DomainSelectionConnection.this.onDomainSelected(domain, useEmergencyPdn);
+            synchronized (mLock) {
+                if (mDisposed) return;
+                DomainSelectionConnection.this.onDomainSelected(domain, useEmergencyPdn);
+            }
         }
 
         @Override
         public void onCancel() {
-            DomainSelectionConnection.this.onCancel();
+            synchronized (mLock) {
+                if (mDisposed || mHandler == null) return;
+                mHandler.post(() -> {
+                    DomainSelectionConnection.this.onCancel();
+                });
+            }
         }
     }
 
@@ -156,7 +190,7 @@
                     if (DBG) logd("EVENT_EMERGENCY_NETWORK_SCAN_RESULT result=" + regResult);
                     CompletableFuture.runAsync(
                             () -> mResultCallback.accept(regResult),
-                            mController.getDomainSelectionServiceExecutor()).join();
+                            mController.getDomainSelectionServiceExecutor());
                     break;
                 case EVENT_QUALIFIED_NETWORKS_CHANGED:
                     onQualifiedNetworksChanged();
@@ -170,6 +204,8 @@
 
     protected String mTag = "DomainSelectionConnection";
 
+    private boolean mDisposed = false;
+    private final Object mLock = new Object();
     private final LocalLog mLocalLog = new LocalLog(30);
     private final @NonNull TransportSelectorCallback mTransportSelectorCallback;
 
@@ -196,15 +232,13 @@
     /** The attributes required to determine the domain. */
     private @Nullable DomainSelectionService.SelectionAttributes mSelectionAttributes;
 
-    private @Nullable Looper mLooper;
+    private final @NonNull Looper mLooper;
     protected @Nullable DomainSelectionConnectionHandler mHandler;
     private boolean mRegisteredRegistrant;
     private boolean mIsWaitingForScanResult;
 
     private @NonNull AndroidFuture<Integer> mOnComplete;
 
-    private @Nullable Executor mWwanSelectedExecutor;
-
     /**
      * Creates an instance.
      *
@@ -220,6 +254,7 @@
         mPhone = phone;
         mSelectorType = selectorType;
         mIsEmergency = isEmergency;
+        mLooper = Looper.getMainLooper();
 
         mTransportSelectorCallback = new TransportSelectorCallbackWrapper();
         mOnComplete = new AndroidFuture<>();
@@ -323,6 +358,8 @@
     public void onRequestEmergencyNetworkScan(
             @NonNull @RadioAccessNetworkType int[] preferredNetworks,
             @EmergencyScanType int scanType) {
+        if (mHandler == null) return;
+
         // Can be overridden if required
         if (!mRegisteredRegistrant) {
             mPhone.registerForEmergencyNetworkScan(mHandler,
@@ -376,9 +413,12 @@
      * to clean up all ongoing operations with the framework.
      */
     public void cancelSelection() {
-        if (mDomainSelector == null) return;
-        mDomainSelector.cancelSelection();
-        dispose();
+        synchronized (mLock) {
+            if (mDomainSelector != null) {
+                mDomainSelector.cancelSelection();
+            }
+            dispose();
+        }
     }
 
     /**
@@ -400,9 +440,12 @@
      * Finishes the selection procedure and cleans everything up.
      */
     public void finishSelection() {
-        if (mDomainSelector == null) return;
-        mDomainSelector.finishSelection();
-        dispose();
+        synchronized (mLock) {
+            if (mDomainSelector != null) {
+                mDomainSelector.finishSelection();
+            }
+            dispose();
+        }
     }
 
     /** Indicates that the service connection has been removed. */
@@ -412,23 +455,18 @@
     }
 
     private void dispose() {
+        mDisposed = true;
         if (mRegisteredRegistrant) {
             mPhone.unregisterForEmergencyNetworkScan(mHandler);
             mRegisteredRegistrant = false;
         }
         onCancel(true);
         mController.removeConnection(this);
-        if (mLooper != null) mLooper.quitSafely();
-        mLooper = null;
+        if (mHandler != null) mHandler.removeCallbacksAndMessages(null);
         mHandler = null;
     }
 
     protected void initHandler() {
-        if (mLooper == null) {
-            HandlerThread handlerThread = new HandlerThread(mTag);
-            handlerThread.start();
-            mLooper = handlerThread.getLooper();
-        }
         if (mHandler == null) mHandler = new DomainSelectionConnectionHandler(mLooper);
     }
 
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 9b44001..e2418c5 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -935,6 +935,23 @@
     }
 
     /**
+     * Get a list of the {@link EmergencyNumber}s that have the corresponding emergency number.
+     * Note: {@link #getEmergencyNumber(String)} assumes there is ONLY one record for a phone number
+     * when in reality there CAN be multiple instances if the same number is reported by the radio
+     * for a specific mcc and the emergency number database specifies the number without an mcc
+     * specified.
+     *
+     * @param emergencyNumber the emergency number to find.
+     * @return the list of emergency numbers matching.
+     */
+    public List<EmergencyNumber> getEmergencyNumbers(String emergencyNumber) {
+        final String toFind = PhoneNumberUtils.stripSeparators(emergencyNumber);
+        return getEmergencyNumberList().stream()
+                .filter(num -> num.getNumber().equals(toFind))
+                .toList();
+    }
+
+    /**
      * Get the emergency service categories for the corresponding emergency number. The only
      * trusted sources for the categories are the
      * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} and
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 3099eb8..896063f 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -18,6 +18,8 @@
 
 import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 import static android.telephony.SubscriptionManager.isValidSubscriptionId;
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
@@ -55,10 +57,12 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteStateCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
@@ -70,6 +74,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.DeviceStateMonitor;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.flags.FeatureFlags;
@@ -138,6 +143,11 @@
     private static final int EVENT_SET_SATELLITE_PLMN_INFO_DONE = 29;
     private static final int CMD_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE = 30;
     private static final int EVENT_EVALUATE_SATELLITE_ATTACH_RESTRICTION_CHANGE_DONE = 31;
+    private static final int CMD_REQUEST_NTN_SIGNAL_STRENGTH = 32;
+    private static final int EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE = 33;
+    private static final int EVENT_NTN_SIGNAL_STRENGTH_CHANGED = 34;
+    private static final int CMD_START_SENDING_NTN_SIGNAL_STRENGTH = 35;
+    private static final int EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING = 36;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -150,6 +160,7 @@
     @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
     private final CommandsInterface mCi;
     private ContentResolver mContentResolver = null;
+    private final DeviceStateMonitor mDSM;
 
     private final Object mRadioStateLock = new Object();
 
@@ -187,6 +198,7 @@
             new AtomicBoolean(false);
     private final AtomicBoolean mRegisteredForSatelliteModemStateChangedWithSatelliteService =
             new AtomicBoolean(false);
+    private final AtomicBoolean mRegisteredForNtnSignalStrengthChanged = new AtomicBoolean(false);
     /**
      * Map key: subId, value: callback to get error code of the provision request.
      */
@@ -198,6 +210,12 @@
      */
     private final ConcurrentHashMap<IBinder, ISatelliteProvisionStateCallback>
             mSatelliteProvisionStateChangedListeners = new ConcurrentHashMap<>();
+    /**
+     * Map key: binder of the callback, value: callback to receive non-terrestrial signal strength
+     * state changed events.
+     */
+    private final ConcurrentHashMap<IBinder, INtnSignalStrengthCallback>
+            mNtnSignalStrengthChangedListeners = new ConcurrentHashMap<>();
     private final Object mIsSatelliteSupportedLock = new Object();
     @GuardedBy("mIsSatelliteSupportedLock")
     private Boolean mIsSatelliteSupported = null;
@@ -215,6 +233,10 @@
     private final Object mNeedsSatellitePointingLock = new Object();
     @GuardedBy("mNeedsSatellitePointingLock")
     private boolean mNeedsSatellitePointing = false;
+    private final Object mNtnSignalsStrengthLock = new Object();
+    @GuardedBy("mNtnSignalsStrengthLock")
+    private NtnSignalStrength mNtnSignalStrength =
+            new NtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE);
     /** Key: subId, value: (key: PLMN, value: set of
      * {@link android.telephony.NetworkRegistrationInfo.ServiceType})
      */
@@ -279,6 +301,7 @@
         mFeatureFlags = featureFlags;
         Phone phone = SatelliteServiceUtils.getPhone();
         mCi = phone.mCi;
+        mDSM = phone.getDeviceStateMonitor();
         // Create the SatelliteModemInterface singleton, which is used to manage connections
         // to the satellite service and HAL interface.
         mSatelliteModemInterface = SatelliteModemInterface.make(mContext, this);
@@ -333,6 +356,8 @@
                         handleCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
         mCarrierConfigManager.registerCarrierConfigChangeListener(
                         new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
+        mDSM.registerForSignalStrengthReportDecision(this, CMD_START_SENDING_NTN_SIGNAL_STRENGTH,
+                null);
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -1054,6 +1079,91 @@
                 break;
             }
 
+            case CMD_REQUEST_NTN_SIGNAL_STRENGTH: {
+                logd("CMD_REQUEST_NTN_SIGNAL_STRENGTH");
+                request = (SatelliteControllerHandlerRequest) msg.obj;
+                onCompleted = obtainMessage(EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE, request);
+                mSatelliteModemInterface.requestNtnSignalStrength(onCompleted);
+                break;
+            }
+
+            case EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE: {
+                ar = (AsyncResult) msg.obj;
+                request = (SatelliteControllerHandlerRequest) ar.userObj;
+                ResultReceiver result = (ResultReceiver) request.argument;
+                int errorCode =  SatelliteServiceUtils.getSatelliteError(ar,
+                        "requestNtnSignalStrength");
+                if (errorCode == SATELLITE_RESULT_SUCCESS) {
+                    NtnSignalStrength ntnSignalStrength = (NtnSignalStrength) ar.result;
+                    if (ntnSignalStrength != null) {
+                        synchronized (mNtnSignalsStrengthLock) {
+                            mNtnSignalStrength = ntnSignalStrength;
+                        }
+                        Bundle bundle = new Bundle();
+                        bundle.putParcelable(KEY_NTN_SIGNAL_STRENGTH, ntnSignalStrength);
+                        result.send(SATELLITE_RESULT_SUCCESS, bundle);
+                    } else {
+                        synchronized (mNtnSignalsStrengthLock) {
+                            if (mNtnSignalStrength.getLevel() != NTN_SIGNAL_STRENGTH_NONE) {
+                                mNtnSignalStrength = new NtnSignalStrength(
+                                        NTN_SIGNAL_STRENGTH_NONE);
+                            }
+                        }
+                        loge("EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE: ntnSignalStrength is null");
+                        result.send(SatelliteManager.SATELLITE_RESULT_REQUEST_FAILED, null);
+                    }
+                } else {
+                    synchronized (mNtnSignalsStrengthLock) {
+                        if (mNtnSignalStrength.getLevel() != NTN_SIGNAL_STRENGTH_NONE) {
+                            mNtnSignalStrength = new NtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE);
+                        }
+                    }
+                    result.send(errorCode, null);
+                }
+                break;
+            }
+
+            case EVENT_NTN_SIGNAL_STRENGTH_CHANGED: {
+                ar = (AsyncResult) msg.obj;
+                if (ar.result == null) {
+                    loge("EVENT_NTN_SIGNAL_STRENGTH_CHANGED: result is null");
+                } else {
+                    handleEventNtnSignalStrengthChanged((NtnSignalStrength) ar.result);
+                }
+                break;
+            }
+
+            case CMD_START_SENDING_NTN_SIGNAL_STRENGTH: {
+                ar = (AsyncResult) msg.obj;
+                boolean shouldReport = (boolean) ar.result;
+                logd("CMD_START_SENDING_NTN_SIGNAL_STRENGTH: shouldReport=" + shouldReport);
+                request = new SatelliteControllerHandlerRequest(shouldReport,
+                        SatelliteServiceUtils.getPhone());
+                if (SATELLITE_RESULT_SUCCESS != evaluateOemSatelliteRequestAllowed(true)) {
+                    return;
+                }
+                onCompleted = obtainMessage(EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING,
+                        request);
+                if (shouldReport) {
+                    mSatelliteModemInterface.startSendingNtnSignalStrength(onCompleted);
+                } else {
+                    mSatelliteModemInterface.stopSendingNtnSignalStrength(onCompleted);
+                }
+                break;
+            }
+
+            case EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING: {
+                ar = (AsyncResult) msg.obj;
+                request = (SatelliteControllerHandlerRequest) ar.userObj;
+                int errorCode =  SatelliteServiceUtils.getSatelliteError(ar,
+                        "EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING");
+                if (errorCode != SATELLITE_RESULT_SUCCESS) {
+                    loge(((boolean) request.argument ? "startSendingNtnSignalStrength"
+                            : "stopSendingNtnSignalStrength") + "returns " + errorCode);
+                }
+                break;
+            }
+
             default:
                 Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                         msg.what);
@@ -1686,6 +1796,8 @@
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             result.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+            logd("addSatelliteAttachRestrictionForCarrier: carrierEnabledSatelliteFlag is "
+                    + "disabled");
             return;
         }
 
@@ -1723,6 +1835,8 @@
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             result.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+            logd("removeSatelliteAttachRestrictionForCarrier: carrierEnabledSatelliteFlag is "
+                    + "disabled");
             return;
         }
 
@@ -1765,6 +1879,75 @@
     }
 
     /**
+     * Request to get the signal strength of the satellite connection.
+     *
+     * @param subId The subId of the subscription to request for.
+     * @param result Result receiver to get the error code of the request and the current signal
+     * strength of the satellite connection.
+     */
+    public void requestNtnSignalStrength(int subId, @NonNull ResultReceiver result) {
+        if (DBG) logd("requestNtnSignalStrength()");
+
+        int error = evaluateOemSatelliteRequestAllowed(true);
+        if (error != SATELLITE_RESULT_SUCCESS) {
+            result.send(error, null);
+            return;
+        }
+
+        /* In case cache is available, it is not needed to request non-terrestrial signal strength
+        to modem */
+        synchronized (mNtnSignalsStrengthLock) {
+            if (mNtnSignalStrength.getLevel() != NTN_SIGNAL_STRENGTH_NONE) {
+                Bundle bundle = new Bundle();
+                bundle.putParcelable(KEY_NTN_SIGNAL_STRENGTH, mNtnSignalStrength);
+                result.send(SATELLITE_RESULT_SUCCESS, bundle);
+                return;
+            }
+        }
+
+        Phone phone = SatelliteServiceUtils.getPhone();
+        sendRequestAsync(CMD_REQUEST_NTN_SIGNAL_STRENGTH, result, phone);
+    }
+
+    /**
+     * Registers for NTN signal strength changed from satellite modem.
+     *
+     * @param subId The subId of the subscription to request for.
+     * @param callback The callback to handle the non-terrestrial network signal strength changed
+     * event.
+     *
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
+     */
+    @SatelliteManager.SatelliteResult public int registerForNtnSignalStrengthChanged(
+            int subId, @NonNull INtnSignalStrengthCallback callback) {
+        if (DBG) logd("registerForNtnSignalStrengthChanged()");
+
+        int error = evaluateOemSatelliteRequestAllowed(true);
+        if (error != SATELLITE_RESULT_SUCCESS) return error;
+
+        mNtnSignalStrengthChangedListeners.put(callback.asBinder(), callback);
+        return SATELLITE_RESULT_SUCCESS;
+    }
+
+    /**
+     * Unregisters for NTN signal strength changed from satellite modem.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param subId The subId of the subscription to unregister for provision state changed.
+     * @param callback The callback that was passed to
+     * {@link #registerForNtnSignalStrengthChanged(int, INtnSignalStrengthCallback)}
+     */
+    public void unregisterForNtnSignalStrengthChanged(
+            int subId, @NonNull INtnSignalStrengthCallback callback) {
+        if (DBG) logd("unregisterForNtnSignalStrengthChanged()");
+
+        int error = evaluateOemSatelliteRequestAllowed(true);
+        if (error == SATELLITE_RESULT_SUCCESS) {
+            mNtnSignalStrengthChangedListeners.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.
@@ -1907,6 +2090,7 @@
      */
     public void onCellularRadioPowerOffRequested() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("onCellularRadioPowerOffRequested: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
@@ -1987,6 +2171,7 @@
      */
     public boolean isSatelliteAttachRequired() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("isSatelliteAttachRequired: oemEnabledSatelliteFlag is disabled");
             return false;
         }
 
@@ -2195,6 +2380,7 @@
             registerForSatelliteProvisionStateChanged();
             registerForPendingDatagramCount();
             registerForSatelliteModemStateChanged();
+            registerForNtnSignalStrengthChanged();
 
             requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                     new ResultReceiver(this) {
@@ -2263,6 +2449,21 @@
         }
     }
 
+    private void registerForNtnSignalStrengthChanged() {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("registerForNtnSignalStrengthChanged: oemEnabledSatelliteFlag is disabled");
+            return;
+        }
+
+        if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
+            if (!mRegisteredForNtnSignalStrengthChanged.get()) {
+                mSatelliteModemInterface.registerForNtnSignalStrengthChanged(
+                        this, EVENT_NTN_SIGNAL_STRENGTH_CHANGED, null);
+                mRegisteredForNtnSignalStrengthChanged.set(true);
+            }
+        }
+    }
+
     private void handleEventSatelliteProvisionStateChanged(boolean provisioned) {
         logd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
 
@@ -2270,16 +2471,16 @@
             mIsSatelliteProvisioned = provisioned;
         }
 
-        List<ISatelliteProvisionStateCallback> toBeRemoved = new ArrayList<>();
+        List<ISatelliteProvisionStateCallback> deadCallersList = new ArrayList<>();
         mSatelliteProvisionStateChangedListeners.values().forEach(listener -> {
             try {
                 listener.onSatelliteProvisionStateChanged(provisioned);
             } catch (RemoteException e) {
                 logd("handleSatelliteProvisionStateChangedEvent RemoteException: " + e);
-                toBeRemoved.add(listener);
+                deadCallersList.add(listener);
             }
         });
-        toBeRemoved.forEach(listener -> {
+        deadCallersList.forEach(listener -> {
             mSatelliteProvisionStateChangedListeners.remove(listener.asBinder());
         });
     }
@@ -2320,6 +2521,31 @@
         }
     }
 
+    private void handleEventNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength) {
+        logd("handleEventNtnSignalStrengthChanged: ntnSignalStrength=" + ntnSignalStrength);
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("handleEventNtnSignalStrengthChanged: oemEnabledSatelliteFlag is disabled");
+            return;
+        }
+
+        synchronized (mNtnSignalsStrengthLock) {
+            mNtnSignalStrength = ntnSignalStrength;
+        }
+
+        List<INtnSignalStrengthCallback> deadCallersList = new ArrayList<>();
+        mNtnSignalStrengthChangedListeners.values().forEach(listener -> {
+            try {
+                listener.onNtnSignalStrengthChanged(ntnSignalStrength);
+            } catch (RemoteException e) {
+                logd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mNtnSignalStrengthChangedListeners.remove(listener.asBinder());
+        });
+    }
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected void setSettingsKeyForSatelliteMode(int val) {
         logd("setSettingsKeyForSatelliteMode val: " + val);
@@ -2396,6 +2622,7 @@
     private void configureSatellitePlmnForCarrier(int subId) {
         logd("configureSatellitePlmnForCarrier");
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("configureSatellitePlmnForCarrier: carrierEnabledSatelliteFlag is disabled");
             return;
         }
         synchronized (mSupportedSatelliteServicesLock) {
@@ -2421,6 +2648,8 @@
 
     private void updateSupportedSatelliteServicesForActiveSubscriptions() {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("updateSupportedSatelliteServicesForActiveSubscriptions: "
+                    + "carrierEnabledSatelliteFlag is disabled");
             return;
         }
 
@@ -2448,6 +2677,7 @@
     @NonNull
     private List<String> readSatellitePlmnsFromOverlayConfig() {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("readSatellitePlmnsFromOverlayConfig: carrierEnabledSatelliteFlag is disabled");
             return new ArrayList<>();
         }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 6ecb642..62f7371 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -31,10 +31,12 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.telephony.Rlog;
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.telephony.satellite.stub.INtnSignalStrengthConsumer;
 import android.telephony.satellite.stub.ISatellite;
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
 import android.telephony.satellite.stub.ISatelliteListener;
@@ -87,6 +89,8 @@
     @NonNull private final RegistrantList mPendingDatagramsRegistrants = new RegistrantList();
     @NonNull private final RegistrantList mSatelliteDatagramsReceivedRegistrants =
             new RegistrantList();
+    @NonNull private final RegistrantList mNtnSignalStrengthChangedRegistrants =
+            new RegistrantList();
 
     @NonNull private final ISatelliteListener mListener = new ISatelliteListener.Stub() {
         @Override
@@ -136,6 +140,13 @@
             }
             mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
         }
+
+        @Override
+        public void onNtnSignalStrengthChanged(
+                android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) {
+            mNtnSignalStrengthChangedRegistrants.notifyResult(
+                    SatelliteServiceUtils.fromModemInterface(ntnSignalStrength));
+        }
     };
 
     /**
@@ -441,6 +452,27 @@
     }
 
     /**
+     * Registers for non-terrestrial signal strength level changed.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForNtnSignalStrengthChanged(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+        mNtnSignalStrengthChangedRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregisters for non-terrestrial signal strength level changed.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForNtnSignalStrengthChanged(@NonNull Handler h) {
+        mNtnSignalStrengthChangedRegistrants.remove(h);
+    }
+
+    /**
      * Request to enable or disable the satellite service listening mode.
      * Listening mode allows the satellite service to listen for incoming pages.
      *
@@ -1153,6 +1185,102 @@
         }
     }
 
+    /**
+     * Request to get the signal strength of the satellite connection.
+     *
+     * @param message The Message to send to result of the operation to.
+     */
+    public void requestNtnSignalStrength(@NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.requestSignalStrength(
+                        new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                int error = SatelliteServiceUtils.fromSatelliteError(result);
+                                logd("requestNtnSignalStrength: " + error);
+                                Binder.withCleanCallingIdentity(() ->
+                                        sendMessageWithResult(message, null, error));
+                            }
+                        }, new INtnSignalStrengthConsumer.Stub() {
+                            @Override
+                            public void accept(NtnSignalStrength result) {
+                                logd("requestNtnSignalStrength: " + result);
+                                Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
+                                        message, result,
+                                        SatelliteManager.SATELLITE_RESULT_SUCCESS));
+                            }
+                        });
+            } catch (RemoteException e) {
+                loge("requestNtnSignalStrength: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            loge("requestNtnSignalStrength: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
+
+    /**
+     * The satellite service should report the NTN signal strength via
+     * ISatelliteListener#onNtnSignalStrengthChanged when the NTN signal strength changes.
+     *
+     * @param message The Message to send to result of the operation to.
+     */
+    public void startSendingNtnSignalStrength(@NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.startSendingNtnSignalStrength(new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        int error = SatelliteServiceUtils.fromSatelliteError(result);
+                        logd("startSendingNtnSignalStrength: " + error);
+                        Binder.withCleanCallingIdentity(() ->
+                                sendMessageWithResult(message, null, error));
+                    }
+                });
+            } catch (RemoteException e) {
+                loge("startSendingNtnSignalStrength: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            loge("startSendingNtnSignalStrength: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
+
+    /**
+     * The satellite service should stop reporting NTN signal strength to the framework.
+     *
+     * @param message The Message to send to result of the operation to.
+     */
+    public void stopSendingNtnSignalStrength(@NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.stopSendingNtnSignalStrength(new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        int error = SatelliteServiceUtils.fromSatelliteError(result);
+                        logd("stopSendingNtnSignalStrength: " + error);
+                        Binder.withCleanCallingIdentity(() ->
+                                sendMessageWithResult(message, null, error));
+                    }
+                });
+            } catch (RemoteException e) {
+                loge("stopSendingNtnSignalStrength: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            loge("stopSendingNtnSignalStrength: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
 
     public boolean isSatelliteServiceSupported() {
         return mIsSatelliteServiceSupported;
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index fc461fd..b42c7d4 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -31,6 +31,7 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.AntennaPosition;
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
@@ -212,6 +213,16 @@
     }
 
     /**
+     * Convert non-terrestrial signal strength from service definition to framework definition.
+     * @param ntnSignalStrength The non-terrestrial signal strength from the satellite service.
+     * @return The converted non-terrestrial signal strength for the framework.
+     */
+    @Nullable public static NtnSignalStrength fromModemInterface(
+            android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) {
+        return new NtnSignalStrength(ntnSignalStrength.signalStrengthLevel);
+    }
+
+    /**
      * Convert SatelliteDatagram from framework definition to service definition.
      * @param datagram The SatelliteDatagram from the framework.
      * @return The converted SatelliteDatagram for the satellite service.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index ea19b62..3c8db99 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -783,7 +783,7 @@
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
         doAnswer(invocation -> mNetworkId++).when(mNetwork).getNetId();
         doReturn(mNetwork).when(mConnectivityManager).registerNetworkAgent(
-                any(), any(), any(), any(), any(), any(), anyInt());
+                any(), any(), any(), any(), any(), any(), any(), anyInt());
 
         doReturn(true).when(mEuiccManager).isEnabled();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
index 28a37f7..018759a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -20,7 +20,9 @@
 import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -30,6 +32,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import static java.util.Arrays.asList;
 
@@ -40,22 +43,31 @@
 import android.hardware.radio.V1_5.IndicationFilter;
 import android.net.ConnectivityManager;
 import android.net.TetheringManager;
+import android.os.AsyncResult;
 import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.telephony.flags.FeatureFlags;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -114,6 +126,7 @@
 
     private static final int STATE_OFF = 0;
     private static final int STATE_ON = 1;
+    private static final long TIMEOUT = 500;
 
     // The keys are the single IndicationFilter flags,
     // The values are the array of states, when one state turn on, the corresponding
@@ -135,6 +148,9 @@
     UiModeManager mUiModeManager;
 
     private DeviceStateMonitor mDSM;
+    private TestSatelliteController mSatelliteControllerUT;
+
+    @Mock private FeatureFlags mFeatureFlags;
 
     // Given a stateType, return the event type that can change the state
     private int state2Event(@StateType int stateType) {
@@ -162,11 +178,12 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        MockitoAnnotations.initMocks(this);
         mUiModeManager = mock(UiModeManager.class);
         mContextFixture.setSystemService(Context.UI_MODE_SERVICE, mUiModeManager);
         // We don't even need a mock executor, we just need to not throw.
         doReturn(null).when(mContextFixture.getTestDouble()).getMainExecutor();
-        mDSM = new DeviceStateMonitor(mPhone);
+        mDSM = new DeviceStateMonitor(mPhone, mFeatureFlags);
 
         // Initialize with ALL states off
         updateAllStatesToOff();
@@ -177,6 +194,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mSatelliteControllerUT = null;
         mDSM = null;
         super.tearDown();
     }
@@ -453,4 +471,160 @@
         verify(mSimulatedCommandsVerifier).setUnsolResponseFilter(
                 eq(INDICATION_FILTERS_MINIMUM), nullable(Message.class));
     }
+
+    @Test
+    public void testRegisterForSignalStrengthReportDecisionWithFeatureEnabled() {
+        logd("testRegisterForSignalStrengthReportDecisionWithFeatureEnabled()");
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        mSatelliteControllerUT = new TestSatelliteController(Looper.myLooper(), mDSM);
+
+        updateState(STATE_TYPE_RADIO_OFF_OR_NOT_AVAILABLE, 0);
+        updateState(STATE_TYPE_SCREEN, STATE_OFF);
+        mSatelliteControllerUT.resetCount();
+        sEventDeviceStatusChanged.drainPermits();
+
+        updateState(STATE_TYPE_SCREEN, STATE_ON);
+        assertTrue(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(1, mSatelliteControllerUT.getStopEventCount());
+        mSatelliteControllerUT.resetCount();
+
+        mSatelliteControllerUT.resetCount();
+        updateState(STATE_TYPE_SCREEN, STATE_OFF);
+        assertTrue(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(1, mSatelliteControllerUT.getStopEventCount());
+        mSatelliteControllerUT.resetCount();
+
+        updateState(STATE_TYPE_RADIO_ON, 0);
+        assertTrue(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(1, mSatelliteControllerUT.getStopEventCount());
+        mSatelliteControllerUT.resetCount();
+
+        updateState(STATE_TYPE_SCREEN, STATE_ON);
+        assertTrue(waitForEventDeviceStatusChanged());
+        assertEquals(1, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(0, mSatelliteControllerUT.getStopEventCount());
+        mSatelliteControllerUT.resetCount();
+
+        updateState(STATE_TYPE_RADIO_OFF_OR_NOT_AVAILABLE, 0);
+        assertTrue(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(1, mSatelliteControllerUT.getStopEventCount());
+    }
+
+    @Test
+    public void testRegisterForSignalStrengthReportDecisionWithFeatureDisabled() {
+        logd("testRegisterForSignalStrengthReportDecisionWithFeatureDisabled()");
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
+        mSatelliteControllerUT = new TestSatelliteController(Looper.myLooper(), mDSM);
+
+        updateState(STATE_TYPE_RADIO_OFF_OR_NOT_AVAILABLE, 0);
+        updateState(STATE_TYPE_SCREEN, STATE_OFF);
+        mSatelliteControllerUT.resetCount();
+        sEventDeviceStatusChanged.drainPermits();
+
+
+        /* Sending stop ntn signal strength as radio is off */
+        updateState(STATE_TYPE_SCREEN, STATE_ON);
+        assertFalse(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(0, mSatelliteControllerUT.getStopEventCount());
+
+        updateState(STATE_TYPE_SCREEN, STATE_OFF);
+        assertFalse(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(0, mSatelliteControllerUT.getStopEventCount());
+
+        updateState(STATE_TYPE_RADIO_ON, 0);
+        assertFalse(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(0, mSatelliteControllerUT.getStopEventCount());
+
+        updateState(STATE_TYPE_SCREEN, STATE_ON);
+        assertFalse(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(0, mSatelliteControllerUT.getStopEventCount());
+
+        updateState(STATE_TYPE_RADIO_OFF_OR_NOT_AVAILABLE, 0);
+        assertFalse(waitForEventDeviceStatusChanged());
+        assertEquals(0, mSatelliteControllerUT.getStartEventCount());
+        assertEquals(0, mSatelliteControllerUT.getStopEventCount());
+    }
+
+    private static Semaphore sEventDeviceStatusChanged = new Semaphore(0);
+    private boolean waitForEventDeviceStatusChanged() {
+        try {
+            if (!sEventDeviceStatusChanged.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                logd("Time out to receive EVENT_DEVICE_STATUS_CHANGED");
+                return false;
+            }
+        } catch (Exception ex) {
+            logd("waitForEventDeviceStatusChanged: ex=" + ex);
+            return false;
+        }
+        return true;
+    }
+
+    private static class TestSatelliteController extends Handler {
+        public static final int EVENT_DEVICE_STATUS_CHANGED = 35;
+        private final DeviceStateMonitor mDsm;
+        private int mStartEventCount;
+        private int mStopEventCount;
+
+        TestSatelliteController(Looper looper, DeviceStateMonitor dsm) {
+            super(looper);
+            mDsm = dsm;
+            mDsm.registerForSignalStrengthReportDecision(this, EVENT_DEVICE_STATUS_CHANGED, null);
+        }
+
+        /**
+         * Resets the count of occurred events.
+         */
+        public void resetCount() {
+            mStartEventCount = 0;
+            mStopEventCount = 0;
+        }
+
+        public int getStartEventCount() {
+            return mStartEventCount;
+        }
+
+        public int getStopEventCount() {
+            return mStopEventCount;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case EVENT_DEVICE_STATUS_CHANGED: {
+                    logd("EVENT_DEVICE_STATUS_CHANGED");
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    boolean shouldReport = (boolean) ar.result;
+                    if (shouldReport) {
+                        startSendingNtnSignalStrength();
+                    } else {
+                        stopSendingNtnSignalStrength();
+                    }
+                    try {
+                        sEventDeviceStatusChanged.release();
+                    } catch (Exception ex) {
+                        logd("waitForEventDeviceStatusChanged: ex=" + ex);
+                    }
+                    break;
+                }
+                default:
+                    break;
+            }
+        }
+
+        private void startSendingNtnSignalStrength() {
+            mStartEventCount++;
+        }
+
+        private void stopSendingNtnSignalStrength() {
+            mStopEventCount++;
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java
index f729b80..8dd350f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DisplayInfoControllerTest.java
@@ -42,11 +42,14 @@
 import android.testing.TestableLooper;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.flags.FeatureFlags;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.util.Collections;
 import java.util.concurrent.Executor;
@@ -54,17 +57,18 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class DisplayInfoControllerTest extends TelephonyTest {
-
     private static final int PHONE_ID = 0;
     private static final String MCC = "600";
     private static final String MNC = "01";
     private static final String NUMERIC = MCC + MNC;
     private static final String NETWORK = "TestNet";
 
+    // Mocked classes
+    private FeatureFlags mFeatureFlags;
+
     private DisplayInfoController mDic;
     private ServiceStateTracker mSst;
     private ServiceStateTrackerTestHandler mSstHandler;
-    private SignalStrengthController mSsc;
     private PersistableBundle mBundle;
     private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
 
@@ -75,8 +79,8 @@
 
         @Override
         public void onLooperPrepared() {
-            mSsc = new SignalStrengthController(mPhone);
-            doReturn(mSsc).when(mPhone).getSignalStrengthController();
+            SignalStrengthController ssc = new SignalStrengthController(mPhone);
+            doReturn(ssc).when(mPhone).getSignalStrengthController();
             doReturn(new ServiceState()).when(mPhone).getServiceState();
             doReturn(NUMERIC).when(mTelephonyManager).getSimOperatorNumericForPhone(eq(PHONE_ID));
             doReturn(NETWORK).when(mTelephonyManager).getSimOperatorNameForPhone(eq(PHONE_ID));
@@ -101,6 +105,7 @@
         logd("DisplayInfoControllerTest setup!");
         super.setUp(getClass().getSimpleName());
 
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         doReturn((Executor) Runnable::run).when(mContext).getMainExecutor();
         mBundle = mContextFixture.getCarrierConfigBundle();
         mSstHandler = new ServiceStateTrackerTestHandler(getClass().getSimpleName());
@@ -191,7 +196,7 @@
         assertFalse(ss.getRoaming()); // home
 
         doReturn(mSst).when(mPhone).getServiceStateTracker();
-        mDic = new DisplayInfoController(mPhone);
+        mDic = new DisplayInfoController(mPhone, mFeatureFlags);
         mDic.updateTelephonyDisplayInfo();
         TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
 
@@ -211,7 +216,7 @@
         assertFalse(ss.getRoaming()); // home
 
         doReturn(mSst).when(mPhone).getServiceStateTracker();
-        mDic = new DisplayInfoController(mPhone);
+        mDic = new DisplayInfoController(mPhone, mFeatureFlags);
         mDic.updateTelephonyDisplayInfo();
         TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
 
@@ -232,7 +237,7 @@
         assertTrue(ss1.getRoaming()); // roam
 
         doReturn(mSst).when(mPhone).getServiceStateTracker();
-        mDic = new DisplayInfoController(mPhone);
+        mDic = new DisplayInfoController(mPhone, mFeatureFlags);
         mDic.updateTelephonyDisplayInfo();
         TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
 
@@ -254,7 +259,7 @@
         assertFalse(ss.getRoaming()); // home
 
         doReturn(mSst).when(mPhone).getServiceStateTracker();
-        mDic = new DisplayInfoController(mPhone);
+        mDic = new DisplayInfoController(mPhone, mFeatureFlags);
         mDic.updateTelephonyDisplayInfo();
         TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
 
@@ -275,10 +280,32 @@
         assertTrue(ss1.getRoaming()); // roam
 
         doReturn(mSst).when(mPhone).getServiceStateTracker();
-        mDic = new DisplayInfoController(mPhone);
+        mDic = new DisplayInfoController(mPhone, mFeatureFlags);
         mDic.updateTelephonyDisplayInfo();
         TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
 
         assertTrue(tdi.isRoaming());
     }
+
+    @Test
+    public void testIsRoamingOverride_HideRoamingIndicator() {
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, new String[] {NUMERIC});
+        mBundle.putBoolean(CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL, false);
+        doReturn(true).when(mFeatureFlags).hideRoamingIcon();
+        sendCarrierConfigUpdate();
+
+        changeRegState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        ServiceState ss1 = mSst.getServiceState();
+
+        assertTrue(ss1.getRoaming()); // roam
+
+        doReturn(mSst).when(mPhone).getServiceStateTracker();
+        mDic = new DisplayInfoController(mPhone, mFeatureFlags);
+        mDic.updateTelephonyDisplayInfo();
+        TelephonyDisplayInfo tdi = mDic.getTelephonyDisplayInfo();
+
+        assertFalse(tdi.isRoaming()); // display override
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index d304f5c..435763b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -108,6 +108,7 @@
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
 import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsNrSaModeHandler;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -183,6 +184,7 @@
     }
 
     // Mocked classes
+    protected FeatureFlags mFeatureFlags;
     protected GsmCdmaPhone mPhone;
     protected GsmCdmaPhone mPhone2;
     protected ImsPhone mImsPhone;
@@ -421,6 +423,7 @@
     protected void setUp(String tag) throws Exception {
         TAG = tag;
         enableStrictMode();
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         mPhone = Mockito.mock(GsmCdmaPhone.class);
         mPhone2 = Mockito.mock(GsmCdmaPhone.class);
         mImsPhone = Mockito.mock(ImsPhone.class);
@@ -590,7 +593,7 @@
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
                 .makeIccPhoneBookInterfaceManager(nullable(Phone.class));
         doReturn(mDisplayInfoController).when(mTelephonyComponentFactory)
-                .makeDisplayInfoController(nullable(Phone.class));
+                .makeDisplayInfoController(nullable(Phone.class), any(FeatureFlags.class));
         doReturn(mWspTypeDecoder).when(mTelephonyComponentFactory)
                 .makeWspTypeDecoder(nullable(byte[].class));
         doReturn(mImsCT).when(mTelephonyComponentFactory)
@@ -610,7 +613,7 @@
         doReturn(mCarrierActionAgent).when(mTelephonyComponentFactory)
                 .makeCarrierActionAgent(nullable(Phone.class));
         doReturn(mDeviceStateMonitor).when(mTelephonyComponentFactory)
-                .makeDeviceStateMonitor(nullable(Phone.class));
+                .makeDeviceStateMonitor(nullable(Phone.class), any(FeatureFlags.class));
         doReturn(mAccessNetworksManager).when(mTelephonyComponentFactory)
                 .makeAccessNetworksManager(nullable(Phone.class), any(Looper.class));
         doReturn(mNitzStateMachine).when(mTelephonyComponentFactory)
@@ -625,6 +628,7 @@
         doReturn(mDataProfileManager).when(mTelephonyComponentFactory)
                 .makeDataProfileManager(any(Phone.class), any(DataNetworkController.class),
                         any(DataServiceManager.class), any(Looper.class),
+                        any(FeatureFlags.class),
                         any(DataProfileManager.DataProfileManagerCallback.class));
 
         //mPhone
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
index b6d77e9..98d3ce5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
@@ -154,6 +154,9 @@
         assertTrue(createApnSetting(
                 ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_EMERGENCY)
                 .canHandleType(ApnSetting.TYPE_EMERGENCY));
+        assertTrue(createApnSetting(
+                ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_RCS | ApnSetting.TYPE_EMERGENCY)
+                .canHandleType(ApnSetting.TYPE_RCS));
         assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
                 .canHandleType(ApnSetting.TYPE_MCX));
         assertTrue(createApnSetting(
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 5e7213b..f1ab894 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -209,6 +209,8 @@
                     .setMaxConns(321)
                     .setWaitTime(456)
                     .setMaxConnsTime(789)
+                    .setInfrastructureBitmask(ApnSetting.INFRASTRUCTURE_SATELLITE
+                            | ApnSetting.INFRASTRUCTURE_CELLULAR)
                     .build())
             .setPreferred(false)
             .build();
@@ -401,6 +403,17 @@
             .setPreferred(false)
             .build();
 
+    private final DataProfile mNtnDataProfile = new DataProfile.Builder()
+            .setApnSetting(new ApnSetting.Builder()
+                    .setEntryName("ntn")
+                    .setApnName("ntn")
+                    .setApnTypeBitmask(ApnSetting.TYPE_RCS)
+                    .setCarrierEnabled(true)
+                    .setInfrastructureBitmask(ApnSetting.INFRASTRUCTURE_SATELLITE)
+                    .build())
+            .setPreferred(false)
+            .build();
+
     /** Data call response map. The first key is the transport type, the second key is the cid. */
     private final Map<Integer, Map<Integer, DataCallResponse>> mDataCallResponses = new HashMap<>();
 
@@ -904,7 +917,8 @@
         List<DataProfile> profiles = List.of(mGeneralPurposeDataProfile,
                 mGeneralPurposeDataProfileAlternative, mImsCellularDataProfile,
                 mImsIwlanDataProfile, mEmergencyDataProfile, mFotaDataProfile,
-                mTetheringDataProfile);
+                mTetheringDataProfile, mMmsOnWlanDataProfile, mLowLatencyDataProfile,
+                mNtnDataProfile);
 
         doAnswer(invocation -> {
             DataProfile dp = (DataProfile) invocation.getArguments()[0];
@@ -935,23 +949,30 @@
             TelephonyNetworkRequest networkRequest =
                     (TelephonyNetworkRequest) invocation.getArguments()[0];
             int networkType = (int) invocation.getArguments()[1];
-            boolean ignorePermanentFailure = (boolean) invocation.getArguments()[2];
+            boolean isNtn = (boolean) invocation.getArguments()[2];
+            boolean ignorePermanentFailure = (boolean) invocation.getArguments()[3];
 
             for (DataProfile dataProfile : profiles) {
-                if (dataProfile.canSatisfy(networkRequest.getCapabilities())
-                        && (dataProfile.getApnSetting().getNetworkTypeBitmask() == 0
-                        || (dataProfile.getApnSetting().getNetworkTypeBitmask()
+                ApnSetting apnSetting = dataProfile.getApnSetting();
+                if (apnSetting != null
+                        && dataProfile.canSatisfy(networkRequest.getCapabilities())
+                        && (apnSetting.getNetworkTypeBitmask() == 0
+                        || (apnSetting.getNetworkTypeBitmask()
                         & ServiceState.getBitmaskForTech(networkType)) != 0)
-                        && (ignorePermanentFailure || (dataProfile.getApnSetting() != null
-                        && !dataProfile.getApnSetting().getPermanentFailed()))) {
+                        && ((isNtn && apnSetting.isForInfrastructure(
+                                ApnSetting.INFRASTRUCTURE_SATELLITE))
+                        || ((!isNtn && apnSetting.isForInfrastructure(
+                                ApnSetting.INFRASTRUCTURE_CELLULAR))))
+                        && (ignorePermanentFailure || !apnSetting.getPermanentFailed())) {
                     return dataProfile;
                 }
             }
             logd("Cannot find data profile to satisfy " + networkRequest + ", network type="
-                    + TelephonyManager.getNetworkTypeName(networkType));
+                    + TelephonyManager.getNetworkTypeName(networkType) + ", ignorePermanentFailure="
+                    + ignorePermanentFailure + ", isNtn=" + isNtn);
             return null;
         }).when(mDataProfileManager).getDataProfileForNetworkRequest(
-                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean());
+                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean(), anyBoolean());
 
         doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
                 .getPreferredTransportByNetworkCapability(anyInt());
@@ -1233,7 +1254,7 @@
                     + TelephonyManager.getNetworkTypeName(networkType));
             return null;
         }).when(mDataProfileManager).getDataProfileForNetworkRequest(
-                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean());
+                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean(), anyBoolean());
 
         // verify the network still connects
         verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
@@ -1278,7 +1299,7 @@
                 createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean());
+                        anyBoolean(), anyBoolean());
 
         NetworkCapabilities netCaps = new NetworkCapabilities();
         netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
@@ -1493,7 +1514,7 @@
         // Now RAT changes from UMTS to GSM
         doReturn(null).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_GSM),
-                anyBoolean());
+                anyBoolean(), anyBoolean());
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_GSM,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         verifyAllDataDisconnected();
@@ -1507,14 +1528,14 @@
         // Now RAT changes from GSM to UMTS
         doReturn(null).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_UMTS),
-                anyBoolean());
+                anyBoolean(), anyBoolean());
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_UMTS,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
         doReturn(mGeneralPurposeDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean());
+                        anyBoolean(), anyBoolean());
         // Now RAT changes from UMTS to LTE
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
@@ -3509,7 +3530,7 @@
                 createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean());
+                        anyBoolean(), anyBoolean());
 
         NetworkCapabilities netCaps = new NetworkCapabilities();
         netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
@@ -4664,7 +4685,7 @@
                 createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean());
+                        anyBoolean(), anyBoolean());
         mDataNetworkControllerUT.addNetworkRequest(new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
@@ -4702,7 +4723,7 @@
                 createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mLowLatencyDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean());
+                        anyBoolean(), anyBoolean());
         processAllFutureMessages();
 
         dataNetworkList = getDataNetworks();
@@ -4731,12 +4752,11 @@
                 createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS));
         processAllMessages();
         verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
-        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
 
         // Mock the designated MMS profile when WLAN is preferred
         doReturn(mMmsOnWlanDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_IWLAN),
-                anyBoolean());
+                anyBoolean(), anyBoolean());
         setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager,
                 createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE));
 
@@ -4746,4 +4766,16 @@
         processAllMessages();
         verifyConnectedNetworkHasDataProfile(mMmsOnWlanDataProfile);
     }
+
+    @Test
+    public void testNonTerrestrialNetwork() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        mIsNonTerrestrialNetwork = true;
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_RCS));
+        processAllMessages();
+        verifyConnectedNetworkHasDataProfile(mNtnDataProfile);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index 9235e46..e34d1ba 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -443,7 +443,7 @@
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), networkCapabilitiesCaptor.capture(), any(), any(),
-                anyInt());
+                any(), anyInt());
         // The very first link properties from telephony is an empty link properties. It will be
         // updated later.
         assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
@@ -550,7 +550,7 @@
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         // Make sure the initial network capability has NOT_SUSPENDED
         assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
@@ -614,7 +614,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         // Make sure the 2nd network agent was created with NOT_SUSPENDED.
         assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
@@ -674,7 +674,7 @@
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), networkCapabilitiesCaptor.capture(), any(), any(),
-                anyInt());
+                any(), anyInt());
         // The very first link properties from telephony is an empty link properties. It will be
         // updated later.
         assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
@@ -1434,8 +1434,8 @@
                 .forClass(NetworkAgentConfig.class);
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
-                any(LinkProperties.class), any(NetworkCapabilities.class), any(), captor.capture(),
-                anyInt());
+                any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
+                captor.capture(), anyInt());
 
         NetworkAgentConfig networkAgentConfig = captor.getValue();
 
@@ -1518,7 +1518,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
@@ -1540,7 +1540,7 @@
         // Agent not re-created, so register should be called once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)).isTrue();
@@ -1709,7 +1709,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
         // The new agent should have the new IP address.
         assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
                 InetAddresses.parseNumericAddress(IPV4_ADDRESS1),
@@ -1758,7 +1758,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
         // The new agent should have the new IP address.
         assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
                 InetAddresses.parseNumericAddress(IPV6_ADDRESS1));
@@ -1849,7 +1849,7 @@
         // Agent should not be re-created, so register should be called ony once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         // The network should have IPv6 address now
         assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1893,7 +1893,7 @@
         // Agent should not be re-created, so register should be called ony once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         // The network should have IPv6 address now
         assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1938,7 +1938,7 @@
         // Agent should not be re-created, so register should be called ony once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                anyInt());
+                any(), anyInt());
 
         // The network should have IPv6 address now
         assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index 5c7e3ec..f9a11be 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
 import android.content.ContentValues;
@@ -53,6 +54,7 @@
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
 import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -81,6 +83,7 @@
     private static final String IMS_APN = "IMS_APN";
     private static final String TETHERING_APN = "DUN_APN";
     private static final String APN_SET_ID_1_APN = "APN_SET_ID_1_APN";
+    private static final String RCS_APN = "RCS_APN";
     private static final String APN_SET_ID_1_TETHERING_APN = "APN_SET_ID_1_TETHERING_APN";
     private static final String MATCH_ALL_APN_SET_ID_IMS_APN = "MATCH_ALL_APN_SET_ID_IMS_APN";
     private static final String PLMN = "330123";
@@ -93,6 +96,8 @@
 
     private DataProfileManager mDataProfileManagerUT;
 
+    private FeatureFlags mFeatureFlags;
+
     private final ApnSettingContentProvider mApnSettingContentProvider =
             new ApnSettingContentProvider();
 
@@ -132,6 +137,7 @@
                 Telephony.Carriers.CARRIER_ID,
                 Telephony.Carriers.SKIP_464XLAT,
                 Telephony.Carriers.ALWAYS_ON,
+                Telephony.Carriers.INFRASTRUCTURE_BITMASK
         };
 
         private int mPreferredApnSet = 0;
@@ -170,7 +176,8 @@
                         DEFAULT_APN_SET_ID,     // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 // default internet data profile for RAT CDMA, to test update preferred data profile
                 new Object[]{
@@ -205,7 +212,8 @@
                         DEFAULT_APN_SET_ID,     // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 new Object[]{
                         2,                      // id
@@ -239,7 +247,8 @@
                         DEFAULT_APN_SET_ID,     // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 new Object[]{
                         3,                      // id
@@ -273,7 +282,8 @@
                         DEFAULT_APN_SET_ID,     // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // alwys_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 new Object[]{
                         4,                      // id
@@ -308,7 +318,8 @@
                         DEFAULT_APN_SET_ID,     // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 // This APN entry is created to test de-duping.
                 new Object[]{
@@ -344,7 +355,8 @@
                         DEFAULT_APN_SET_ID,     // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 new Object[]{
                         6,                      // id
@@ -379,7 +391,8 @@
                         APN_SET_ID_1,           // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 new Object[]{
                         7,                      // id
@@ -414,7 +427,8 @@
                         APN_SET_ID_1,           // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
                 },
                 new Object[]{
                         8,                      // id
@@ -449,7 +463,44 @@
                         MATCH_ALL_APN_SET_ID,   // apn_set_id
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
-                        0                       // always_on
+                        0,                      // always_on
+                        1                       // INFRASTRUCTURE_CELLULAR
+                },
+                new Object[]{
+                        9,                      // id
+                        PLMN,                   // numeric
+                        RCS_APN,                // name
+                        RCS_APN,                // apn
+                        "",                     // proxy
+                        "",                     // port
+                        "",                     // mmsc
+                        "",                     // mmsproxy
+                        "",                     // mmsport
+                        "",                     // user
+                        "",                     // password
+                        -1,                     // authtype
+                        "rcs",                  // types
+                        "IPV4V6",               // protocol
+                        "IPV4V6",               // roaming_protocol
+                        1,                      // carrier_enabled
+                        0,                      // profile_id
+                        1,                      // modem_cognitive
+                        0,                      // max_conns
+                        0,                      // wait_time
+                        0,                      // max_conns_time
+                        0,                      // mtu
+                        1280,                   // mtu_v4
+                        1280,                   // mtu_v6
+                        "",                     // mvno_type
+                        "",                     // mnvo_match_data
+                        TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                                | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+                        0,                      // lingering_network_type_bitmask
+                        DEFAULT_APN_SET_ID,     // apn_set_id
+                        -1,                     // carrier_id
+                        -1,                     // skip_464xlat
+                        0,                      // always_on
+                        2                       // INFRASTRUCTURE_SATELLITE
                 }
         );
 
@@ -621,6 +672,7 @@
         logd("DataProfileManagerTest +Setup!");
         super.setUp(getClass().getSimpleName());
         mDataProfileManagerCallback = Mockito.mock(DataProfileManagerCallback.class);
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 Telephony.Carriers.CONTENT_URI.getAuthority(), mApnSettingContentProvider);
 
@@ -632,7 +684,8 @@
             return null;
         }).when(mDataProfileManagerCallback).invokeFromExecutor(any(Runnable.class));
         mDataProfileManagerUT = new DataProfileManager(mPhone, mDataNetworkController,
-                mMockedWwanDataServiceManager, Looper.myLooper(), mDataProfileManagerCallback);
+                mMockedWwanDataServiceManager, Looper.myLooper(), mFeatureFlags,
+                mDataProfileManagerCallback);
         ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
                 ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
         verify(mDataNetworkController).registerDataNetworkControllerCallback(
@@ -663,7 +716,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
 
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
@@ -674,7 +727,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
 
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
@@ -684,7 +737,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(IMS_APN);
 
@@ -693,7 +746,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dp).isNull();
 
         doReturn(new NetworkRegistrationInfo.Builder()
@@ -706,7 +759,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_NR, false);
+                TelephonyManager.NETWORK_TYPE_NR, false, false);
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(TETHERING_APN);
     }
@@ -718,7 +771,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_GSM, false);
+                TelephonyManager.NETWORK_TYPE_GSM, false, false);
         // Should not find data profile due to RAT incompatible.
         assertThat(dp).isNull();
     }
@@ -730,14 +783,14 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         logd("Set setLastSetupTimestamp on " + dataProfile);
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
 
         // See if another one can be returned.
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
     }
 
@@ -748,7 +801,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -761,7 +814,7 @@
                 .addEnterpriseId(2), ConnectivityManager.TYPE_NONE,
                 0, NetworkRequest.Type.REQUEST), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -777,7 +830,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -793,7 +846,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -803,13 +856,28 @@
     }
 
     @Test
+    public void testGetDataProfileForSatellite() {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+                .build();
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
+        DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
+                TelephonyManager.NETWORK_TYPE_LTE, true, false);
+
+        assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
+        assertThat(dp.getApnSetting().getApnName()).isEqualTo(RCS_APN);
+    }
+
+    @Test
     public void testSetPreferredDataProfile() {
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
         dataProfile.setPreferred(true);
@@ -823,14 +891,14 @@
 
         // Test See if the same one can be returned.
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
 
         // Test Another default internet network connected due to RAT changed. Verify the preferred
         // data profile is updated.
         DataProfile legacyRatDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_CDMA, false);
+                tnr, TelephonyManager.NETWORK_TYPE_CDMA, false, false);
         DataNetwork legacyRatInternetNetwork = Mockito.mock(DataNetwork.class);
         doReturn(legacyRatDataProfile).when(legacyRatInternetNetwork).getDataProfile();
         doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
@@ -849,7 +917,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                         .build(), mPhone);
         DataProfile dunDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                dunTnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                dunTnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         DataNetwork dunInternetNetwork = Mockito.mock(DataNetwork.class);
         doReturn(dunDataProfile).when(dunInternetNetwork).getDataProfile();
         doReturn(new DataNetworkController.NetworkRequestList(List.of(dunTnr)))
@@ -948,7 +1016,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile).isNull();
 
         // expect default EIMS when SIM absent
@@ -957,7 +1025,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("sos");
 
         // expect no default IMS when SIM absent
@@ -966,7 +1034,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile).isEqualTo(null);
 
         // Verify null as initial attached data profile is sent to modem
@@ -996,7 +1064,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile).isNull();
 
         // expect default EIMS when SIM absent
@@ -1005,7 +1073,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("sos");
 
         // expect no default IMS when SIM absent
@@ -1014,7 +1082,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile).isEqualTo(null);
 
         // Verify in legacy mode, null IA should NOT be sent to modem
@@ -1049,7 +1117,7 @@
                 new TelephonyNetworkRequest(new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                         .build(), mPhone),
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(IMS_APN);
     }
 
@@ -1061,7 +1129,7 @@
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         // This should get the merged data profile after deduping.
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue();
     }
 
@@ -1198,7 +1266,7 @@
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                 .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
 
         assertThat(dataProfile.getApn()).isEqualTo("sos");
         assertThat(dataProfile.getTrafficDescriptor().getDataNetworkName()).isEqualTo("sos");
@@ -1215,7 +1283,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
         DataNetwork internetNetwork = Mockito.mock(DataNetwork.class);
@@ -1286,7 +1354,7 @@
 
         // The carrier configured data profile should be the preferred APN after APN reset
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
 
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
@@ -1299,7 +1367,7 @@
 
         // The carrier configured data profile should be the preferred APN after APN reset
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
     }
@@ -1580,7 +1648,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
 
         // Mark the APN as permanent failed.
         dp.getApnSetting().setPermanentFailed(true);
@@ -1588,7 +1656,7 @@
         // Data profile manager should return a different data profile for setup as the previous
         // data profile has been marked as permanent failed.
         assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false)).isNotEqualTo(dp);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false)).isNotEqualTo(dp);
     }
 
     @Test
@@ -1603,7 +1671,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false);
 
         // Mark the APN as permanent failed.
         dp.getApnSetting().setPermanentFailed(true);
@@ -1611,7 +1679,7 @@
         // Since preferred APN is already set, and that data profile was marked as permanent failed,
         // so this should result in getting nothing.
         assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false)).isNull();
+                TelephonyManager.NETWORK_TYPE_LTE, false, false)).isNull();
     }
 
     private void changeSimStateTo(@TelephonyManager.SimState int simState) {
@@ -1632,6 +1700,6 @@
         // Verify the we can get the previously permanent failed data profile again.
         assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(
                 new TelephonyNetworkRequest(request, mPhone),
-                TelephonyManager.NETWORK_TYPE_LTE, false)).isNotNull();
+                TelephonyManager.NETWORK_TYPE_LTE, false, false)).isNotNull();
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileTest.java
index 4a2eb38..f5981f7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileTest.java
@@ -179,6 +179,7 @@
         assertThat(dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_MMS)).isFalse();
         assertThat(dp.canSatisfy(new int[]{NetworkCapabilities.NET_CAPABILITY_INTERNET,
                 NetworkCapabilities.NET_CAPABILITY_MMS})).isFalse();
+        assertThat(dp.canSatisfy(new int[]{NetworkCapabilities.NET_CAPABILITY_RCS})).isFalse();
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
index 8372d8f..dd95ff0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -158,7 +158,8 @@
         mockedDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
                 mMockedWlanDataServiceManager);
         mDataRetryManagerUT = new DataRetryManager(mPhone, mDataNetworkController,
-                mockedDataServiceManagers, Looper.myLooper(), mDataRetryManagerCallbackMock);
+                mockedDataServiceManagers, Looper.myLooper(), mFeatureFlags,
+                mDataRetryManagerCallbackMock);
 
         ArgumentCaptor<DataConfigManagerCallback> dataConfigManagerCallbackCaptor =
                 ArgumentCaptor.forClass(DataConfigManagerCallback.class);
@@ -340,29 +341,30 @@
 
     @Test
     public void testDataSetupUnthrottling() throws Exception {
-        testDataSetupRetryNetworkSuggestedNeverRetry();
+        doReturn(true).when(mFeatureFlags).unthrottleCheckTransport();
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+                .build();
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
+        DataNetworkController.NetworkRequestList
+                networkRequestList = new DataNetworkController.NetworkRequestList(tnr);
+        mDataRetryManagerUT.evaluateDataSetupRetry(mDataProfile3,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, networkRequestList, 123,
+                456);
+        mDataRetryManagerUT.evaluateDataSetupRetry(mDataProfile3,
+                AccessNetworkConstants.TRANSPORT_TYPE_WLAN, networkRequestList, 123,
+                456);
+        processAllFutureMessages();
         Mockito.clearInvocations(mDataRetryManagerCallbackMock);
+
         DataNetworkController.NetworkRequestList mockNrl = Mockito.mock(
                 DataNetworkController.NetworkRequestList.class);
         Field field = DataRetryManager.class.getDeclaredField("mDataRetryEntries");
         field.setAccessible(true);
         List<DataRetryEntry> mDataRetryEntries =
                 (List<DataRetryEntry>) field.get(mDataRetryManagerUT);
+        assertThat(mDataRetryEntries.size()).isEqualTo(2);
 
-        // schedule 2 setup retries
-        DataSetupRetryEntry scheduledRetry1 = new DataSetupRetryEntry.Builder<>()
-                .setDataProfile(mDataProfile3)
-                .setNetworkRequestList(mockNrl)
-                .setTransport(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                .setSetupRetryType(1)
-                .build();
-        DataSetupRetryEntry scheduledRetry2 = new DataSetupRetryEntry.Builder<>()
-                .setNetworkRequestList(mockNrl)
-                .setDataProfile(mDataProfile3)
-                .setTransport(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
-                .setSetupRetryType(1)
-                .build();
-        mDataRetryEntries.addAll(List.of(scheduledRetry1, scheduledRetry2));
         // Suppose we set the data profile as permanently failed.
         mDataProfile3.getApnSetting().setPermanentFailed(true);
 
@@ -408,8 +410,21 @@
         assertThat(entry.transport).isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
         // check mDataProfile3-WWAN retry is cancelled, but not the WLAN
-        assertThat(scheduledRetry1.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
-        assertThat(scheduledRetry2.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
+        assertThat(mDataRetryEntries.size()).isEqualTo(3);
+        for (DataRetryEntry retry : mDataRetryEntries) {
+            DataSetupRetryEntry setupRetry = (DataSetupRetryEntry) retry;
+            if (setupRetry.transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+                if (setupRetry.retryDelayMillis == 0) {
+                    assertThat(setupRetry.getState())
+                            .isEqualTo(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
+                } else {
+                    assertThat(setupRetry.getState())
+                            .isEqualTo(DataRetryEntry.RETRY_STATE_CANCELLED);
+                }
+            } else {
+                assertThat(setupRetry.getState()).isEqualTo(DataRetryEntry.RETRY_STATE_NOT_RETRIED);
+            }
+        }
     }
 
     @Test
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 9e78afd..94fd934 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -679,6 +679,37 @@
     }
 
     @Test
+    public void testSetAutoSelectedValidationFeatureNotSupported() throws Exception {
+        doReturn(false).when(mCellularNetworkValidator).isValidationFeatureSupported();
+        initialize();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        doReturn(new SubscriptionInfoInternal.Builder(mSubscriptionManagerService
+                .getSubscriptionInfoInternal(2)).setOpportunistic(1).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
+
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
+        processAllMessages();
+        mPhoneSwitcherUT.mValidationCallback.onNetworkAvailable(null, 2);
+        processAllMessages();
+        assertEquals(2, mPhoneSwitcherUT.getAutoSelectedDataSubId());
+
+        // Switch to the default sub, verify AutoSelectedDataSubId is the default value.
+        clearInvocations(mSetOpptDataCallback1);
+        mPhoneSwitcherUT.trySetOpportunisticDataSubscription(SubscriptionManager
+                .DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback1);
+        processAllMessages();
+        assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                mPhoneSwitcherUT.getAutoSelectedDataSubId());
+    }
+
+    @Test
     @SmallTest
     public void testSetPreferredDataModemCommand() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
@@ -1357,7 +1388,7 @@
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(2);
 
         // Switch to primary before a primary is selected/inactive.
-        setDefaultDataSubId(-1);
+        setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
         processAllMessages();
@@ -1992,7 +2023,7 @@
         doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
     }
 
-    private void setDefaultDataSubId(int defaultDataSub) throws Exception {
+    private void setDefaultDataSubId(int defaultDataSub) {
         mDefaultDataSub = defaultDataSub;
         doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
         for (Phone phone : mPhones) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
index 9752885..26a9fde 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
@@ -87,6 +87,24 @@
             .setMaxConnsTime(789)
             .build();
 
+    private static final ApnSetting RCS_APN_SETTING = new ApnSetting.Builder()
+            .setId(2165)
+            .setOperatorNumeric("12345")
+            .setEntryName("rcs")
+            .setApnName("rcs")
+            .setUser("user")
+            .setPassword("passwd")
+            .setApnTypeBitmask(ApnSetting.TYPE_RCS)
+            .setProtocol(ApnSetting.PROTOCOL_IPV6)
+            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+            .setCarrierEnabled(true)
+            .setNetworkTypeBitmask(0)
+            .setProfileId(1234)
+            .setMaxConns(321)
+            .setWaitTime(456)
+            .setMaxConnsTime(789)
+            .build();
+
     @Before
     public void setUp() throws Exception {
         logd("TelephonyNetworkRequestTest +Setup!");
@@ -211,6 +229,20 @@
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
                 .build();
         assertThat(internetRequest.canBeSatisfiedBy(caps)).isTrue();
+
+        TelephonyNetworkRequest rcsRequest = new TelephonyNetworkRequest(
+                new NetworkRequest.Builder()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+                        .build(), mPhone);
+        caps = new NetworkCapabilities.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+                .build();
+        assertThat(rcsRequest.canBeSatisfiedBy(caps)).isTrue();
     }
 
     @Test
@@ -223,17 +255,25 @@
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
                         .build(), mPhone);
+        TelephonyNetworkRequest rcsRequest = new TelephonyNetworkRequest(
+                new NetworkRequest.Builder()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+                        .build(), mPhone);
         DataProfile internetDataProfile = new DataProfile.Builder()
                 .setApnSetting(INTERNET_APN_SETTING)
                 .build();
         DataProfile mmsDataProfile = new DataProfile.Builder()
                 .setApnSetting(MMS_APN_SETTING)
                 .build();
+        DataProfile rcsDataProfile = new DataProfile.Builder()
+                .setApnSetting(RCS_APN_SETTING)
+                .build();
 
         assertThat(internetRequest.canBeSatisfiedBy(internetDataProfile)).isTrue();
         assertThat(internetRequest.canBeSatisfiedBy(mmsDataProfile)).isFalse();
         assertThat(mmsRequest.canBeSatisfiedBy(internetDataProfile)).isFalse();
         assertThat(mmsRequest.canBeSatisfiedBy(mmsDataProfile)).isTrue();
+        assertThat(rcsRequest.canBeSatisfiedBy(rcsDataProfile)).isTrue();
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
index ce59cc6..a2a55ee 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -131,19 +131,16 @@
     public void testWwanSelectorCallbackAsync() throws Exception {
         mDsc = new DomainSelectionConnection(mPhone, SELECTOR_TYPE_CALLING, true,
                 mDomainSelectionController);
-        replaceInstance(DomainSelectionConnection.class, "mWwanSelectedExecutor",
-                mDsc, new Executor() {
-                    public void execute(Runnable command) {
-                        command.run();
-                    }
-                });
 
         TransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
 
         assertNotNull(transportCallback);
 
+        replaceInstance(DomainSelectionConnection.class, "mLooper",
+                mDsc, mTestableLooper.getLooper());
         Consumer<WwanSelectorCallback> consumer = Mockito.mock(Consumer.class);
         transportCallback.onWwanSelected(consumer);
+        processAllMessages();
 
         verify(consumer).accept(any());
     }
@@ -171,6 +168,7 @@
         Consumer<EmergencyRegResult> consumer = Mockito.mock(Consumer.class);
 
         wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, null, consumer);
+        processAllMessages();
 
         ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
         ArgumentCaptor<Integer> eventCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -220,11 +218,13 @@
         CancellationSignal signal = new CancellationSignal();
         wwanCallback.onRequestEmergencyNetworkScan(new ArrayList<>(),
                 SCAN_TYPE_NO_PREFERENCE, signal, Mockito.mock(Consumer.class));
+        processAllMessages();
 
         verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
         verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
         signal.cancel();
+        processAllMessages();
 
         verify(mPhone).cancelEmergencyNetworkScan(eq(false), any());
     }
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 a84ca32..2feb34d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -16,7 +16,12 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD;
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GREAT;
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_POOR;
 import static android.telephony.satellite.SatelliteManager.KEY_DEMO_MODE_ENABLED;
+import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_CAPABILITIES;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_ENABLED;
@@ -87,10 +92,12 @@
 import android.os.ResultReceiver;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteStateCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
@@ -335,7 +342,7 @@
                     mQueriedSatelliteAllowed = false;
                 }
             } else {
-                logd("mSatelliteSupportReceiver: resultCode=" + resultCode);
+                logd("mSatelliteAllowedReceiver: resultCode=" + resultCode);
                 mQueriedSatelliteAllowed = false;
             }
             try {
@@ -374,6 +381,36 @@
         }
     };
 
+    private @NtnSignalStrength.NtnSignalStrengthLevel int mQueriedNtnSignalStrengthLevel =
+            NTN_SIGNAL_STRENGTH_NONE;
+    private int mQueriedNtnSignalStrengthResultCode = SATELLITE_RESULT_SUCCESS;
+    private Semaphore mRequestNtnSignalStrengthSemaphore = new Semaphore(0);
+    private ResultReceiver mRequestNtnSignalStrengthReceiver = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mQueriedNtnSignalStrengthResultCode = resultCode;
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_NTN_SIGNAL_STRENGTH)) {
+                    NtnSignalStrength result = resultData.getParcelable(KEY_NTN_SIGNAL_STRENGTH);
+                    logd("result.getLevel()=" + result.getLevel());
+                    mQueriedNtnSignalStrengthLevel = result.getLevel();
+                } else {
+                    loge("KEY_NTN_SIGNAL_STRENGTH does not exist.");
+                    mQueriedNtnSignalStrengthLevel = NTN_SIGNAL_STRENGTH_NONE;
+                }
+            } else {
+                logd("KEY_NTN_SIGNAL_STRENGTH: resultCode=" + resultCode);
+                mQueriedNtnSignalStrengthLevel = NTN_SIGNAL_STRENGTH_NONE;
+            }
+            try {
+                mRequestNtnSignalStrengthSemaphore.release();
+            } catch (Exception ex) {
+                loge("mRequestNtnSignalStrengthReceiver: Got exception in releasing semaphore, ex="
+                        + ex);
+            }
+        }
+    };
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -2002,6 +2039,276 @@
                 SATELLITE_MODEM_STATE_CONNECTED);
     }
 
+    @Test
+    public void testRequestNtnSignalStrengthWithFeatureFlagEnabled() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+
+        resetSatelliteControllerUT();
+
+        mRequestNtnSignalStrengthSemaphore.drainPermits();
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+
+        @NtnSignalStrength.NtnSignalStrengthLevel int expectedLevel = NTN_SIGNAL_STRENGTH_GREAT;
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        /* In case request is not successful, result should be NTN_SIGNAL_STRENGTH_NONE */
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE, SATELLITE_RESULT_NOT_SUPPORTED);
+
+        resetSatelliteControllerUT();
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+
+        doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+
+        resetSatelliteControllerUT();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        // reset cache to NTN_SIGNAL_STRENGTH_NONE
+        sendNtnSignalStrengthChangedEvent(NTN_SIGNAL_STRENGTH_NONE, null);
+        processAllMessages();
+        expectedLevel = NTN_SIGNAL_STRENGTH_POOR;
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+    }
+
+    @Test
+    public void testRequestNtnSignalStrengthWithFeatureFlagDisabled() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
+
+        resetSatelliteControllerUT();
+        mRequestNtnSignalStrengthSemaphore.drainPermits();
+        doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+
+        @NtnSignalStrength.NtnSignalStrengthLevel int expectedLevel = NTN_SIGNAL_STRENGTH_GREAT;
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+
+        expectedLevel = NTN_SIGNAL_STRENGTH_POOR;
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_MODEM_ERROR);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+    }
+
+    @Test
+    public void testRegisterForNtnSignalStrengthChangedWithFeatureFlagEnabled() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+
+        Semaphore semaphore = new Semaphore(0);
+        final NtnSignalStrength[] signalStrength = new NtnSignalStrength[1];
+        INtnSignalStrengthCallback callback =
+                new INtnSignalStrengthCallback.Stub() {
+                    @Override
+                    public void onNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength) {
+                        logd("onNtnSignalStrengthChanged: ntnSignalStrength="
+                                + ntnSignalStrength);
+                        try {
+                            signalStrength[0] = ntnSignalStrength;
+                            semaphore.release();
+                        } catch (Exception ex) {
+                            loge("onNtnSignalStrengthChanged: Got exception in releasing "
+                                    + "semaphore, ex=" + ex);
+                        }
+                    }
+                };
+
+        int errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID,
+                callback);
+        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
+        @NtnSignalStrength.NtnSignalStrengthLevel int expectedLevel = NTN_SIGNAL_STRENGTH_NONE;
+
+        setUpResponseForRequestIsSatelliteSupported(false,
+                SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
+        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
+        verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_NOT_SUPPORTED);
+
+        resetSatelliteControllerUT();
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
+        verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
+
+        expectedLevel = NTN_SIGNAL_STRENGTH_GOOD;
+        sendNtnSignalStrengthChangedEvent(expectedLevel, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForNtnSignalStrengthChanged"));
+        assertEquals(expectedLevel, signalStrength[0].getLevel());
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_GOOD, SATELLITE_RESULT_SUCCESS);
+
+        expectedLevel = NTN_SIGNAL_STRENGTH_POOR;
+        sendNtnSignalStrengthChangedEvent(expectedLevel, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForNtnSignalStrengthChanged"));
+        assertEquals(expectedLevel, signalStrength[0].getLevel());
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_POOR, SATELLITE_RESULT_SUCCESS);
+
+        mSatelliteControllerUT.unregisterForNtnSignalStrengthChanged(SUB_ID, callback);
+        sendNtnSignalStrengthChangedEvent(NTN_SIGNAL_STRENGTH_GREAT, null);
+        processAllMessages();
+        assertFalse(waitForForEvents(
+                semaphore, 1, "testRegisterForNtnSignalStrengthChanged"));
+        /* Even if all listeners are unregistered, the cache is updated with the latest value when a
+         new value event occurs. */
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_GREAT, SATELLITE_RESULT_SUCCESS);
+    }
+
+    @Test
+    public void testRegisterForNtnSignalStrengthChangedWithFeatureFlagDisabled() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
+
+        Semaphore semaphore = new Semaphore(0);
+        final NtnSignalStrength[] signalStrength = new NtnSignalStrength[1];
+        INtnSignalStrengthCallback callback =
+                new INtnSignalStrengthCallback.Stub() {
+                    @Override
+                    public void onNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength) {
+                        logd("onNtnSignalStrengthChanged: ntnSignalStrength="
+                                + ntnSignalStrength);
+                        try {
+                            signalStrength[0] = ntnSignalStrength;
+                            semaphore.release();
+                        } catch (Exception ex) {
+                            loge("onNtnSignalStrengthChanged: Got exception in releasing "
+                                    + "semaphore, ex=" + ex);
+                        }
+                    }
+                };
+
+        int errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID,
+                callback);
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+
+        setUpResponseForRequestIsSatelliteSupported(false,
+                SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
+        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+        setUpResponseForRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_SUCCESS);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+
+        resetSatelliteControllerUT();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
+        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+        verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+
+        @NtnSignalStrength.NtnSignalStrengthLevel int expectedNtnSignalStrengthLevel =
+                NTN_SIGNAL_STRENGTH_GOOD;
+        sendNtnSignalStrengthChangedEvent(expectedNtnSignalStrengthLevel, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 0, "testRegisterForNtnSignalStrengthChanged"));
+    }
+
+    @Test
+    public void testSendingNtnSignalStrengthWithFeatureEnabled() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+
+        int expectedResult = SATELLITE_RESULT_SUCCESS;
+        // startSendingNtnSignalStrength() is requested when screen on event comes.
+        reset(mMockSatelliteModemInterface);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestIsSatelliteSupported(true, expectedResult);
+        setUpResponseForRequestIsSatelliteProvisioned(true, expectedResult);
+        verifySatelliteSupported(true, expectedResult);
+        verifySatelliteProvisioned(true, expectedResult);
+        setUpResponseForStartSendingNtnSignalStrength(expectedResult);
+        sendCmdStartSendingNtnSignalStrengthChangedEvent(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1))
+                .startSendingNtnSignalStrength(any(Message.class));
+
+        // stopSendingNtnSignalStrength() is requested when screen off event comes.
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForStopSendingNtnSignalStrength(expectedResult);
+        sendCmdStartSendingNtnSignalStrengthChangedEvent(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1))
+                .stopSendingNtnSignalStrength(any(Message.class));
+
+        // startSendingNtnSignalStrength() is requested but received fail from the service.
+        reset(mMockSatelliteModemInterface);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForStartSendingNtnSignalStrength(SATELLITE_RESULT_INVALID_MODEM_STATE);
+        sendCmdStartSendingNtnSignalStrengthChangedEvent(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1))
+                .startSendingNtnSignalStrength(any(Message.class));
+
+        // stopSendingNtnSignalStrength() is requested but received fail from the service.
+        reset(mMockSatelliteModemInterface);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForStopSendingNtnSignalStrength(SATELLITE_RESULT_NO_RESOURCES);
+        sendCmdStartSendingNtnSignalStrengthChangedEvent(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1))
+                .stopSendingNtnSignalStrength(any(Message.class));
+    }
+
+    @Test
+    public void testSendingNtnSignalStrengthWithFeatureDisabled() {
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
+
+        int expectedResult = SATELLITE_RESULT_SUCCESS;
+        // startSendingNtnSignalStrength() is requested when screen on event comes.
+        reset(mMockSatelliteModemInterface);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForRequestIsSatelliteSupported(true, expectedResult);
+        setUpResponseForRequestIsSatelliteProvisioned(true, expectedResult);
+        verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+        setUpResponseForStartSendingNtnSignalStrength(expectedResult);
+        sendCmdStartSendingNtnSignalStrengthChangedEvent(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, never())
+                .startSendingNtnSignalStrength(any(Message.class));
+
+        // stopSendingNtnSignalStrength() is requested when screen off event comes.
+        reset(mMockSatelliteModemInterface);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+        setUpResponseForStopSendingNtnSignalStrength(expectedResult);
+        sendCmdStartSendingNtnSignalStrengthChangedEvent(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, never())
+                .stopSendingNtnSignalStrength(any(Message.class));
+    }
+
     private void resetSatelliteControllerUTEnabledState() {
         logd("resetSatelliteControllerUTEnabledState");
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
@@ -2229,6 +2536,20 @@
         }).when(mMockSatelliteModemInterface).requestSatelliteCapabilities(any(Message.class));
     }
 
+    private void setUpResponseForRequestNtnSignalStrength(
+            @NtnSignalStrength.NtnSignalStrengthLevel int ntnSignalStrengthLevel,
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
+                ? null : new SatelliteException(error);
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[0];
+            AsyncResult.forMessage(message, new NtnSignalStrength(ntnSignalStrengthLevel),
+                    exception);
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).requestNtnSignalStrength(any(Message.class));
+    }
+
     private boolean waitForForEvents(
             Semaphore semaphore, int expectedNumberOfEvents, String caller) {
         for (int i = 0; i < expectedNumberOfEvents; i++) {
@@ -2281,6 +2602,30 @@
         }).when(mMockPointingAppController).stopSatelliteTransmissionUpdates(any(Message.class));
     }
 
+    private void setUpResponseForStartSendingNtnSignalStrength(
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
+                ? null : new SatelliteException(error);
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[0];
+            AsyncResult.forMessage(message, null, exception);
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).startSendingNtnSignalStrength(any(Message.class));
+    }
+
+    private void setUpResponseForStopSendingNtnSignalStrength(
+            @SatelliteManager.SatelliteResult int error) {
+        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
+                ? null : new SatelliteException(error);
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[0];
+            AsyncResult.forMessage(message, null, exception);
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).stopSendingNtnSignalStrength(any(Message.class));
+    }
+
     private boolean waitForRequestIsSatelliteSupportedResult(int expectedNumberOfEvents) {
         for (int i = 0; i < expectedNumberOfEvents; i++) {
             try {
@@ -2391,6 +2736,22 @@
         return true;
     }
 
+    private boolean waitForRequestNtnSignalStrengthResult(int expectedNumberOfEvents) {
+        for (int i = 0; i < expectedNumberOfEvents; i++) {
+            try {
+                if (!mRequestNtnSignalStrengthSemaphore.tryAcquire(TIMEOUT,
+                        TimeUnit.MILLISECONDS)) {
+                    loge("Timeout to receive requestNtnSignalStrength() callback");
+                    return false;
+                }
+            } catch (Exception ex) {
+                loge("requestNtnSignalStrength: Got exception=" + ex);
+                return false;
+            }
+        }
+        return true;
+    }
+
     private boolean waitForIIntegerConsumerResult(int expectedNumberOfEvents) {
         for (int i = 0; i < expectedNumberOfEvents; i++) {
             try {
@@ -2434,6 +2795,17 @@
         assertEquals(provisioned, mQueriedIsSatelliteProvisioned);
     }
 
+    private void verifyRequestNtnSignalStrength(
+            @NtnSignalStrength.NtnSignalStrengthLevel int signalStrengthLevel,
+            int expectedErrorCode) {
+        mRequestNtnSignalStrengthSemaphore.drainPermits();
+        mSatelliteControllerUT.requestNtnSignalStrength(SUB_ID, mRequestNtnSignalStrengthReceiver);
+        processAllMessages();
+        assertTrue(waitForRequestNtnSignalStrengthResult(1));
+        assertEquals(expectedErrorCode, mQueriedNtnSignalStrengthResultCode);
+        assertEquals(signalStrengthLevel, mQueriedNtnSignalStrengthLevel);
+    }
+
     private void sendProvisionedStateChangedEvent(boolean provisioned, Throwable exception) {
         Message msg = mSatelliteControllerUT.obtainMessage(
                 26 /* EVENT_SATELLITE_PROVISION_STATE_CHANGED */);
@@ -2448,6 +2820,33 @@
         msg.sendToTarget();
     }
 
+    private void sendNtnSignalStrengthChangedEvent(
+            @NtnSignalStrength.NtnSignalStrengthLevel int ntnSignalStrengthLevel,
+            Throwable exception) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                34 /* EVENT_NTN_SIGNAL_STRENGTH_CHANGED */);
+        msg.obj = new AsyncResult(null, new NtnSignalStrength(ntnSignalStrengthLevel),
+                exception);
+        msg.sendToTarget();
+    }
+
+    private void sendCmdStartSendingNtnSignalStrengthChangedEvent(boolean shouldReport) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                35 /* CMD_START_SENDING_NTN_SIGNAL_STRENGTH */);
+        msg.obj = new AsyncResult(null, shouldReport, null);
+        msg.sendToTarget();
+    }
+
+    private void sendStartSendingNtnSignalStrengthChangedEvent(
+            @NtnSignalStrength.NtnSignalStrengthLevel int ntnSignalStrengthLevel,
+            Throwable exception) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                36 /* EVENT_START_SENDING_NTN_SIGNAL_STRENGTH_DONE */);
+        msg.obj = new AsyncResult(null, new NtnSignalStrength(ntnSignalStrengthLevel),
+                exception);
+        msg.sendToTarget();
+    }
+
     private void setRadioPower(boolean on) {
         mSimulatedCommands.setRadioPower(on, false, false, null);
     }