Merge "Protect against NPE in onCallQualitychange callback." into main
diff --git a/OWNERS b/OWNERS
index 7e57200..ff1a04e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -18,4 +18,4 @@
 xiaotonj@google.com
 
 # Domain Selection code is co-owned, adding additional owners for this code
-per-file EmergencyStateTracker*=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com
+per-file EmergencyStateTracker*=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,jdyou@google.com
diff --git a/flags/data.aconfig b/flags/data.aconfig
index daddc2e..cad7da7 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -104,3 +104,10 @@
   description: "The DataCallSessionStats metrics will capture whether the IWLAN PDN is set up on cross-SIM calling."
   bug: "313956117"
 }
+
+flag {
+  name: "force_iwlan_mms"
+  namespace: "telephony"
+  description: "When QNS prefers MMS on IWLAN, MMS will be attempted on IWLAN if it can, even though if existing cellular network already supports MMS."
+  bug: "316211526"
+}
diff --git a/flags/network.aconfig b/flags/network.aconfig
index e4ae5ee..c0394e8 100644
--- a/flags/network.aconfig
+++ b/flags/network.aconfig
@@ -53,5 +53,12 @@
   name: "support_nr_sa_rrc_idle"
   namespace: "telephony"
   description: "Support RRC idle for NR SA."
-  bug: "301467052"
+  bug: "298233308"
+}
+
+flag {
+  name: "network_registration_info_reject_cause"
+  namespace: "telephony"
+  description: "Elevate NRI#getRejectCause from System to Public"
+  bug: "239730435"
 }
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index f7c2a62..d2c36d6 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -19,4 +19,11 @@
   namespace: "telephony"
   description: "Supports customized cellular service capabilities per subscription."
   bug: "296097429"
+}
+
+flag {
+  name: "data_only_service_allow_emergency_call_only"
+  namespace: "telephony"
+  description: "Support emergency call only for data only cellular service."
+  bug: "296097429"
 }
\ No newline at end of file
diff --git a/flags/telephony.aconfig b/flags/telephony.aconfig
index c6fd2c4..d59b249 100644
--- a/flags/telephony.aconfig
+++ b/flags/telephony.aconfig
@@ -19,4 +19,11 @@
     namespace: "telephony"
     description: "This flag controls the order of the binder to prevent deadlock in system_server"
     bug: "315973270"
+}
+
+flag {
+    name: "prevent_invocation_repeat_of_ril_call_when_device_does_not_support_voice"
+    namespace: "telephony"
+    description: "This flag prevents repeat invocation of call related APIs in RIL when the device is not voice capable"
+    bug: "290833783"
 }
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index 38c6672..5e617f9 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.AsyncResult;
@@ -25,8 +26,11 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.flags.FeatureFlags;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -55,6 +59,9 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected boolean mNumberConverted = false;
+
+    protected final @NonNull FeatureFlags mFeatureFlags;
+
     private final int VALID_COMPARE_LENGTH   = 3;
 
     //***** Events
@@ -77,7 +84,8 @@
     protected static final int EVENT_THREE_WAY_DIAL_BLANK_FLASH    = 20;
 
     @UnsupportedAppUsage
-    public CallTracker() {
+    public CallTracker(FeatureFlags featureFlags) {
+        mFeatureFlags = featureFlags;
     }
 
     protected void pollCallsWhenSafe() {
@@ -91,6 +99,14 @@
 
     protected void
     pollCallsAfterDelay() {
+        if (mFeatureFlags.preventInvocationRepeatOfRilCallWhenDeviceDoesNotSupportVoice()) {
+            if (!mCi.getHalVersion(TelephonyManager.HAL_SERVICE_VOICE)
+                    .greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
+                log("Skip polling because HAL_SERVICE_VOICE < RADIO_HAL_VERSION_1.4");
+                return;
+            }
+        }
+
         Message msg = obtainMessage();
 
         msg.what = EVENT_REPOLL_AFTER_DELAY;
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index d76ee19..5517bc6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -47,6 +47,7 @@
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.telephony.Rlog;
 
@@ -155,7 +156,9 @@
 
     //***** Constructors
 
-    public GsmCdmaCallTracker (GsmCdmaPhone phone) {
+    public GsmCdmaCallTracker(GsmCdmaPhone phone, FeatureFlags featureFlags) {
+        super(featureFlags);
+
         this.mPhone = phone;
         mCi = phone.mCi;
         mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 279ef3d..19851cd 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -96,6 +96,7 @@
 import android.telephony.UssdResponse;
 import android.telephony.ims.ImsCallProfile;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 
@@ -119,6 +120,7 @@
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
 import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource;
 import com.android.internal.telephony.security.NullCipherNotifier;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
@@ -304,15 +306,24 @@
     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
     private final CallWaitingController mCallWaitingController;
 
+    private CellularNetworkSecuritySafetySource mSafetySource;
     private CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier;
     private NullCipherNotifier mNullCipherNotifier;
 
+    /**
+     * Temporary placeholder variables until b/312788638 is resolved, whereupon these should be
+     * ported to TelephonyManager.
+     */
     // Set via Carrier Config
-    private boolean mIsN1ModeAllowedByCarrier = true;
+    private static final Integer N1_MODE_DISALLOWED_REASON_CARRIER = 1;
     // Set via a call to the method on Phone; the only caller is IMS, and all of this code will
     // need to be updated to a voting mechanism (...enabled for reason...) if additional callers
     // are desired.
-    private boolean mIsN1ModeAllowedByIms = true;
+    private static final Integer N1_MODE_DISALLOWED_REASON_IMS = 2;
+
+    // Set of use callers/reasons why N1 Mode is disallowed. If the set is empty, it's allowed.
+    private final Set<Integer> mN1ModeDisallowedReasons = new ArraySet<>();
+
     // If this value is null, then the modem value is unknown. If a caller explicitly sets the
     // N1 mode, this value will be initialized before any attempt to set the value in the modem.
     private Boolean mModemN1Mode = null;
@@ -464,7 +475,7 @@
         }
 
         mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName())
-                .makeGsmCdmaCallTracker(this);
+                .makeGsmCdmaCallTracker(this, mFeatureFlags);
         mIccPhoneBookIntManager = mTelephonyComponentFactory
                 .inject(IccPhoneBookInterfaceManager.class.getName())
                 .makeIccPhoneBookInterfaceManager(this);
@@ -525,6 +536,12 @@
 
         mCi.registerForImeiMappingChanged(this, EVENT_IMEI_MAPPING_CHANGED, null);
 
+        if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
+                || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
+            mSafetySource =
+                    mTelephonyComponentFactory.makeCellularNetworkSecuritySafetySource(mContext);
+        }
+
         if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()) {
             logi(
                     "enable_identifier_disclosure_transparency_unsol_events is on. Registering for "
@@ -533,7 +550,7 @@
             mIdentifierDisclosureNotifier =
                     mTelephonyComponentFactory
                             .inject(CellularIdentifierDisclosureNotifier.class.getName())
-                            .makeIdentifierDisclosureNotifier();
+                            .makeIdentifierDisclosureNotifier(mSafetySource);
             mCi.registerForCellularIdentifierDisclosures(
                     this, EVENT_CELL_IDENTIFIER_DISCLOSURE, null);
         }
@@ -1842,8 +1859,11 @@
             boolean check = true;
             for (int itr = 0;itr < dtmfString.length(); itr++) {
                 if (!PhoneNumberUtils.is12Key(dtmfString.charAt(itr))) {
-                    Rlog.e(LOG_TAG,
-                            "sendDtmf called with invalid character '" + dtmfString.charAt(itr)+ "'");
+                    Rlog.e(
+                            LOG_TAG,
+                            "sendDtmf called with invalid character '"
+                                    + dtmfString.charAt(itr)
+                                    + "'");
                     check = false;
                     break;
                 }
@@ -2423,7 +2443,11 @@
             // This might be called by IMS on another thread, so to avoid the requirement to
             // lock, post it through the handler.
             post(() -> {
-                mIsN1ModeAllowedByIms = enable;
+                if (enable) {
+                    mN1ModeDisallowedReasons.remove(N1_MODE_DISALLOWED_REASON_IMS);
+                } else {
+                    mN1ModeDisallowedReasons.add(N1_MODE_DISALLOWED_REASON_IMS);
+                }
                 if (mModemN1Mode == null) {
                     mCi.isN1ModeEnabled(obtainMessage(EVENT_GET_N1_MODE_ENABLED_DONE, result));
                 } else {
@@ -2437,7 +2461,7 @@
 
     /** Only called on the handler thread. */
     private void maybeUpdateModemN1Mode(@Nullable Message result) {
-        final boolean wantN1Enabled = mIsN1ModeAllowedByCarrier && mIsN1ModeAllowedByIms;
+        final boolean wantN1Enabled = mN1ModeDisallowedReasons.isEmpty();
 
         logd("N1 Mode: isModemN1Enabled=" + mModemN1Mode + ", wantN1Enabled=" + wantN1Enabled);
 
@@ -2458,11 +2482,20 @@
     private void updateCarrierN1ModeSupported(@NonNull PersistableBundle b) {
         if (!mFeatureFlags.enableCarrierConfigN1Control()) return;
 
+        if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) return;
+
         final int[] supportedNrModes = b.getIntArray(
                 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
 
-        mIsN1ModeAllowedByCarrier = ArrayUtils.contains(
-                supportedNrModes, CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA);
+
+        if (ArrayUtils.contains(
+                supportedNrModes,
+                CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA)) {
+            mN1ModeDisallowedReasons.remove(N1_MODE_DISALLOWED_REASON_CARRIER);
+        } else {
+            mN1ModeDisallowedReasons.add(N1_MODE_DISALLOWED_REASON_CARRIER);
+        }
+
         if (mModemN1Mode == null) {
             mCi.isN1ModeEnabled(obtainMessage(EVENT_GET_N1_MODE_ENABLED_DONE));
         } else {
@@ -2597,7 +2630,7 @@
             Bundle extras = new Bundle();
             extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
 
-            final TelecomManager telecomManager = TelecomManager.from(mContext);
+            final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
             telecomManager.placeCall(
                     Uri.fromParts(PhoneAccount.SCHEME_TEL, cfNumber, null), extras);
 
@@ -2850,13 +2883,15 @@
             mCi.setCallWaiting(enable, serviceClass, onComplete);
         } else if (mSsOverCdmaSupported) {
             String cwPrefix = CdmaMmiCode.getCallWaitingPrefix(enable);
-            Rlog.i(LOG_TAG, "setCallWaiting in CDMA : dial for set call waiting" + " prefix= " + cwPrefix);
+            Rlog.i(
+                    LOG_TAG,
+                    "setCallWaiting in CDMA : dial for set call waiting" + " prefix= " + cwPrefix);
 
             PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(getSubId());
             Bundle extras = new Bundle();
             extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
 
-            final TelecomManager telecomManager = TelecomManager.from(mContext);
+            final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
             telecomManager.placeCall(
                     Uri.fromParts(PhoneAccount.SCHEME_TEL, cwPrefix, null), extras);
 
@@ -3710,7 +3745,7 @@
                 if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
                         && mIdentifierDisclosureNotifier != null
                         && disclosure != null) {
-                    mIdentifierDisclosureNotifier.addDisclosure(disclosure);
+                    mIdentifierDisclosureNotifier.addDisclosure(mContext, getSubId(), disclosure);
                 }
                 break;
 
@@ -4995,7 +5030,7 @@
     }
 
     private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
-        final TelecomManager telecomManager = TelecomManager.from(mContext);
+        final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
         final TelephonyManager telephonyManager = TelephonyManager.from(mContext);
         final Iterator<PhoneAccountHandle> phoneAccounts =
             telecomManager.getCallCapablePhoneAccounts(true).listIterator();
@@ -5216,7 +5251,7 @@
     }
 
     private void updateVoNrSettings(@NonNull PersistableBundle config) {
-        if (mSimState != TelephonyManager.SIM_STATE_LOADED) {
+        if (getIccCard().getState() != IccCardConstants.State.LOADED) {
             return;
         }
 
@@ -5353,11 +5388,11 @@
         // enable/disable API, so we only toggle the enable state if the unsol events feature
         // flag is enabled.
         if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()) {
-          if (prefEnabled) {
-            mIdentifierDisclosureNotifier.enable();
-          } else {
-            mIdentifierDisclosureNotifier.disable();
-          }
+            if (prefEnabled) {
+                mIdentifierDisclosureNotifier.enable(mContext);
+            } else {
+                mIdentifierDisclosureNotifier.disable(mContext);
+            }
         } else {
             logi("Not toggling enable state for disclosure notifier. Feature flag "
                     + "enable_identifier_disclosure_transparency_unsol_events is disabled");
@@ -5408,4 +5443,12 @@
     public boolean isNullCipherNotificationSupported() {
         return mIsNullCipherNotificationSupported;
     }
+
+    @Override
+    public void refreshSafetySources(String refreshBroadcastId) {
+        if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
+                || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
+            mSafetySource.refresh(mContext, refreshBroadcastId);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index d07e731..8488ab0 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -420,22 +420,13 @@
             return;
         }
 
-        // b/153860050 Occasionally we receive carrier config change broadcast without subId
-        // being specified in it. So here we do additional check to make sur we don't miss the
-        // subId.
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            subId = SubscriptionManager.getSubscriptionId(phoneId);
-            if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                CarrierConfigManager cm = mContext.getSystemService(CarrierConfigManager.class);
-                if (cm != null && cm.getConfigForSubId(subId) != null) {
-                    loge("onCarrierConfigChanged with invalid subId while subId "
-                            + subId + " is active and its config is loaded");
-                }
+        CarrierConfigManager cm = mContext.getSystemService(CarrierConfigManager.class);
+        if (cm != null) {
+            if (CarrierConfigManager.isConfigForIdentifiedCarrier(cm.getConfigForSubId(subId))) {
+                mCarrierConfigLoadedSubIds[phoneId] = subId;
+                reEvaluateAll();
             }
         }
-
-        mCarrierConfigLoadedSubIds[phoneId] = subId;
-        reEvaluateAll();
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index ea7a6de..1ab86f4 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -366,7 +366,8 @@
         String overrideSecondaryTimerRule = config.getString(
                 CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
         createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
-        updatePhysicalChannelConfigs();
+        updatePhysicalChannelConfigs(
+                mPhone.getServiceStateTracker().getPhysicalChannelConfigList());
     }
 
     private void createTimerRules(String icons, String timers, String secondaryTimers) {
@@ -598,6 +599,7 @@
     private final class DefaultState extends State {
         @Override
         public boolean processMessage(Message msg) {
+            AsyncResult ar;
             if (DBG) log("DefaultState: process " + getEventName(msg.what));
             switch (msg.what) {
                 case EVENT_UPDATE:
@@ -623,12 +625,12 @@
                     transitionToCurrentState();
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED:
-                    AsyncResult result = (AsyncResult) msg.obj;
-                    mIsPhysicalChannelConfigOn = (boolean) result.result;
+                    ar = (AsyncResult) msg.obj;
+                    mIsPhysicalChannelConfigOn = (boolean) ar.result;
                     if (DBG) {
                         log("mIsPhysicalChannelConfigOn changed to: " + mIsPhysicalChannelConfigOn);
                     }
@@ -668,7 +670,8 @@
                     transitionToCurrentState();
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                     }
@@ -720,6 +723,7 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("LegacyState: process " + getEventName(msg.what));
             updateTimers();
+            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_SERVICE_STATE_CHANGED:
                     mServiceState = mPhone.getServiceStateTracker().getServiceState();
@@ -748,7 +752,8 @@
                     mIsNrRestricted = isNrRestricted();
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                         if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
@@ -758,7 +763,7 @@
                     }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
                         if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
@@ -801,6 +806,7 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("IdleState: process " + getEventName(msg.what));
             updateTimers();
+            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_SERVICE_STATE_CHANGED:
                     mServiceState = mPhone.getServiceStateTracker().getServiceState();
@@ -829,7 +835,8 @@
                     }
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                         if (isPhysicalLinkActive()) {
@@ -841,7 +848,7 @@
                     }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     if (isPhysicalLinkActive()) {
                         transitionWithTimerTo(mLteConnectedState);
@@ -885,6 +892,7 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("LteConnectedState: process " + getEventName(msg.what));
             updateTimers();
+            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_SERVICE_STATE_CHANGED:
                     mServiceState = mPhone.getServiceStateTracker().getServiceState();
@@ -913,7 +921,8 @@
                     }
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                         if (!isPhysicalLinkActive()) {
@@ -925,7 +934,7 @@
                     }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     if (!isPhysicalLinkActive()) {
                         transitionWithTimerTo(mIdleState);
@@ -969,6 +978,7 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("NrIdleState: process " + getEventName(msg.what));
             updateTimers();
+            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_SERVICE_STATE_CHANGED:
                     mServiceState = mPhone.getServiceStateTracker().getServiceState();
@@ -994,7 +1004,8 @@
                     }
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                     }
@@ -1006,7 +1017,7 @@
                     }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     if (isPhysicalLinkActive()) {
                         transitionWithTimerTo(mNrConnectedState);
@@ -1047,6 +1058,7 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
             updateTimers();
+            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_SERVICE_STATE_CHANGED:
                     mServiceState = mPhone.getServiceStateTracker().getServiceState();
@@ -1072,7 +1084,8 @@
                     }
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                     }
@@ -1084,7 +1097,7 @@
                     }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     if (!isPhysicalLinkActive() && mFeatureFlags.supportNrSaRrcIdle()) {
                         transitionWithTimerTo(mNrIdleState);
@@ -1126,6 +1139,7 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("NrConnectedAdvancedState: process " + getEventName(msg.what));
             updateTimers();
+            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_SERVICE_STATE_CHANGED:
                     mServiceState = mPhone.getServiceStateTracker().getServiceState();
@@ -1158,7 +1172,8 @@
                     }
                     break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
-                    updatePhysicalChannelConfigs();
+                    ar = (AsyncResult) msg.obj;
+                    updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                     }
@@ -1170,7 +1185,7 @@
                     }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
-                    AsyncResult ar = (AsyncResult) msg.obj;
+                    ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
                     break;
                 default:
@@ -1191,9 +1206,7 @@
     private final NrConnectedAdvancedState mNrConnectedAdvancedState =
             new NrConnectedAdvancedState();
 
-    private void updatePhysicalChannelConfigs() {
-        List<PhysicalChannelConfig> physicalChannelConfigs =
-                mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+    private void updatePhysicalChannelConfigs(List<PhysicalChannelConfig> physicalChannelConfigs) {
         boolean isPccListEmpty = physicalChannelConfigs == null || physicalChannelConfigs.isEmpty();
         if (isPccListEmpty && isUsingPhysicalChannelConfigForRrcDetection()) {
             log("Physical channel configs updated: not updating PCC fields for empty PCC list "
@@ -1249,7 +1262,7 @@
         } else {
             if (mFeatureFlags.supportNrSaRrcIdle() && mDoesPccListIndicateIdle
                     && isUsingPhysicalChannelConfigForRrcDetection()
-                    && !mPrimaryCellChangedWhileIdle && isTimerActiveForNrSaRrcIdle()
+                    && !mPrimaryCellChangedWhileIdle && isTimerActiveForRrcIdle()
                     && !isNrAdvancedForPccFields(nrBandwidths, nrBands)) {
                 log("Allow primary cell change during RRC idle timer without changing state: "
                         + mLastAnchorNrCellId + " -> " + anchorNrCellId);
@@ -1349,7 +1362,9 @@
 
         String currentState = getCurrentState().getName();
 
-        if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()) {
+        if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()
+                && getDataNetworkType()
+                == mDisplayInfoController.getTelephonyDisplayInfo().getNetworkType()) {
             // remove primary timer if device goes back to the original icon
             if (DBG) {
                 log("Remove primary timer since icon of primary state and current icon equal: "
@@ -1403,11 +1418,13 @@
         mSecondaryTimerState = "";
     }
 
-    private boolean isTimerActiveForNrSaRrcIdle() {
+    private boolean isTimerActiveForRrcIdle() {
         if (mIsPrimaryTimerActive) {
-            return mPrimaryTimerState.equals(STATE_CONNECTED_RRC_IDLE);
+            return mPrimaryTimerState.equals(STATE_CONNECTED_RRC_IDLE)
+                    || mPrimaryTimerState.equals(STATE_NOT_RESTRICTED_RRC_IDLE);
         } else if (mIsSecondaryTimerActive) {
-            return mSecondaryTimerState.equals(STATE_CONNECTED_RRC_IDLE);
+            return mSecondaryTimerState.equals(STATE_CONNECTED_RRC_IDLE)
+                    || mSecondaryTimerState.equals(STATE_NOT_RESTRICTED_RRC_IDLE);
         } else {
             return false;
         }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 3b47670..97eb447 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -5214,6 +5214,12 @@
     }
 
     /**
+     * Refresh the safety sources in response to the identified broadcast.
+     */
+    public void refreshSafetySources(String refreshBroadcastId) {
+    }
+
+    /**
      * Notifies the IMS call status to the modem.
      *
      * @param imsCallInfo The list of {@link ImsCallInfo}.
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 1e0aa3a..7141f37 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -34,6 +34,7 @@
 import android.telephony.PhoneCapability;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyRegistryManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -46,7 +47,10 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * This class manages phone's configuration which defines the potential capability (static) of the
@@ -74,7 +78,10 @@
     private static PhoneConfigurationManager sInstance = null;
     private final Context mContext;
     private PhoneCapability mStaticCapability;
-    private Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>();
+    private final Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>(3);
+    private final Set<Integer> mSubIdsSupportingSimultaneousCellularCalls = new HashSet<>(3);
+    private final HashSet<Consumer<Set<Integer>>> mSimultaneousCellularCallingListeners =
+            new HashSet<>(1);
     private final RadioConfig mRadioConfig;
     private final Handler mHandler;
     // mPhones is obtained from PhoneFactory and can have phones corresponding to inactive modems as
@@ -148,6 +155,25 @@
         }
     }
 
+    /**
+     * Updates the mapping between the slot IDs that support simultaneous calling and the
+     * associated sub IDs as well as notifies listeners.
+     */
+    private void updateSimultaneousSubIdsFromPhoneIdMappingAndNotify() {
+        if (!mFeatureFlags.simultaneousCallingIndications()) return;
+        Set<Integer> slotCandidates = mSlotsSupportingSimultaneousCellularCalls.stream()
+                .map(i -> mPhones[i].getSubId())
+                .filter(i ->i > SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+                        .collect(Collectors.toSet());
+        if (mSubIdsSupportingSimultaneousCellularCalls.equals(slotCandidates))  return;
+        log("updateSimultaneousSubIdsFromPhoneIdMapping update: "
+                + mSubIdsSupportingSimultaneousCellularCalls + " -> " + slotCandidates);
+        mSubIdsSupportingSimultaneousCellularCalls.clear();
+        mSubIdsSupportingSimultaneousCellularCalls.addAll(slotCandidates);
+        mNotifier.notifySimultaneousCellularCallingSubscriptionsChanged(
+                mSubIdsSupportingSimultaneousCellularCalls);
+    }
+
     private void registerForRadioState(Phone phone) {
         phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone);
     }
@@ -163,12 +189,16 @@
     /**
      * If virtual DSDA is enabled for this UE, then increase maxActiveVoiceSubscriptions to 2.
      */
-    private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions(
+    private PhoneCapability maybeOverrideMaxActiveVoiceSubscriptions(
             final PhoneCapability staticCapability) {
-        if (staticCapability.getLogicalModemList().size() > 1 && mVirtualDsdaEnabled) {
+        boolean isVDsdaEnabled = staticCapability.getLogicalModemList().size() > 1
+                && mVirtualDsdaEnabled;
+        boolean isBkwdCompatDsdaEnabled = mFeatureFlags.simultaneousCallingIndications()
+                && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA);
+        if (isVDsdaEnabled || isBkwdCompatDsdaEnabled) {
             // Since we already initialized maxActiveVoiceSubscriptions to the count the
-            // modem is capable of, vDSDA is only able to increase that count via this method. We do
-            // not allow vDSDA to decrease maxActiveVoiceSubscriptions:
+            // modem is capable of, we are only able to increase that count via this method. We do
+            // not allow a decrease of maxActiveVoiceSubscriptions:
             int updatedMaxActiveVoiceSubscriptions =
                     Math.max(staticCapability.getMaxActiveVoiceSubscriptions(), 2);
             return new PhoneCapability.Builder(staticCapability)
@@ -180,13 +210,39 @@
     }
 
     private void maybeEnableCellularDSDASupport() {
-        if (mRadioConfig != null && mRadioConfig.getRadioConfigProxy(null)
-                .getVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_2_2) &&
-                getPhoneCount() > 1 &&
-                mStaticCapability.getMaxActiveVoiceSubscriptions() > 1) {
+        boolean bkwdsCompatDsda = mFeatureFlags.simultaneousCallingIndications()
+                && getPhoneCount() > 1
+                && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA);
+        boolean halSupportSimulCalling = mRadioConfig != null
+                && mRadioConfig.getRadioConfigProxy(null).getVersion().greaterOrEqual(
+                        RIL.RADIO_HAL_VERSION_2_2)
+                && getPhoneCount() > 1 && mStaticCapability.getMaxActiveVoiceSubscriptions() > 1;
+        // Register for simultaneous calling support changes in the modem if the HAL supports it
+        if (halSupportSimulCalling) {
             updateSimultaneousCallingSupport();
             mRadioConfig.registerForSimultaneousCallingSupportStatusChanged(mHandler,
                     EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED, null);
+        } else if (bkwdsCompatDsda) {
+            // For older devices that only declare that they support DSDA via modem config,
+            // set DSDA as capable now statically.
+            log("DSDA modem config detected - setting DSDA enabled");
+            for (Phone p : mPhones) {
+                mSlotsSupportingSimultaneousCellularCalls.add(p.getPhoneId());
+            }
+            updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+            notifySimultaneousCellularCallingSlotsChanged();
+        }
+        // Register for subId updates to notify listeners when simultaneous calling is configured
+        if (mFeatureFlags.simultaneousCallingIndications()
+                && (bkwdsCompatDsda || halSupportSimulCalling)) {
+            mContext.getSystemService(TelephonyRegistryManager.class)
+                    .addOnSubscriptionsChangedListener(
+                            new SubscriptionManager.OnSubscriptionsChangedListener() {
+                                @Override
+                                public void onSubscriptionsChanged() {
+                                    updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+                                }
+                            }, mHandler::post);
         }
     }
 
@@ -291,7 +347,6 @@
                         if (mSlotsSupportingSimultaneousCellularCalls.size() > getPhoneCount()) {
                             loge("Invalid size of DSDA slots. Disabling cellular DSDA.");
                             mSlotsSupportingSimultaneousCellularCalls.clear();
-                            break;
                         }
                     } else {
                         log(msg.what + " failure. Not getting logical slots that support "
@@ -299,8 +354,8 @@
                         mSlotsSupportingSimultaneousCellularCalls.clear();
                     }
                     if (mFeatureFlags.simultaneousCallingIndications()) {
-                        mNotifier.notifySimultaneousCellularCallingSubscriptionsChanged(
-                                mSlotsSupportingSimultaneousCellularCalls);
+                        updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+                        notifySimultaneousCellularCallingSlotsChanged();
                     }
                     break;
                 default:
@@ -439,7 +494,7 @@
                     mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
             mRadioConfig.getPhoneCapability(callback);
         }
-        mStaticCapability = maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
+        mStaticCapability = maybeOverrideMaxActiveVoiceSubscriptions(mStaticCapability);
         log("getStaticPhoneCapability: mStaticCapability " + mStaticCapability);
         return mStaticCapability;
     }
@@ -455,8 +510,31 @@
         return mStaticCapability.getMaxActiveDataSubscriptions();
     }
 
+    /**
+     * Register to listen to changes in the Phone slots that support simultaneous calling.
+     * @param consumer A consumer that will be used to consume the new slots supporting simultaneous
+     *                 cellular calling when it changes.
+     */
+    public void registerForSimultaneousCellularCallingSlotsChanged(
+            Consumer<Set<Integer>> consumer) {
+        mSimultaneousCellularCallingListeners.add(consumer);
+    }
+
+    private void notifySimultaneousCellularCallingSlotsChanged() {
+        log("notifying listeners of changes to simultaneous cellular calling - new state:"
+                + mSlotsSupportingSimultaneousCellularCalls);
+        for (Consumer<Set<Integer>> consumer : mSimultaneousCellularCallingListeners) {
+            try {
+                consumer.accept(new HashSet<>(mSlotsSupportingSimultaneousCellularCalls));
+            } catch (Exception e) {
+                log("Unexpected Exception encountered when notifying listener: " + e);
+            }
+        }
+    }
+
     private void notifyCapabilityChanged() {
-        mNotifier.notifyPhoneCapabilityChanged(mStaticCapability);
+        mNotifier.notifyPhoneCapabilityChanged(maybeOverrideMaxActiveVoiceSubscriptions(
+                mStaticCapability));
     }
 
     /**
@@ -540,6 +618,10 @@
             } else {
                 // The number of active modems is 0 or 1, disable cellular DSDA:
                 mSlotsSupportingSimultaneousCellularCalls.clear();
+                if (mFeatureFlags.simultaneousCallingIndications()) {
+                    updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+                    notifySimultaneousCellularCallingSlotsChanged();
+                }
             }
 
             // When the user enables DSDS mode, the default VOICE and SMS subId should be switched
@@ -717,6 +799,13 @@
                 Context context, int numOfActiveModems) {
             PhoneFactory.onMultiSimConfigChanged(context, numOfActiveModems);
         }
+
+        /**
+         * Wrapper function to query the sysprop for multi_sim_config
+         */
+        public Optional<String> getMultiSimProperty() {
+            return TelephonyProperties.multi_sim_config();
+        }
     }
 
     private static void log(String s) {
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java
index 164ec7d..59defc3 100644
--- a/src/java/com/android/internal/telephony/SimResponse.java
+++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -112,6 +112,8 @@
             android.hardware.radio.sim.CarrierRestrictions carrierRestrictions,
             int multiSimPolicy) {
         RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
+        boolean carrierLockInfoSupported = mRil.getHalVersion(HAL_SERVICE_SIM).greater(
+                RIL.RADIO_HAL_VERSION_2_2);
         if (rr == null) {
             return;
         }
@@ -132,7 +134,8 @@
                 RILUtils.convertAidlCarrierInfoList(
                         carrierRestrictions.allowedCarrierInfoList)).setExcludedCarrierInfo(
                 RILUtils.convertAidlCarrierInfoList(
-                        carrierRestrictions.excludedCarrierInfoList)).build();
+                        carrierRestrictions.excludedCarrierInfoList)).setCarrierLockInfoFeature(
+                carrierLockInfoSupported).build();
         if (responseInfo.error == RadioError.NONE) {
             RadioResponse.sendMessageResponse(rr.mResult, ret);
         }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 8b41f6e..f5aa074 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -48,6 +48,7 @@
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.telephony.nitz.NitzStateMachineImpl;
 import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource;
 import com.android.internal.telephony.security.NullCipherNotifier;
 import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.UiccCard;
@@ -278,8 +279,14 @@
         return sInstance;
     }
 
-    public GsmCdmaCallTracker makeGsmCdmaCallTracker(GsmCdmaPhone phone) {
-        return new GsmCdmaCallTracker(phone);
+    /**
+     * Create a new GsmCdmaCallTracker
+     * @param phone GsmCdmaPhone
+     * @param featureFlags Telephony feature flag
+     */
+    public GsmCdmaCallTracker makeGsmCdmaCallTracker(GsmCdmaPhone phone,
+            @NonNull FeatureFlags featureFlags) {
+        return new GsmCdmaCallTracker(phone, featureFlags);
     }
 
     public SmsStorageMonitor makeSmsStorageMonitor(Phone phone) {
@@ -569,9 +576,16 @@
         return new DataSettingsManager(phone, dataNetworkController, looper, callback);
     }
 
+    /** Create CellularNetworkSecuritySafetySource. */
+    public CellularNetworkSecuritySafetySource makeCellularNetworkSecuritySafetySource(
+            Context context) {
+        return CellularNetworkSecuritySafetySource.getInstance(context);
+    }
+
     /** Create CellularIdentifierDisclosureNotifier. */
-    public CellularIdentifierDisclosureNotifier makeIdentifierDisclosureNotifier() {
-        return CellularIdentifierDisclosureNotifier.getInstance();
+    public CellularIdentifierDisclosureNotifier makeIdentifierDisclosureNotifier(
+            CellularNetworkSecuritySafetySource safetySource) {
+        return CellularIdentifierDisclosureNotifier.getInstance(safetySource);
     }
 
     /** Create NullCipherNotifier. */
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index e8cd8f0..8cffe72 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -38,6 +38,7 @@
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.NetworkRegistrationInfo.RegistrationState;
+import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -48,6 +49,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -126,7 +128,7 @@
 
     private final @NonNull LocalLog mLocalLog = new LocalLog(128);
     private final @NonNull Context mContext;
-    private final @NonNull FeatureFlags mFlags;
+    private static @NonNull FeatureFlags sFeatureFlags = new FeatureFlagsImpl();
     private final @NonNull SubscriptionManagerService mSubscriptionManagerService;
     private final @NonNull PhoneSwitcher mPhoneSwitcher;
     private final @NonNull AutoDataSwitchControllerCallback mPhoneSwitcherCallback;
@@ -173,7 +175,10 @@
          * How preferred the current phone is.
          */
         enum UsableState {
-            HOME(1), ROAMING_ENABLED(0), NOT_USABLE(-1);
+            HOME(2),
+            ROAMING_ENABLED(1),
+            NON_TERRESTRIAL(0),
+            NOT_USABLE(-1);
             /**
              * The higher the score, the more preferred.
              * HOME is preferred over ROAMING assuming roaming is metered.
@@ -186,8 +191,7 @@
         /** The phone */
         @NonNull private final Phone mPhone;
         /** Data registration state of the phone */
-        @RegistrationState private int mDataRegState = NetworkRegistrationInfo
-                .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+        @RegistrationState private int mDataRegState;
         /** Current Telephony display info of the phone */
         @NonNull private TelephonyDisplayInfo mDisplayInfo;
         /** Signal strength of the phone */
@@ -196,6 +200,10 @@
         private boolean mListeningForEvents;
         private PhoneSignalStatus(@NonNull Phone phone) {
             this.mPhone = phone;
+            this.mDataRegState = phone.getServiceState().getNetworkRegistrationInfo(
+                            NetworkRegistrationInfo.DOMAIN_PS,
+                            AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .getRegistrationState();
             this.mDisplayInfo = phone.getDisplayInfoController().getTelephonyDisplayInfo();
             this.mSignalStrength = phone.getSignalStrength();
         }
@@ -214,12 +222,24 @@
          * @return The current usable state of the phone.
          */
         private UsableState getUsableState() {
+            ServiceState serviceState = mPhone.getServiceState();
+            boolean isUsingNonTerrestrialNetwork = sFeatureFlags.carrierEnabledSatelliteFlag()
+                    && (serviceState != null) && serviceState.isUsingNonTerrestrialNetwork();
+
             switch (mDataRegState) {
                 case NetworkRegistrationInfo.REGISTRATION_STATE_HOME:
+                    if (isUsingNonTerrestrialNetwork) {
+                        return UsableState.NON_TERRESTRIAL;
+                    }
                     return UsableState.HOME;
                 case NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING:
-                    return mPhone.getDataRoamingEnabled()
-                            ? UsableState.ROAMING_ENABLED : UsableState.NOT_USABLE;
+                    if (mPhone.getDataRoamingEnabled()) {
+                        if (isUsingNonTerrestrialNetwork) {
+                            return UsableState.NON_TERRESTRIAL;
+                        }
+                        return UsableState.ROAMING_ENABLED;
+                    }
+                    return UsableState.NOT_USABLE;
                 default:
                     return UsableState.NOT_USABLE;
             }
@@ -275,7 +295,7 @@
             @NonNull AutoDataSwitchControllerCallback phoneSwitcherCallback) {
         super(looper);
         mContext = context;
-        mFlags = featureFlags;
+        sFeatureFlags = featureFlags;
         mSubscriptionManagerService = SubscriptionManagerService.getInstance();
         mPhoneSwitcher = phoneSwitcher;
         mPhoneSwitcherCallback = phoneSwitcherCallback;
@@ -599,7 +619,7 @@
             boolean backToDefault = false;
             boolean needValidation = true;
 
-            if (mFlags.autoSwitchAllowRoaming()) {
+            if (sFeatureFlags.autoSwitchAllowRoaming()) {
                 if (mDefaultNetworkIsOnNonCellular) {
                     debugMessage.append(", back to default as default network")
                             .append(" is active on nonCellular transport");
@@ -719,7 +739,7 @@
             return INVALID_PHONE_INDEX;
         }
 
-        if (mFlags.autoSwitchAllowRoaming()) {
+        if (sFeatureFlags.autoSwitchAllowRoaming()) {
             // check whether primary and secondary signal status are worth switching
             if (!isRatSignalStrengthBasedSwitchEnabled()
                     && isHomeService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
@@ -741,7 +761,7 @@
 
             Phone secondaryDataPhone = null;
             PhoneSignalStatus candidatePhoneStatus = mPhonesSignalStatus[phoneId];
-            if (mFlags.autoSwitchAllowRoaming()) {
+            if (sFeatureFlags.autoSwitchAllowRoaming()) {
                 PhoneSignalStatus.UsableState currentUsableState =
                         mPhonesSignalStatus[defaultPhoneId].getUsableState();
                 PhoneSignalStatus.UsableState candidatePhoneUsableRank =
@@ -811,7 +831,7 @@
      * @return {@code true} If the feature of switching base on RAT and signal strength is enabled.
      */
     private boolean isRatSignalStrengthBasedSwitchEnabled() {
-        return mFlags.autoDataSwitchRatSs() && mScoreTolerance >= 0;
+        return sFeatureFlags.autoDataSwitchRatSs() && mScoreTolerance >= 0;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 7c500f8..d52a3b4 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -31,6 +31,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkProvider;
+import android.net.NetworkRequest;
 import android.net.NetworkScore;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -91,6 +92,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RIL;
+import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
 import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
@@ -743,6 +745,11 @@
     private @Nullable Consumer<Integer> mNetworkValidationResultCodeCallback;
 
     /**
+     * Callback used to listen QNS preference changes.
+     */
+    private @Nullable AccessNetworksManagerCallback mAccessNetworksManagerCallback;
+
+    /**
      * The network bandwidth.
      */
     public static class NetworkBandwidth {
@@ -1172,6 +1179,23 @@
                         getHandler(), EVENT_VOICE_CALL_ENDED, null);
             }
 
+            if (mFlags.forceIwlanMms()) {
+                if (mDataProfile.canSatisfy(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+                    mAccessNetworksManagerCallback = new AccessNetworksManagerCallback(
+                            getHandler()::post) {
+                        @Override
+                        public void onPreferredTransportChanged(
+                                @NetCapability int networkCapability) {
+                            if (networkCapability == NetworkCapabilities.NET_CAPABILITY_MMS) {
+                                log("MMS preference changed.");
+                                updateNetworkCapabilities();
+                            }
+                        }
+                    };
+                    mAccessNetworksManager.registerCallback(mAccessNetworksManagerCallback);
+                }
+            }
+
             // Only add symmetric code here, for example, registering and unregistering.
             // DefaultState.enter() is the starting point in the life cycle of the DataNetwork,
             // and DefaultState.exit() is the end. For non-symmetric initializing works, put them
@@ -1181,6 +1205,10 @@
         @Override
         public void exit() {
             logv("Unregistering all events.");
+            if (mFlags.forceIwlanMms() && mAccessNetworksManagerCallback != null) {
+                mAccessNetworksManager.unregisterCallback(mAccessNetworksManagerCallback);
+            }
+
             // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
             if (mPhone.getImsPhone() != null) {
                 mPhone.getImsPhone().getCallTracker().unregisterForVoiceCallStarted(getHandler());
@@ -2333,6 +2361,35 @@
             builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         }
 
+        // Check if the feature force MMS on IWLAN is enabled. When the feature is enabled, MMS
+        // will be attempted on IWLAN if possible, even if existing cellular networks already
+        // supports IWLAN.
+        if (mFlags.forceIwlanMms() && builder.build()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+            // If QNS sets MMS preferred on IWLAN, and it is possible to setup an MMS network on
+            // IWLAN, then we need to remove the MMS capability on the cellular network. This will
+            // allow the new MMS network to be brought up on IWLAN when MMS network request arrives.
+            if (mAccessNetworksManager.getPreferredTransportByNetworkCapability(
+                    NetworkCapabilities.NET_CAPABILITY_MMS)
+                    == AccessNetworkConstants.TRANSPORT_TYPE_WLAN && mTransport
+                    == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+
+                DataProfile dataProfile = mDataNetworkController.getDataProfileManager()
+                        .getDataProfileForNetworkRequest(new TelephonyNetworkRequest(
+                                new NetworkRequest.Builder().addCapability(
+                                NetworkCapabilities.NET_CAPABILITY_MMS).build(), mPhone),
+                        TelephonyManager.NETWORK_TYPE_IWLAN, false, false, false);
+                // If we find another data data profile that can support MMS on IWLAN, then remove
+                // the MMS capability from this cellular network. This will allow IWLAN to be
+                // brought up for MMS later.
+                if (dataProfile != null && !dataProfile.equals(mDataProfile)) {
+                    log("Found a different data profile " + mDataProfile.getApn()
+                            + " that can serve MMS on IWLAN.");
+                    builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+                }
+            }
+        }
+
         // If one of the capabilities are for special use, for example, IMS, CBS, then this
         // network should be restricted, regardless data is enabled or not.
         if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 7f70e32..d16387f 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -3784,6 +3784,11 @@
      * de-registered yet.
      */
     private boolean isSafeToTearDown(@NonNull DataNetwork dataNetwork) {
+        if (dataNetwork.hasNetworkCapabilityInNetworkRequests(
+                NetworkCapabilities.NET_CAPABILITY_EIMS)) {
+            // FWK currently doesn't track emergency registration state for graceful tear down.
+            return true;
+        }
         for (int imsFeature : SUPPORTED_IMS_FEATURES) {
             String imsFeaturePackage = mImsFeaturePackageName.get(imsFeature);
             if (imsFeaturePackage != null) {
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index b4055a3..0aaae30 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -34,6 +34,7 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyManager.SimState;
@@ -818,8 +819,13 @@
                 })
                 .collect(Collectors.toList());
         if (dataProfiles.size() == 0) {
+            String ntnReason = "";
+            if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
+                ntnReason = " and infrastructure for "
+                        + NetworkRegistrationInfo.isNonTerrestrialNetworkToString(isNtn);
+            }
             log("Can't find any data profile for network type "
-                    + TelephonyManager.getNetworkTypeName(networkType));
+                    + TelephonyManager.getNetworkTypeName(networkType) + ntnReason);
             return null;
         }
 
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 259b482..0dda7b5 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -93,6 +93,7 @@
             case "RCS":
                 return NetworkCapabilities.NET_CAPABILITY_RCS;
             default:
+                loge("Illegal network capability: " + capabilityString);
                 return -1;
         }
     }
@@ -108,7 +109,7 @@
     public static @NetCapability Set<Integer> getNetworkCapabilitiesFromString(
             @NonNull String capabilitiesString) {
         // e.g. "IMS|" is not allowed
-        if (!capabilitiesString.matches("(\\s*[a-zA-Z]+\\s*)(\\|\\s*[a-zA-Z]+\\s*)*")) {
+        if (!capabilitiesString.matches("(\\s*[a-zA-Z_]+\\s*)(\\|\\s*[a-zA-Z_]+\\s*)*")) {
             return Collections.singleton(-1);
         }
         return Arrays.stream(capabilitiesString.split("\\s*\\|\\s*"))
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
index c66aebc..40adc7c 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -66,6 +66,8 @@
     protected static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 2;
     protected static final int EVENT_SERVICE_CONNECTED = 3;
     protected static final int EVENT_SERVICE_BINDING_TIMEOUT = 4;
+    protected static final int EVENT_RESET_NETWORK_SCAN_DONE = 5;
+    protected static final int EVENT_LAST = EVENT_RESET_NETWORK_SCAN_DONE;
 
     private static final int DEFAULT_BIND_RETRY_TIMEOUT_MS = 4 * 1000;
 
@@ -73,6 +75,7 @@
     private static final int STATUS_DOMAIN_SELECTED  = 1 << 1;
     private static final int STATUS_WAIT_BINDING     = 1 << 2;
     private static final int STATUS_WAIT_SCAN_RESULT = 1 << 3;
+    private static final int STATUS_WAIT_RESET_SCAN_RESULT = 1 << 4;
 
     /** Callback to receive responses from DomainSelectionConnection. */
     public interface DomainSelectionConnectionCallback {
@@ -85,6 +88,16 @@
         void onSelectionTerminated(@DisconnectCauses int cause);
     }
 
+    private static class ScanRequest {
+        final int[] mPreferredNetworks;
+        final int mScanType;
+
+        ScanRequest(int[] preferredNetworks, int scanType) {
+            mPreferredNetworks = preferredNetworks;
+            mScanType = scanType;
+        }
+    }
+
     /**
      * A wrapper class for {@link ITransportSelectorCallback} interface.
      */
@@ -95,7 +108,7 @@
                 mDomainSelector = selector;
                 if (checkState(STATUS_DISPOSED)) {
                     try {
-                        selector.cancelSelection();
+                        selector.finishSelection();
                     } catch (RemoteException e) {
                         // ignore exception
                     }
@@ -117,20 +130,6 @@
         }
 
         @Override
-        public @NonNull IWwanSelectorCallback onWwanSelected() {
-            synchronized (mLock) {
-                if (mWwanSelectorCallback == null) {
-                    mWwanSelectorCallback = new WwanSelectorCallbackAdaptor();
-                }
-                if (checkState(STATUS_DISPOSED)) {
-                    return mWwanSelectorCallback;
-                }
-                DomainSelectionConnection.this.onWwanSelected();
-                return mWwanSelectorCallback;
-            }
-        }
-
-        @Override
         public void onWwanSelectedAsync(@NonNull final ITransportSelectorResultCallback cb) {
             synchronized (mLock) {
                 if (checkState(STATUS_DISPOSED)) {
@@ -180,7 +179,8 @@
         @Override
         public void onRequestEmergencyNetworkScan(
                 @NonNull @RadioAccessNetworkType int[] preferredNetworks,
-                @EmergencyScanType int scanType, @NonNull IWwanSelectorResultCallback cb) {
+                @EmergencyScanType int scanType, boolean resetScan,
+                @NonNull IWwanSelectorResultCallback cb) {
             synchronized (mLock) {
                 if (checkState(STATUS_DISPOSED)) {
                     return;
@@ -190,7 +190,7 @@
                 mHandler.post(() -> {
                     synchronized (mLock) {
                         DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
-                                preferredNetworks, scanType);
+                                preferredNetworks, scanType, resetScan);
                     }
                 });
             }
@@ -275,6 +275,17 @@
                         }
                     }
                     break;
+                case EVENT_RESET_NETWORK_SCAN_DONE:
+                    synchronized (mLock) {
+                        clearState(STATUS_WAIT_RESET_SCAN_RESULT);
+                        if (checkState(STATUS_DISPOSED)
+                                || (mPendingScanRequest == null)) {
+                            return;
+                        }
+                        onRequestEmergencyNetworkScan(mPendingScanRequest.mPreferredNetworks,
+                                mPendingScanRequest.mScanType, false);
+                    }
+                    break;
                 default:
                     loge("handleMessage unexpected msg=" + msg.what);
                     break;
@@ -320,6 +331,8 @@
 
     private @NonNull AndroidFuture<Integer> mOnComplete;
 
+    private @Nullable ScanRequest mPendingScanRequest;
+
     /**
      * Creates an instance.
      *
@@ -449,10 +462,11 @@
      *
      * @param preferredNetworks The ordered list of preferred networks to scan.
      * @param scanType Indicates the scan preference, such as full service or limited service.
+     * @param resetScan Indicates that the previous scan result shall be reset before scanning.
      */
     public void onRequestEmergencyNetworkScan(
             @NonNull @RadioAccessNetworkType int[] preferredNetworks,
-            @EmergencyScanType int scanType) {
+            @EmergencyScanType int scanType, boolean resetScan) {
         // Can be overridden if required
 
         synchronized (mLock) {
@@ -464,6 +478,29 @@
                 return;
             }
 
+            if (checkState(STATUS_WAIT_RESET_SCAN_RESULT)) {
+                if (mPendingScanRequest != null) {
+                    /* Consecutive scan requests without cancellation is not an expected use case.
+                     * DomainSelector should cancel the previous request or wait for the result
+                     * before requesting a new scan.*/
+                    logi("onRequestEmergencyNetworkScan consecutive scan requests");
+                    return;
+                } else {
+                    // The reset has not been completed.
+                    // case1) Long delay in cancelEmergencyNetworkScan by modem.
+                    // case2) A consecutive scan requests with short interval from DomainSelector.
+                    logi("onRequestEmergencyNetworkScan reset not completed");
+                }
+                mPendingScanRequest = new ScanRequest(preferredNetworks, scanType);
+                return;
+            } else if (resetScan) {
+                setState(STATUS_WAIT_RESET_SCAN_RESULT);
+                mPendingScanRequest = new ScanRequest(preferredNetworks, scanType);
+                mPhone.cancelEmergencyNetworkScan(resetScan,
+                        mHandler.obtainMessage(EVENT_RESET_NETWORK_SCAN_DONE));
+                return;
+            }
+
             if (!mRegisteredRegistrant) {
                 mPhone.registerForEmergencyNetworkScan(mHandler,
                         EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null);
@@ -471,6 +508,7 @@
             }
             setState(STATUS_WAIT_SCAN_RESULT);
             mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null);
+            mPendingScanRequest = null;
         }
     }
 
@@ -506,6 +544,7 @@
     }
 
     private void onCancel(boolean resetScan) {
+        mPendingScanRequest = null;
         if (checkState(STATUS_WAIT_SCAN_RESULT)) {
             clearState(STATUS_WAIT_SCAN_RESULT);
             mPhone.cancelEmergencyNetworkScan(resetScan, null);
@@ -517,17 +556,7 @@
      * to clean up all ongoing operations with the framework.
      */
     public void cancelSelection() {
-        synchronized (mLock) {
-            try {
-                if (mDomainSelector != null) {
-                    mDomainSelector.cancelSelection();
-                }
-            } catch (RemoteException e) {
-                loge("cancelSelection exception=" + e);
-            } finally {
-                dispose();
-            }
-        }
+        finishSelection();
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
index cf75963..410f89b 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
@@ -24,6 +24,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.SystemProperties;
 import android.telephony.DomainSelectionService;
 import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
@@ -33,6 +34,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -47,6 +50,10 @@
     @VisibleForTesting
     protected static final String PACKAGE_NAME_NONE = "none";
     private static final String TAG = DomainSelectionResolver.class.getSimpleName();
+    private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
+    /** For test purpose only with userdebug release */
+    private static final String PROP_DISABLE_DOMAIN_SELECTION =
+            "telephony.test.disable_domain_selection";
     private static DomainSelectionResolver sInstance = null;
 
     /**
@@ -57,6 +64,8 @@
      *                               to be bound to the domain selection controller.
      */
     public static void make(Context context, String flattenedComponentName) {
+        Log.i(TAG, "make flag=" + Flags.apDomainSelectionEnabled()
+                + ", useOem=" + Flags.useOemDomainSelectionService());
         if (sInstance == null) {
             sInstance = new DomainSelectionResolver(context, flattenedComponentName);
         }
@@ -129,6 +138,10 @@
      *         {@code false} otherwise.
      */
     public boolean isDomainSelectionSupported() {
+        if (DBG && SystemProperties.getBoolean(PROP_DISABLE_DOMAIN_SELECTION, false)) {
+            logi("Disabled for test");
+            return false;
+        }
         return mDefaultComponentName != null && PhoneFactory.getDefaultPhone()
                 .getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1);
     }
@@ -195,7 +208,6 @@
      * @return {@code true} if the requested operation is successfully done,
      *         {@code false} otherwise.
      */
-    @VisibleForTesting
     public boolean setDomainSelectionServiceOverride(@NonNull ComponentName componentName) {
         if (mController == null) {
             logd("Controller is not initialized.");
@@ -221,7 +233,6 @@
      * @return {@code true} if the requested operation is successfully done,
      *         {@code false} otherwise.
      */
-    @VisibleForTesting
     public boolean clearDomainSelectionServiceOverride() {
         if (mController == null) {
             logd("Controller is not initialized.");
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
index b64c7cb..58917bc 100644
--- a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
@@ -28,6 +28,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.Annotation.DisconnectCauses;
@@ -201,6 +203,7 @@
      * @param exited {@code true} if the request caused the device to move out of airplane mode.
      * @param callId The call identifier.
      * @param number The dialed number.
+     * @param isTest Indicates it's a test emergency number.
      * @param callFailCause The reason why the last CS attempt failed.
      * @param imsReasonInfo The reason why the last PS attempt failed.
      * @param emergencyRegResult The current registration result for emergency services.
@@ -208,16 +211,17 @@
      */
     public static @NonNull DomainSelectionService.SelectionAttributes getSelectionAttributes(
             int slotId, int subId, boolean exited,
-            @NonNull String callId, @NonNull String number, int callFailCause,
-            @Nullable ImsReasonInfo imsReasonInfo,
+            @NonNull String callId, @NonNull String number, boolean isTest,
+            int callFailCause, @Nullable ImsReasonInfo imsReasonInfo,
             @Nullable EmergencyRegResult emergencyRegResult) {
         DomainSelectionService.SelectionAttributes.Builder builder =
                 new DomainSelectionService.SelectionAttributes.Builder(
                         slotId, subId, SELECTOR_TYPE_CALLING)
                 .setEmergency(true)
+                .setTestEmergencyNumber(isTest)
                 .setExitedFromAirplaneMode(exited)
                 .setCallId(callId)
-                .setNumber(number)
+                .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null))
                 .setCsDisconnectCause(callFailCause);
 
         if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
@@ -233,11 +237,12 @@
         if (attr == null) return null;
         DomainSelectionService.SelectionAttributes.Builder builder =
                 new DomainSelectionService.SelectionAttributes.Builder(
-                        attr.getSlotId(), attr.getSubId(), SELECTOR_TYPE_CALLING)
+                        attr.getSlotIndex(), attr.getSubscriptionId(), SELECTOR_TYPE_CALLING)
                 .setCallId(attr.getCallId())
-                .setNumber(attr.getNumber())
+                .setAddress(attr.getAddress())
                 .setVideoCall(attr.isVideoCall())
                 .setEmergency(true)
+                .setTestEmergencyNumber(attr.isTestEmergencyNumber())
                 .setExitedFromAirplaneMode(attr.isExitedFromAirplaneMode())
                 .setEmergencyRegResult(new EmergencyRegResult(AccessNetworkType.UNKNOWN,
                         NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
diff --git a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
index 0532a05..0fd9201 100644
--- a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
@@ -20,6 +20,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
 import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.DisconnectCause;
@@ -78,7 +80,7 @@
     /** {@inheritDoc} */
     @Override
     public void onRequestEmergencyNetworkScan(@RadioAccessNetworkType int[] preferredNetworks,
-            @EmergencyScanType int scanType) {
+            @EmergencyScanType int scanType, boolean resetScan) {
         // Not expected with normal calling.
         // Override to prevent abnormal behavior.
     }
@@ -119,7 +121,7 @@
                         slotId, subId, SELECTOR_TYPE_CALLING)
                         .setEmergency(false)
                         .setCallId(callId)
-                        .setNumber(number)
+                        .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null))
                         .setCsDisconnectCause(callFailCause)
                         .setVideoCall(isVideoCall);
 
diff --git a/src/java/com/android/internal/telephony/domainselection/OWNERS b/src/java/com/android/internal/telephony/domainselection/OWNERS
index b9112be..2a76770 100644
--- a/src/java/com/android/internal/telephony/domainselection/OWNERS
+++ b/src/java/com/android/internal/telephony/domainselection/OWNERS
@@ -6,3 +6,4 @@
 mkoon@google.com
 seheele@google.com
 radhikaagrawal@google.com
+jdyou@google.com
diff --git a/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
index 36a7b17..b3f4924 100644
--- a/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
@@ -53,17 +53,6 @@
         if (mCallback != null) mCallback.onSelectionTerminated(cause);
     }
 
-    @Override
-    public void finishSelection() {
-        CompletableFuture<Integer> future = getCompletableFuture();
-
-        if (future != null && !future.isDone()) {
-            cancelSelection();
-        } else {
-            super.finishSelection();
-        }
-    }
-
     /**
      * Requests a domain selection for SMS.
      *
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index f3c0a6c..17ff542 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -84,7 +84,9 @@
      * Timeout before we continue with the emergency call without waiting for DDS switch response
      * from the modem.
      */
-    private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
+    private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1 * 1000;
+    @VisibleForTesting
+    public static final int DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS = 3 * 1000;
     /** Default value for if Emergency Callback Mode is supported. */
     private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true;
     /** Default Emergency Callback Mode exit timeout value. */
@@ -645,8 +647,7 @@
         setEmergencyModeInProgress(true);
 
         Message m = mHandler.obtainMessage(msg, Integer.valueOf(emergencyType));
-        if ((mIsTestEmergencyNumber && emergencyType == EMERGENCY_TYPE_CALL)
-                || (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS)) {
+        if (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS) {
             Rlog.d(TAG, "TestEmergencyNumber for " + emergencyTypeToString(emergencyType)
                     + ": Skipping setting emergency mode on modem.");
             // Send back a response for the command, but with null information
@@ -1227,6 +1228,11 @@
                 mRadioOnHelper = new RadioOnHelper(mContext);
             }
 
+            final Phone phoneForEmergency = phone;
+            final String expectedCallId = mOngoingCallId;
+            final int waitForInServiceTimeout =
+                    needToTurnOnRadio ? DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS : 0;
+            Rlog.i(TAG, "turnOnRadioAndSwitchDds: timeout=" + waitForInServiceTimeout);
             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
@@ -1241,25 +1247,33 @@
                             completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF);
                         }
                     } else {
+                        if (!Objects.equals(mOngoingCallId, expectedCallId)) {
+                            Rlog.i(TAG, "onComplete " + expectedCallId + " canceled.");
+                            return;
+                        }
                         switchDdsAndSetEmergencyMode(phone, emergencyType);
                     }
                 }
 
                 @Override
                 public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) {
-                    // We currently only look to make sure that the radio is on before dialing. We
-                    // should be able to make emergency calls at any time after the radio has been
-                    // powered on and isn't in the UNAVAILABLE state, even if it is reporting the
-                    // OUT_OF_SERVICE state.
+                    // Wait for normal service state or timeout if required.
+                    if (phone == phoneForEmergency
+                            && waitForInServiceTimeout > 0
+                            && !isNetworkRegistered(phone)) {
+                        return false;
+                    }
                     return phone.getServiceStateTracker().isRadioOn()
                             && !satelliteController.isSatelliteEnabled();
                 }
 
                 @Override
                 public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) {
-                    return true;
+                    // onTimeout shall be called only with the Phone for emergency
+                    return phone.getServiceStateTracker().isRadioOn()
+                            && !satelliteController.isSatelliteEnabled();
                 }
-            }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, 0);
+            }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout);
         } else {
             switchDdsAndSetEmergencyMode(phone, emergencyType);
         }
@@ -1412,6 +1426,27 @@
                 || phone.getServiceState().isEmergencyOnly();
     }
 
+    private static boolean isNetworkRegistered(Phone phone) {
+        ServiceState ss = phone.getServiceStateTracker().getServiceState();
+        if (ss != null) {
+            NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+                    NetworkRegistrationInfo.DOMAIN_PS,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            if (nri != null && nri.isNetworkRegistered()) {
+                // PS is IN_SERVICE state.
+                return true;
+            }
+            nri = ss.getNetworkRegistrationInfo(
+                    NetworkRegistrationInfo.DOMAIN_CS,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            if (nri != null && nri.isNetworkRegistered()) {
+                // CS is IN_SERVICE state.
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Checks whether both {@code Phone}s are same or not.
      */
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
index 9c4ebfa..384112d 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -96,7 +96,7 @@
                 continue;
             }
 
-            int timeoutCallbackInterval = (forEmergencyCall && phone == phoneForEmergencyCall)
+            int timeoutCallbackInterval = (phone == phoneForEmergencyCall)
                     ? emergencyTimeoutIntervalMillis : 0;
             mInProgressListeners.add(mListeners.get(i));
             mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
index 4ba38f0..5949f66 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
@@ -505,7 +505,7 @@
         if (mPhone != null) {
             subId = mPhone.getSubId();
         }
-        mSatelliteController.unregisterForSatelliteModemStateChanged(subId, mSatelliteCallback);
+        mSatelliteController.unregisterForModemStateChanged(subId, mSatelliteCallback);
         mHandler.removeMessages(MSG_SATELLITE_ENABLED_CHANGED);
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
index 3dedde8..234723f 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
@@ -263,9 +263,10 @@
             PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
                     KEY_NR_SA_DISABLE_POLICY_INT, KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
             mNrSaDisablePolicy = bundle.getInt(KEY_NR_SA_DISABLE_POLICY_INT);
-            mIsNrSaSupported = Arrays.stream(
-                    bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY)).anyMatch(
-                        value -> value == CARRIER_NR_AVAILABILITY_SA);
+            int[] nrAvailabilities = bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+            mIsNrSaSupported = nrAvailabilities != null
+                    && Arrays.stream(nrAvailabilities).anyMatch(
+                            value -> value == CARRIER_NR_AVAILABILITY_SA);
 
             Log.d(TAG, "setNrSaDisablePolicy : NrSaDisablePolicy = "
                     + mNrSaDisablePolicy + ", IsNrSaSupported = "  + mIsNrSaSupported);
@@ -286,7 +287,7 @@
 
     private void setNrSaMode(boolean onOrOff) {
         if (mIsNrSaSupported) {
-            mPhone.getDefaultPhone().mCi.setN1ModeEnabled(onOrOff, null);
+            mPhone.getDefaultPhone().setN1ModeEnabled(onOrOff, null);
             Log.i(TAG, "setNrSaMode : " + onOrOff);
 
             setNrSaDisabledForWfc(!onOrOff);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 9f3ec3b..2f1c2d6 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -2832,6 +2832,15 @@
         mCT.triggerNotifyAnbr(mediaType, direction, bitsPerSecond);
     }
 
+    /**
+     * Check whether making a call using Wi-Fi is possible or not.
+     * @return {code true} if IMS is registered over IWLAN else return {code false}.
+     */
+    public boolean canMakeWifiCall() {
+        return isImsRegistered() && (getImsRegistrationTech()
+                == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 3a0bffe..b5a052d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1259,8 +1259,6 @@
         }
     }
 
-    private @NonNull final FeatureFlags mFeatureFlags;
-
     //***** Events
 
 
@@ -1273,8 +1271,9 @@
     @VisibleForTesting
     public ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory, Executor executor,
             FeatureFlags featureFlags) {
+        super(featureFlags);
+
         this.mPhone = phone;
-        mFeatureFlags = featureFlags;
         mConnectorFactory = factory;
         if (executor != null) {
             mExecutor = executor;
diff --git a/src/java/com/android/internal/telephony/metrics/ImsStats.java b/src/java/com/android/internal/telephony/metrics/ImsStats.java
index 12c3488..d9994aa 100644
--- a/src/java/com/android/internal/telephony/metrics/ImsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ImsStats.java
@@ -338,6 +338,13 @@
 
     /** Updates the stats when IMS registration succeeds. */
     public synchronized void onImsRegistered(ImsRegistrationAttributes attributes) {
+        // Updates registered_times as soon as the UE is registered
+        if (mLastRegistrationState != REGISTRATION_STATE_REGISTERED) {
+            // RegistrationStats captures in every state. Changing REGISTERED state has to capture
+            // only once.
+            mLastRegistrationStats.registeredTimes = 1;
+        }
+
         conclude();
 
         mLastTransportType = attributes.getTransportType();
@@ -346,12 +353,6 @@
             updateImsRegistrationStats();
         }
 
-        if (mLastRegistrationState != REGISTRATION_STATE_REGISTERED) {
-            // RegistrationStats captures in every state. Changing REGISTERED state has to capture
-            // only once.
-            mLastRegistrationStats.registeredTimes = 1;
-        }
-
         mLastRegistrationStats.rat =
                 convertTransportTypeToNetworkType(attributes.getTransportType());
         mLastRegistrationStats.isIwlanCrossSim = attributes.getRegistrationTechnology()
diff --git a/src/java/com/android/internal/telephony/metrics/RcsStats.java b/src/java/com/android/internal/telephony/metrics/RcsStats.java
index 8d24def..20b23f9 100644
--- a/src/java/com/android/internal/telephony/metrics/RcsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/RcsStats.java
@@ -1029,8 +1029,11 @@
     }
 
     /** invalidated result when Request message is sent */
-    public synchronized void invalidatedMessageResult(int subId, String sipMessageMethod,
-            int sipMessageDirection, int messageError) {
+    public synchronized void invalidatedMessageResult(String callId, int subId,
+            String sipMessageMethod, int sipMessageDirection, int messageError) {
+        if (mSipMessage == null) {
+            mSipMessage = new SipMessageArray(sipMessageMethod, sipMessageDirection, callId);
+        }
         mSipMessage.addSipMessageStat(subId, sipMessageMethod, 0,
                 sipMessageDirection, messageError);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index e4d16e7..ae4c1f2 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -209,7 +209,7 @@
             case EVENT_SEND_SATELLITE_DATAGRAM_DONE: {
                 ar = (AsyncResult) msg.obj;
                 request = (DatagramDispatcherHandlerRequest) ar.userObj;
-                int error = SatelliteServiceUtils.getSatelliteError(ar, "sendSatelliteDatagram");
+                int error = SatelliteServiceUtils.getSatelliteError(ar, "sendDatagram");
                 SendSatelliteDatagramArgument argument =
                         (SendSatelliteDatagramArgument) request.argument;
 
@@ -330,7 +330,7 @@
             }
 
             if (mDatagramController.needsWaitingForSatelliteConnected()) {
-                logd("sendSatelliteDatagram: wait for satellite connected");
+                logd("sendDatagram: wait for satellite connected");
                 mDatagramController.updateSendStatus(subId,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
                         getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
@@ -345,7 +345,7 @@
                         getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
             } else {
-                logd("sendSatelliteDatagram: mSendingDatagramInProgress="
+                logd("sendDatagram: mSendingDatagramInProgress="
                         + mSendingDatagramInProgress + ", isPollingInIdleState="
                         + mDatagramController.isPollingInIdleState());
             }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index 3ac1bbd..c267fd7 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -588,13 +588,14 @@
             @NonNull Consumer<Integer> callback) {
         if (!mDatagramController.isSendingInIdleState()) {
             // Poll request should be sent to satellite modem only when it is free.
-            logd("pollPendingSatelliteDatagrams: satellite modem is busy sending datagrams.");
+            logd("pollPendingSatelliteDatagramsInternal: satellite modem is busy sending "
+                    + "datagrams.");
             callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
             return;
         }
 
         if (mDatagramController.needsWaitingForSatelliteConnected()) {
-            logd("pollPendingSatelliteDatagrams: wait for satellite connected");
+            logd("pollPendingSatelliteDatagramsInternal: wait for satellite connected");
             synchronized (mLock) {
                 mPendingPollSatelliteDatagramsRequest = new DatagramReceiverHandlerRequest(
                         callback, SatelliteServiceUtils.getPhone(), subId);
diff --git a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
index 4d294f4..add01c0 100644
--- a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
+++ b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
@@ -40,7 +40,7 @@
     public static void resolveNtnCapability(
             @NonNull NetworkRegistrationInfo networkRegistrationInfo, int subId) {
         SatelliteController satelliteController = SatelliteController.getInstance();
-        List<String> satellitePlmnList = satelliteController.getAllSatellitePlmnsForCarrier(subId);
+        List<String> satellitePlmnList = satelliteController.getSatellitePlmnsForCarrier(subId);
         String registeredPlmn = networkRegistrationInfo.getRegisteredPlmn();
         for (String satellitePlmn : satellitePlmnList) {
             if (TextUtils.equals(satellitePlmn, registeredPlmn)) {
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
index 9a6bd69..878ee96 100644
--- a/src/java/com/android/internal/telephony/satellite/PointingAppController.java
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -376,6 +376,7 @@
             loge("startPointingUI: launchIntent is null");
             return;
         }
+        logd("startPointingUI: needFullScreenPointingUI: " + needFullScreenPointingUI);
         launchIntent.putExtra("needFullScreen", needFullScreenPointingUI);
 
         try {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index cb731a2..d5794fc 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.provider.Settings.ACTION_SATELLITE_SETTING;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
@@ -27,19 +28,26 @@
 import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.ArrayRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.nfc.NfcAdapter;
 import android.os.AsyncResult;
@@ -60,6 +68,7 @@
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
@@ -125,6 +134,10 @@
     public static final int SATELLITE_MODE_ENABLED_FALSE = 0;
     public static final int INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE = -1;
 
+    /** Key used to read/write OEM-enabled satellite provision status in shared preferences. */
+    private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY =
+            "oem_enabled_satellite_provision_status_key";
+
     /** Message codes used in handleMessage() */
     //TODO: Move the Commands and events related to position updates to PointingAppController
     private static final int CMD_START_SATELLITE_TRANSMISSION_UPDATES = 1;
@@ -248,9 +261,11 @@
     @GuardedBy("mIsSatelliteEnabledLock")
     private Boolean mIsSatelliteEnabled = null;
     private boolean mIsRadioOn = false;
-    private final Object mIsSatelliteProvisionedLock = new Object();
-    @GuardedBy("mIsSatelliteProvisionedLock")
-    private Boolean mIsSatelliteProvisioned = null;
+    private final Object mSatelliteViaOemProvisionLock = new Object();
+    @GuardedBy("mSatelliteViaOemProvisionLock")
+    private Boolean mIsSatelliteViaOemProvisioned = null;
+    @GuardedBy("mSatelliteViaOemProvisionLock")
+    private Boolean mOverriddenIsSatelliteViaOemProvisioned = null;
     private final Object mSatelliteCapabilitiesLock = new Object();
     @GuardedBy("mSatelliteCapabilitiesLock")
     private SatelliteCapabilities mSatelliteCapabilities;
@@ -310,7 +325,33 @@
     private int mEnforcedEmergencyCallToSatelliteHandoverType =
             INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
     private int mDelayInSendingEventDisplayEmergencyMessage = 0;
-    private boolean mCarrierSatelliteEnabled;
+    @NonNull private SharedPreferences mSharedPreferences = null;
+
+    /**
+     * Key : Subscription ID, Value: {@code true} if the EntitlementStatus is enabled,
+     * {@code false} otherwise.
+     */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    private SparseBooleanArray mSatelliteEntitlementStatusPerCarrier = new SparseBooleanArray();
+    /** Key Subscription ID, value : PLMN allowed list from entitlement. */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    private SparseArray<List<String>> mEntitlementPlmnListPerCarrier = new SparseArray<>();
+    /**
+     * Key : Subscription ID, Value : If there is an entitlementPlmnList, use it. Otherwise, use the
+     * carrierPlmnList. */
+    @GuardedBy("mSupportedSatelliteServicesLock")
+    private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
+
+    /** Key used to read/write satellite system notification done in shared preferences. */
+    private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
+            "satellite_system_notification_done_key";
+    // The notification tag used when showing a notification. The combination of notification tag
+    // and notification id should be unique within the phone app.
+    private static final String NOTIFICATION_TAG = "SatelliteController";
+    private static final int NOTIFICATION_ID = 1;
+    private static final String NOTIFICATION_CHANNEL = "satelliteChannel";
+    private static final String NOTIFICATION_CHANNEL_ID = "satellite";
+
     /**
      * @return The singleton instance of SatelliteController.
      */
@@ -402,6 +443,7 @@
                         new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
         mDSM.registerForSignalStrengthReportDecision(this, CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING,
                 null);
+        loadSatelliteSharedPreferences();
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -773,13 +815,6 @@
                             mWaitingForRadioDisabled = true;
                         }
                         setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_TRUE);
-
-                        /**
-                         * TODO for NTN-based satellites: Check if satellite is acquired.
-                         */
-                        if (mNeedsSatellitePointing) {
-                            mPointingAppController.startPointingUI(false);
-                        }
                         evaluateToSendSatelliteEnabledSuccess();
                     } else {
                         /**
@@ -897,7 +932,7 @@
                         error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
                     } else {
                         boolean supported = (boolean) ar.result;
-                        if (DBG) logd("isSatelliteSupported: " + supported);
+                        logd("isSatelliteSupported: " + supported);
                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, supported);
                         updateSatelliteSupportedStateWhenSatelliteServiceConnected(supported);
                     }
@@ -1009,6 +1044,24 @@
                 if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
                     mIsRadioOn = true;
                 }
+                if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+                    if (mSatelliteModemInterface.isSatelliteServiceConnected()) {
+                        synchronized (mIsSatelliteSupportedLock) {
+                            if (mIsSatelliteSupported == null || !mIsSatelliteSupported) {
+                                ResultReceiver receiver = new ResultReceiver(this) {
+                                    @Override
+                                    protected void onReceiveResult(
+                                            int resultCode, Bundle resultData) {
+                                        logd("onRadioStateChanged.requestIsSatelliteSupported: "
+                                                + "resultCode=" + resultCode
+                                                + ", resultData=" + resultData);
+                                    }
+                                };
+                                sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, receiver, null);
+                            }
+                        }
+                    }
+                }
                 break;
             }
 
@@ -1020,25 +1073,7 @@
             }
 
             case EVENT_IS_SATELLITE_PROVISIONED_DONE: {
-                ar = (AsyncResult) msg.obj;
-                request = (SatelliteControllerHandlerRequest) ar.userObj;
-                int error =  SatelliteServiceUtils.getSatelliteError(ar,
-                        "isSatelliteProvisioned");
-                Bundle bundle = new Bundle();
-                if (error == SATELLITE_RESULT_SUCCESS) {
-                    if (ar.result == null) {
-                        loge("isSatelliteProvisioned: result is null");
-                        error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
-                    } else {
-                        boolean provisioned = ((int[]) ar.result)[0] == 1;
-                        if (DBG) logd("isSatelliteProvisioned: " + provisioned);
-                        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, provisioned);
-                        synchronized (mIsSatelliteProvisionedLock) {
-                            mIsSatelliteProvisioned = provisioned;
-                        }
-                    }
-                }
-                ((ResultReceiver) request.argument).send(error, bundle);
+                handleIsSatelliteProvisionedDoneEvent((AsyncResult) msg.obj);
                 break;
             }
 
@@ -1059,7 +1094,7 @@
                         logd("pollPendingSatelliteDatagram result: " + result);
                     }
                 };
-                pollPendingSatelliteDatagrams(
+                pollPendingDatagrams(
                         SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, internalCallback);
                 break;
 
@@ -1638,11 +1673,11 @@
             return;
         }
 
-        synchronized (mIsSatelliteProvisionedLock) {
-            if (mIsSatelliteProvisioned != null) {
+        synchronized (mSatelliteViaOemProvisionLock) {
+            if (mIsSatelliteViaOemProvisioned != null) {
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
-                        mIsSatelliteProvisioned);
+                        mIsSatelliteViaOemProvisioned);
                 result.send(SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
@@ -1683,16 +1718,16 @@
      * @param callback The callback that was passed to
      * {@link #registerForSatelliteModemStateChanged(int, ISatelliteModemStateCallback)}.
      */
-    public void unregisterForSatelliteModemStateChanged(int subId,
+    public void unregisterForModemStateChanged(int subId,
             @NonNull ISatelliteModemStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("unregisterForSatelliteModemStateChanged: oemEnabledSatelliteFlag is disabled");
+            logd("unregisterForModemStateChanged: oemEnabledSatelliteFlag is disabled");
             return;
         }
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.unregisterForSatelliteModemStateChanged(callback);
         } else {
-            loge("unregisterForSatelliteModemStateChanged: mSatelliteSessionController"
+            loge("unregisterForModemStateChanged: mSatelliteSessionController"
                     + " is not initialized yet");
         }
     }
@@ -1705,16 +1740,16 @@
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId,
+    @SatelliteManager.SatelliteResult public int registerForIncomingDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForSatelliteDatagram: oemEnabledSatelliteFlag is disabled");
+            logd("registerForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
-        logd("registerForSatelliteDatagram: callback=" + callback);
+        logd("registerForIncomingDatagram: callback=" + callback);
         return mDatagramController.registerForSatelliteDatagram(subId, callback);
     }
 
@@ -1724,18 +1759,18 @@
      *
      * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
      * @param callback The callback that was passed to
-     *                 {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
+     *                 {@link #registerForIncomingDatagram(int, ISatelliteDatagramCallback)}.
      */
-    public void unregisterForSatelliteDatagram(int subId,
+    public void unregisterForIncomingDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("unregisterForSatelliteDatagram: oemEnabledSatelliteFlag is disabled");
+            logd("unregisterForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
             return;
         }
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return;
         }
-        logd("unregisterForSatelliteDatagram: callback=" + callback);
+        logd("unregisterForIncomingDatagram: callback=" + callback);
         mDatagramController.unregisterForSatelliteDatagram(subId, callback);
     }
 
@@ -1750,7 +1785,7 @@
      * @param subId The subId of the subscription used for receiving datagrams.
      * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
-    public void pollPendingSatelliteDatagrams(int subId, @NonNull IIntegerConsumer callback) {
+    public void pollPendingDatagrams(int subId, @NonNull IIntegerConsumer callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) {
@@ -1778,9 +1813,12 @@
      *                                 full screen mode.
      * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
-    public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+    public void sendDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
             SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull IIntegerConsumer callback) {
+        logd("sendSatelliteDatagram: subId: " + subId + " datagramType: " + datagramType
+                + " needFullScreenPointingUI: " + needFullScreenPointingUI);
+
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) {
@@ -1861,14 +1899,14 @@
      * @param reason Reason for disallowing satellite communication for carrier.
      * @param callback The callback to get the result of the request.
      */
-    public void addSatelliteAttachRestrictionForCarrier(int subId,
+    public void addAttachRestrictionForCarrier(int subId,
             @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
             @NonNull IIntegerConsumer callback) {
-        if (DBG) logd("addSatelliteAttachRestrictionForCarrier(" + subId + ", " + reason + ")");
+        if (DBG) logd("addAttachRestrictionForCarrier(" + subId + ", " + reason + ")");
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             result.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
-            logd("addSatelliteAttachRestrictionForCarrier: carrierEnabledSatelliteFlag is "
+            logd("addAttachRestrictionForCarrier: carrierEnabledSatelliteFlag is "
                     + "disabled");
             return;
         }
@@ -1900,14 +1938,14 @@
      * @param reason Reason for disallowing satellite communication.
      * @param callback The callback to get the result of the request.
      */
-    public void removeSatelliteAttachRestrictionForCarrier(int subId,
+    public void removeAttachRestrictionForCarrier(int subId,
             @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
             @NonNull IIntegerConsumer callback) {
-        if (DBG) logd("removeSatelliteAttachRestrictionForCarrier(" + subId + ", " + reason + ")");
+        if (DBG) logd("removeAttachRestrictionForCarrier(" + subId + ", " + reason + ")");
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             result.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
-            logd("removeSatelliteAttachRestrictionForCarrier: carrierEnabledSatelliteFlag is "
+            logd("removeAttachRestrictionForCarrier: carrierEnabledSatelliteFlag is "
                     + "disabled");
             return;
         }
@@ -1930,15 +1968,15 @@
 
     /**
      * Get reasons for disallowing satellite communication, as requested by
-     * {@link #addSatelliteAttachRestrictionForCarrier(int, int, IIntegerConsumer)}.
+     * {@link #addAttachRestrictionForCarrier(int, int, IIntegerConsumer)}.
      *
      * @param subId The subId of the subscription to request for.
      *
      * @return Set of reasons for disallowing satellite attach for carrier.
      */
-    @NonNull public Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) {
+    @NonNull public Set<Integer> getAttachRestrictionReasonsForCarrier(int subId) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
-            logd("getSatelliteAttachRestrictionReasonsForCarrier: carrierEnabledSatelliteFlag is "
+            logd("getAttachRestrictionReasonsForCarrier: carrierEnabledSatelliteFlag is "
                     + "disabled");
             return new HashSet<>();
         }
@@ -2005,7 +2043,7 @@
         if (error == SATELLITE_RESULT_SUCCESS) {
             mNtnSignalStrengthChangedListeners.put(callback.asBinder(), callback);
         } else {
-            throw new ServiceSpecificException(error);
+            throw new RemoteException(new IllegalStateException("registration fails: " + error));
         }
     }
 
@@ -2036,9 +2074,9 @@
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteResult public int registerForSatelliteCapabilitiesChanged(
+    @SatelliteManager.SatelliteResult public int registerForCapabilitiesChanged(
             int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
-        if (DBG) logd("registerForSatelliteCapabilitiesChanged()");
+        if (DBG) logd("registerForCapabilitiesChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) return error;
@@ -2054,11 +2092,11 @@
      * @param subId The id of the subscription to unregister for listening satellite capabilities
      * changed event.
      * @param callback The callback that was passed to
-     * {@link #registerForSatelliteCapabilitiesChanged(int, ISatelliteCapabilitiesCallback)}
+     * {@link #registerForCapabilitiesChanged(int, ISatelliteCapabilitiesCallback)}
      */
-    public void unregisterForSatelliteCapabilitiesChanged(
+    public void unregisterForCapabilitiesChanged(
             int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
-        if (DBG) logd("unregisterForSatelliteCapabilitiesChanged()");
+        if (DBG) logd("unregisterForCapabilitiesChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error == SATELLITE_RESULT_SUCCESS) {
@@ -2088,8 +2126,8 @@
         synchronized (mIsSatelliteSupportedLock) {
             mIsSatelliteSupported = null;
         }
-        synchronized (mIsSatelliteProvisionedLock) {
-            mIsSatelliteProvisioned = null;
+        synchronized (mSatelliteViaOemProvisionLock) {
+            mIsSatelliteViaOemProvisioned = null;
         }
         synchronized (mIsSatelliteEnabledLock) {
             mIsSatelliteEnabled = null;
@@ -2202,6 +2240,29 @@
         return true;
     }
 
+    /**
+     * This API can be used in only testing to override oem-enabled satellite provision status.
+     *
+     * @param reset {@code true} mean the overriding status should not be used, {@code false}
+     *              otherwise.
+     * @param isProvisioned The overriding provision status.
+     * @return {@code true} if the provision status is set successfully, {@code false} otherwise.
+     */
+    public boolean setOemEnabledSatelliteProvisionStatus(boolean reset, boolean isProvisioned) {
+        if (!isMockModemAllowed()) {
+            loge("setOemEnabledSatelliteProvisionStatus: mock modem not allowed");
+            return false;
+        }
+        synchronized (mSatelliteViaOemProvisionLock) {
+            if (reset) {
+                mOverriddenIsSatelliteViaOemProvisioned = null;
+            } else {
+                mOverriddenIsSatelliteViaOemProvisioned = isProvisioned;
+            }
+        }
+        return true;
+    }
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected int getEnforcedEmergencyCallToSatelliteHandoverType() {
         return mEnforcedEmergencyCallToSatelliteHandoverType;
@@ -2298,17 +2359,13 @@
      * @return The list of satellite PLMNs used for connecting to satellite networks.
      */
     @NonNull
-    public List<String> getAllSatellitePlmnsForCarrier(int subId) {
+    public List<String> getSatellitePlmnsForCarrier(int subId) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
-            logd("getAllSatellitePlmnsForCarrier: carrierEnabledSatelliteFlag is disabled");
+            logd("getSatellitePlmnsForCarrier: carrierEnabledSatelliteFlag is disabled");
             return new ArrayList<>();
         }
         synchronized (mSupportedSatelliteServicesLock) {
-            if (mSatelliteServicesSupportedByCarriers.containsKey(subId)) {
-                return new ArrayList<>(mSatelliteServicesSupportedByCarriers.get(subId).keySet());
-            } else {
-                return new ArrayList<>();
-            }
+            return mMergedPlmnListPerCarrier.get(subId, new ArrayList<>()).stream().toList();
         }
     }
 
@@ -2450,22 +2507,21 @@
     }
 
     /**
-     * Update the satellite EntitlementStatus and PlmnAllowedList after receiving the HTTP response
-     * from the satellite entitlement server.
-     * If the satellite service is enabled then trigger internal satellite enabled for carrier,
-     * otherwise trigger internal satellite disabled for carrier.
+     * To use the satellite service, update the EntitlementStatus and the PlmnAllowedList after
+     * receiving the satellite configuration from the entitlement server. If satellite
+     * entitlement is enabled, enable satellite for the carrier. Otherwise, disable satellite.
+     *
+     * @param subId              subId
+     * @param entitlementEnabled {@code true} Satellite service enabled
+     * @param allowedPlmnList    plmn allowed list to use the satellite service
+     * @param callback           callback for accept
      */
-    public void updateSatelliteEntitlementStatus(int subId, boolean satelliteEnabled,
-            List<String> plmnAllowed, IIntegerConsumer callback) {
+    public void onSatelliteEntitlementStatusUpdated(int subId, boolean entitlementEnabled,
+            List<String> allowedPlmnList, @Nullable IIntegerConsumer callback) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             return;
         }
 
-        if (mCarrierSatelliteEnabled != satelliteEnabled) {
-            logd("update the carrier satellite enabled to " + satelliteEnabled);
-            mCarrierSatelliteEnabled = satelliteEnabled;
-        }
-
         if (callback == null) {
             callback = new IIntegerConsumer.Stub() {
                 @Override
@@ -2475,12 +2531,25 @@
             };
         }
 
-        if (mCarrierSatelliteEnabled) {
-            removeSatelliteAttachRestrictionForCarrier(subId,
-                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT, callback);
-        } else {
-            addSatelliteAttachRestrictionForCarrier(subId,
-                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT, callback);
+        synchronized (mSupportedSatelliteServicesLock) {
+            if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) {
+                logd("update the carrier satellite enabled to " + entitlementEnabled);
+                mSatelliteEntitlementStatusPerCarrier.put(subId, entitlementEnabled);
+            }
+            mMergedPlmnListPerCarrier.remove(subId);
+
+            mEntitlementPlmnListPerCarrier.put(subId, allowedPlmnList);
+            updatePlmnListPerCarrier(subId);
+            configureSatellitePlmnForCarrier(subId);
+
+            // TODO b/322143408 store entitlement status in telephony db.
+            if (mSatelliteEntitlementStatusPerCarrier.get(subId, false)) {
+                removeAttachRestrictionForCarrier(subId,
+                        SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT, callback);
+            } else {
+                addAttachRestrictionForCarrier(subId,
+                        SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT, callback);
+            }
         }
     }
 
@@ -2528,7 +2597,17 @@
                     SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
         }
-        callback.accept(result);
+        if (result == SATELLITE_RESULT_SUCCESS
+                || result == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
+            persistOemEnabledSatelliteProvisionStatus(true);
+            synchronized (mSatelliteViaOemProvisionLock) {
+                mIsSatelliteViaOemProvisioned = true;
+            }
+            callback.accept(SATELLITE_RESULT_SUCCESS);
+            handleEventSatelliteProvisionStateChanged(true);
+        } else {
+            callback.accept(result);
+        }
     }
 
     private void handleEventDeprovisionSatelliteServiceDone(
@@ -2541,13 +2620,23 @@
         logd("handleEventDeprovisionSatelliteServiceDone: result="
                 + result + ", subId=" + arg.subId);
 
-        if (arg.callback != null) {
+        if (result == SATELLITE_RESULT_SUCCESS
+                || result == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
+            persistOemEnabledSatelliteProvisionStatus(false);
+            synchronized (mSatelliteViaOemProvisionLock) {
+                mIsSatelliteViaOemProvisioned = false;
+            }
+            if (arg.callback != null) {
+                arg.callback.accept(SATELLITE_RESULT_SUCCESS);
+            }
+            handleEventSatelliteProvisionStateChanged(false);
+        } else if (arg.callback != null) {
             arg.callback.accept(result);
-            mProvisionMetricsStats.setResultCode(result)
-                    .setIsProvisionRequest(false)
-                    .reportProvisionMetrics();
-            mControllerMetricsStats.reportDeprovisionCount(result);
         }
+        mProvisionMetricsStats.setResultCode(result)
+                .setIsProvisionRequest(false)
+                .reportProvisionMetrics();
+        mControllerMetricsStats.reportDeprovisionCount(result);
     }
 
     private void handleStartSatelliteTransmissionUpdatesDone(@NonNull AsyncResult ar) {
@@ -2621,9 +2710,13 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected Boolean isSatelliteViaOemProvisioned() {
-        synchronized (mIsSatelliteProvisionedLock) {
-            if (mIsSatelliteProvisioned != null) {
-                return mIsSatelliteProvisioned;
+        synchronized (mSatelliteViaOemProvisionLock) {
+            if (mOverriddenIsSatelliteViaOemProvisioned != null) {
+                return mOverriddenIsSatelliteViaOemProvisioned;
+            }
+
+            if (mIsSatelliteViaOemProvisioned != null) {
+                return mIsSatelliteViaOemProvisioned;
             }
         }
 
@@ -2631,7 +2724,7 @@
                 new ResultReceiver(this) {
                     @Override
                     protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        logd("requestIsSatelliteProvisioned: resultCode=" + resultCode);
+                        logd("isSatelliteViaOemProvisioned: resultCode=" + resultCode);
                     }
                 });
         return null;
@@ -2678,13 +2771,14 @@
             registerForPendingDatagramCount();
             registerForSatelliteModemStateChanged();
             registerForNtnSignalStrengthChanged();
-            registerForSatelliteCapabilitiesChanged();
+            registerForCapabilitiesChanged();
 
             requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
-                            logd("requestIsSatelliteProvisioned: resultCode=" + resultCode);
+                            logd("requestIsSatelliteProvisioned: resultCode=" + resultCode
+                                    + ", resultData=" + resultData);
                             requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                                     false, false,
                                     new IIntegerConsumer.Stub() {
@@ -2699,7 +2793,8 @@
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
-                            logd("requestSatelliteCapabilities: resultCode=" + resultCode);
+                            logd("requestSatelliteCapabilities: resultCode=" + resultCode
+                                    + ", resultData=" + resultData);
                         }
                     });
         }
@@ -2762,9 +2857,9 @@
         }
     }
 
-    private void registerForSatelliteCapabilitiesChanged() {
+    private void registerForCapabilitiesChanged() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("registerForSatelliteCapabilitiesChanged: oemEnabledSatelliteFlag is disabled");
+            logd("registerForCapabilitiesChanged: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
@@ -2780,8 +2875,9 @@
     private void handleEventSatelliteProvisionStateChanged(boolean provisioned) {
         logd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
 
-        synchronized (mIsSatelliteProvisionedLock) {
-            mIsSatelliteProvisioned = provisioned;
+        synchronized (mSatelliteViaOemProvisionLock) {
+            persistOemEnabledSatelliteProvisionStatus(provisioned);
+            mIsSatelliteViaOemProvisioned = provisioned;
         }
 
         List<ISatelliteProvisionStateCallback> deadCallersList = new ArrayList<>();
@@ -2964,13 +3060,8 @@
             return;
         }
         synchronized (mSupportedSatelliteServicesLock) {
-            List<String> carrierPlmnList;
-            if (mSatelliteServicesSupportedByCarriers.containsKey(subId)) {
-                carrierPlmnList =
-                        mSatelliteServicesSupportedByCarriers.get(subId).keySet().stream().toList();
-            } else {
-                carrierPlmnList = new ArrayList<>();
-            }
+            List<String> carrierPlmnList = mMergedPlmnListPerCarrier.get(subId,
+                    new ArrayList<>()).stream().toList();
             int slotId = SubscriptionManager.getSlotIndex(subId);
             mSatelliteModemInterface.setSatellitePlmn(slotId, carrierPlmnList,
                     SatelliteServiceUtils.mergeStrLists(
@@ -2993,6 +3084,7 @@
 
         synchronized (mSupportedSatelliteServicesLock) {
             mSatelliteServicesSupportedByCarriers.clear();
+            mMergedPlmnListPerCarrier.clear();
             int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true);
             if (activeSubIds != null) {
                 for (int subId : activeSubIds) {
@@ -3005,10 +3097,37 @@
         }
     }
 
+    /**
+     * If the entitlementPlmnList exist then used it.
+     * Otherwise, If the carrierPlmnList exist then used it.
+     */
+    private void updatePlmnListPerCarrier(int subId) {
+        synchronized (mSupportedSatelliteServicesLock) {
+            List<String> carrierPlmnList, entitlementPlmnList;
+            entitlementPlmnList = mEntitlementPlmnListPerCarrier.get(subId,
+                    new ArrayList<>()).stream().toList();
+            if (!entitlementPlmnList.isEmpty()) {
+                mMergedPlmnListPerCarrier.put(subId, entitlementPlmnList);
+                logd("update it using entitlementPlmnList=" + entitlementPlmnList);
+                return;
+            }
+
+            if (mSatelliteServicesSupportedByCarriers.containsKey(subId)) {
+                carrierPlmnList =
+                        mSatelliteServicesSupportedByCarriers.get(subId).keySet().stream().toList();
+            } else {
+                carrierPlmnList = new ArrayList<>();
+            }
+            mMergedPlmnListPerCarrier.put(subId, carrierPlmnList);
+            logd("update it using carrierPlmnList=" + carrierPlmnList);
+        }
+    }
+
     private void updateSupportedSatelliteServices(int subId) {
         synchronized (mSupportedSatelliteServicesLock) {
             mSatelliteServicesSupportedByCarriers.put(
                     subId, readSupportedSatelliteServicesFromCarrierConfig(subId));
+            updatePlmnListPerCarrier(subId);
         }
     }
 
@@ -3059,6 +3178,8 @@
         }
 
         updateCarrierConfig(subId);
+        // TODO b/322143408 read the telephony db to get the entitlementStatus and update the
+        //  restriction
         updateSupportedSatelliteServicesForActiveSubscriptions();
         configureSatellitePlmnForCarrier(subId);
 
@@ -3243,7 +3364,7 @@
      * <ul>
      * <li>Users want to enable it.</li>
      * <li>There is no satellite communication restriction, which is added by
-     * {@link #addSatelliteAttachRestrictionForCarrier(int, int, IIntegerConsumer)}</li>
+     * {@link #addAttachRestrictionForCarrier(int, int, IIntegerConsumer)}</li>
      * <li>The carrier config {@link
      * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to
      * {@code true}.</li>
@@ -3358,6 +3479,7 @@
 
     private void handleEventServiceStateChanged() {
         handleServiceStateForSatelliteConnectionViaCarrier();
+        determineSystemNotification();
     }
 
     private void handleServiceStateForSatelliteConnectionViaCarrier() {
@@ -3397,6 +3519,78 @@
         }
     }
 
+    private void persistOemEnabledSatelliteProvisionStatus(boolean isProvisioned) {
+        synchronized (mSatelliteViaOemProvisionLock) {
+            logd("persistOemEnabledSatelliteProvisionStatus: isProvisioned=" + isProvisioned);
+
+            if (!loadSatelliteSharedPreferences()) return;
+
+            if (mSharedPreferences == null) {
+                loge("persistOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
+            } else {
+                mSharedPreferences.edit().putBoolean(
+                        OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, isProvisioned).apply();
+            }
+        }
+    }
+
+    private boolean getPersistedOemEnabledSatelliteProvisionStatus() {
+        synchronized (mSatelliteViaOemProvisionLock) {
+            if (!loadSatelliteSharedPreferences()) return false;
+
+            if (mSharedPreferences == null) {
+                loge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
+                return false;
+            } else {
+                return mSharedPreferences.getBoolean(
+                        OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, false);
+            }
+        }
+    }
+
+    private boolean loadSatelliteSharedPreferences() {
+        if (mSharedPreferences == null) {
+            try {
+                mSharedPreferences =
+                        mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
+                                Context.MODE_PRIVATE);
+            } catch (Exception e) {
+                loge("loadSatelliteSharedPreferences: Cannot get default "
+                        + "shared preferences, e=" + e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void handleIsSatelliteProvisionedDoneEvent(@NonNull AsyncResult ar) {
+        SatelliteControllerHandlerRequest request = (SatelliteControllerHandlerRequest) ar.userObj;
+        int error = SatelliteServiceUtils.getSatelliteError(
+                ar, "handleIsSatelliteProvisionedDoneEvent");
+        boolean isSatelliteProvisionedInModem = false;
+        if (error == SATELLITE_RESULT_SUCCESS) {
+            if (ar.result == null) {
+                loge("handleIsSatelliteProvisionedDoneEvent: result is null");
+                error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
+            } else {
+                isSatelliteProvisionedInModem = ((int[]) ar.result)[0] == 1;
+            }
+        } else if (error == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
+            logd("handleIsSatelliteProvisionedDoneEvent: Modem does not support this request");
+            isSatelliteProvisionedInModem = true;
+        }
+        boolean isSatelliteViaOemProvisioned =
+                isSatelliteProvisionedInModem && getPersistedOemEnabledSatelliteProvisionStatus();
+        logd("isSatelliteProvisionedInModem=" + isSatelliteProvisionedInModem
+                + ", isSatelliteViaOemProvisioned=" + isSatelliteViaOemProvisioned);
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, isSatelliteViaOemProvisioned);
+        synchronized (mSatelliteViaOemProvisionLock) {
+            mIsSatelliteViaOemProvisioned = isSatelliteViaOemProvisioned;
+        }
+        ((ResultReceiver) request.argument).send(error, bundle);
+    }
+
     /**
      * This API can be used by only CTS to override the cached value for the device overlay config
      * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
@@ -3422,6 +3616,78 @@
         return true;
     }
 
+    private void determineSystemNotification() {
+        if (isUsingNonTerrestrialNetworkViaCarrier()) {
+            if (mSharedPreferences == null) {
+                try {
+                    mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
+                            Context.MODE_PRIVATE);
+                } catch (Exception e) {
+                    loge("Cannot get default shared preferences: " + e);
+                }
+            }
+            if (mSharedPreferences == null) {
+                loge("handleEventServiceStateChanged: Cannot get default shared preferences");
+                return;
+            }
+            if (!mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)) {
+                showSatelliteSystemNotification();
+                mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY,
+                        true).apply();
+            }
+        }
+    }
+
+    private void showSatelliteSystemNotification() {
+        logd("showSatelliteSystemNotification");
+        final NotificationChannel notificationChannel = new NotificationChannel(
+                NOTIFICATION_CHANNEL_ID,
+                NOTIFICATION_CHANNEL,
+                NotificationManager.IMPORTANCE_DEFAULT
+        );
+        notificationChannel.setSound(null, null);
+        NotificationManager notificationManager = mContext.getSystemService(
+                NotificationManager.class);
+        notificationManager.createNotificationChannel(notificationChannel);
+
+        Notification.Builder notificationBuilder = new Notification.Builder(mContext)
+                .setContentTitle(mContext.getResources().getString(
+                        R.string.satellite_notification_title))
+                .setContentText(mContext.getResources().getString(
+                        R.string.satellite_notification_summary))
+                .setSmallIcon(R.drawable.ic_satellite_alt_24px)
+                .setChannelId(NOTIFICATION_CHANNEL_ID)
+                .setAutoCancel(true)
+                .setColor(mContext.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setVisibility(Notification.VISIBILITY_PUBLIC);
+
+        // Add action to invoke `What to expect` dialog of Messaging application.
+        Intent intentOpenMessage = new Intent(Intent.ACTION_VIEW);
+        intentOpenMessage.setData(Uri.parse("sms:"));
+        // TODO : b/322733285 add putExtra to invoke "What to expect" dialog.
+        PendingIntent pendingIntentOpenMessage = PendingIntent.getActivity(mContext, 0,
+                intentOpenMessage, PendingIntent.FLAG_IMMUTABLE);
+
+        Notification.Action actionOpenMessage = new Notification.Action.Builder(0,
+                mContext.getResources().getString(R.string.satellite_notification_open_message),
+                pendingIntentOpenMessage).build();
+        notificationBuilder.addAction(actionOpenMessage);
+
+        // Add action to invoke Satellite setting activity in Settings.
+        Intent intentSatelliteSetting = new Intent(ACTION_SATELLITE_SETTING);
+        PendingIntent pendingIntentSatelliteSetting = PendingIntent.getActivity(mContext, 0,
+                intentSatelliteSetting, PendingIntent.FLAG_IMMUTABLE);
+
+        Notification.Action actionOpenSatelliteSetting = new Notification.Action.Builder(null,
+                mContext.getResources().getString(R.string.satellite_notification_how_it_works),
+                pendingIntentSatelliteSetting).build();
+        notificationBuilder.addAction(actionOpenSatelliteSetting);
+
+        notificationManager.notifyAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
+                notificationBuilder.build(), UserHandle.ALL);
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 03481c6..900e124 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -915,18 +915,18 @@
                     @Override
                     public void accept(int result) {
                         int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        logd("pollPendingSatelliteDatagrams: " + error);
+                        logd("pollPendingDatagrams: " + error);
                         Binder.withCleanCallingIdentity(() ->
                                 sendMessageWithResult(message, null, error));
                     }
                 });
             } catch (RemoteException e) {
-                loge("pollPendingSatelliteDatagrams: RemoteException " + e);
+                loge("pollPendingDatagrams: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("pollPendingSatelliteDatagrams: Satellite service is unavailable.");
+            loge("pollPendingDatagrams: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -951,18 +951,18 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("sendSatelliteDatagram: " + error);
+                                logd("sendDatagram: " + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
                             }
                         });
             } catch (RemoteException e) {
-                loge("sendSatelliteDatagram: RemoteException " + e);
+                loge("sendDatagram: RemoteException " + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("sendSatelliteDatagram: Satellite service is unavailable.");
+            loge("sendDatagram: Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         }
@@ -1021,7 +1021,7 @@
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+                                logd("requestIsCommunicationAllowedForCurrentLocation: "
                                         + error);
                                 Binder.withCleanCallingIdentity(() ->
                                         sendMessageWithResult(message, null, error));
@@ -1029,7 +1029,7 @@
                         }, new IBooleanConsumer.Stub() {
                             @Override
                             public void accept(boolean result) {
-                                logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+                                logd("requestIsCommunicationAllowedForCurrentLocation: "
                                         + result);
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                         message, result,
@@ -1037,13 +1037,13 @@
                             }
                         });
             } catch (RemoteException e) {
-                loge("requestIsSatelliteCommunicationAllowedForCurrentLocation: RemoteException "
+                loge("requestIsCommunicationAllowedForCurrentLocation: RemoteException "
                         + e);
                 sendMessageWithResult(message, null,
                         SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
             }
         } else {
-            loge("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+            loge("requestIsCommunicationAllowedForCurrentLocation: "
                     + "Satellite service is unavailable.");
             sendMessageWithResult(message, null,
                     SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
@@ -1236,10 +1236,13 @@
                             }
                         }, new INtnSignalStrengthConsumer.Stub() {
                             @Override
-                            public void accept(NtnSignalStrength result) {
-                                logd("requestNtnSignalStrength: " + result);
+                            public void accept(
+                                    android.telephony.satellite.stub.NtnSignalStrength result) {
+                                NtnSignalStrength ntnSignalStrength =
+                                        SatelliteServiceUtils.fromNtnSignalStrength(result);
+                                logd("requestNtnSignalStrength: " + ntnSignalStrength);
                                 Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                        message, result,
+                                        message, ntnSignalStrength,
                                         SatelliteManager.SATELLITE_RESULT_SUCCESS));
                             }
                         });
@@ -1318,6 +1321,13 @@
         return mIsSatelliteServiceSupported;
     }
 
+    /** Check if vendor satellite service is connected */
+    public boolean isSatelliteServiceConnected() {
+        synchronized (mLock) {
+            return (mSatelliteService != null);
+        }
+    }
+
     /**
      * This API can be used by only CTS to update satellite vendor service package name.
      *
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index aabf826..149b054 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -696,7 +696,7 @@
     protected void requestIsSatelliteCommunicationAllowedForCurrentLocation(
             @NonNull OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> callback) {
         SatelliteManager satelliteManager = mContext.getSystemService(SatelliteManager.class);
-        satelliteManager.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+        satelliteManager.requestIsCommunicationAllowedForCurrentLocation(
                 this::post, callback);
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 541a029..6916e65 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -711,6 +711,9 @@
                     && datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE) {
                 startNbIotInactivityTimer();
+            } else if (isSending(datagramTransferState.sendState)
+                    || isReceiving(datagramTransferState.receiveState)) {
+                restartNbIotInactivityTimer();
             }
         }
     }
@@ -761,9 +764,8 @@
 
         private void handleEventDatagramTransferStateChanged(
                 @NonNull DatagramTransferState datagramTransferState) {
-            if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING
-                    || datagramTransferState.receiveState
-                    == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING) {
+            if (isSending(datagramTransferState.sendState)
+                    || isReceiving(datagramTransferState.receiveState)) {
                 transitionTo(mTransferringState);
             }
         }
@@ -974,6 +976,11 @@
                 R.integer.config_satellite_nb_iot_inactivity_timeout_millis);
     }
 
+    private void restartNbIotInactivityTimer() {
+        stopNbIotInactivityTimer();
+        startNbIotInactivityTimer();
+    }
+
     private void startNbIotInactivityTimer() {
         if (isNbIotInactivityTimerStarted()) {
             logd("NB IOT inactivity timer is already started");
diff --git a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
index b56b276..4540b8a 100644
--- a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
+++ b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
@@ -16,18 +16,21 @@
 
 package com.android.internal.telephony.security;
 
+import android.content.Context;
 import android.telephony.CellularIdentifierDisclosure;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.telephony.Rlog;
 
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Executors;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Encapsulates logic to emit notifications to the user that their cellular identifiers were
@@ -47,6 +50,7 @@
     private static CellularIdentifierDisclosureNotifier sInstance = null;
     private final long mWindowCloseDuration;
     private final TimeUnit mWindowCloseUnit;
+    private final CellularNetworkSecuritySafetySource mSafetySource;
     private final Object mEnabledLock = new Object();
 
     @GuardedBy("mEnabledLock")
@@ -55,14 +59,16 @@
     // events are strictly serialized.
     private ScheduledExecutorService mSerializedWorkQueue;
 
-    private AtomicInteger mDisclosureCount;
+    // This object should only be accessed from within the thread of mSerializedWorkQueue. Access
+    // outside of that thread would require additional synchronization.
+    private Map<Integer, DisclosureWindow> mWindows;
 
-    // One should only interact with this future from within the work queue's thread.
-    private ScheduledFuture<?> mWhenWindowCloses;
-
-    public CellularIdentifierDisclosureNotifier() {
-        this(Executors.newSingleThreadScheduledExecutor(), DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
-                TimeUnit.MINUTES);
+    public CellularIdentifierDisclosureNotifier(CellularNetworkSecuritySafetySource safetySource) {
+        this(
+                Executors.newSingleThreadScheduledExecutor(),
+                DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
+                TimeUnit.MINUTES,
+                safetySource);
     }
 
     /**
@@ -76,18 +82,20 @@
     public CellularIdentifierDisclosureNotifier(
             ScheduledExecutorService notificationQueue,
             long windowCloseDuration,
-            TimeUnit windowCloseUnit) {
+            TimeUnit windowCloseUnit,
+            CellularNetworkSecuritySafetySource safetySource) {
         mSerializedWorkQueue = notificationQueue;
         mWindowCloseDuration = windowCloseDuration;
         mWindowCloseUnit = windowCloseUnit;
-        mDisclosureCount = new AtomicInteger(0);
+        mWindows = new HashMap<>();
+        mSafetySource = safetySource;
     }
 
     /**
-     * Add a CellularIdentifierDisclosure to be tracked by this instance.
-     * If appropriate, this will trigger a user notification.
+     * Add a CellularIdentifierDisclosure to be tracked by this instance. If appropriate, this will
+     * trigger a user notification.
      */
-    public void addDisclosure(CellularIdentifierDisclosure disclosure) {
+    public void addDisclosure(Context context, int subId, CellularIdentifierDisclosure disclosure) {
         Rlog.d(TAG, "Identifier disclosure reported: " + disclosure);
 
         synchronized (mEnabledLock) {
@@ -108,7 +116,7 @@
             // because we know that any actions taken on disabled will be scheduled after this
             // incrementAndNotify call.
             try {
-                mSerializedWorkQueue.execute(incrementAndNotify());
+                mSerializedWorkQueue.execute(incrementAndNotify(context, subId));
             } catch (RejectedExecutionException e) {
                 Rlog.e(TAG, "Failed to schedule incrementAndNotify: " + e.getMessage());
             }
@@ -119,12 +127,12 @@
      * Re-enable if previously disabled. This means that {@code addDisclsoure} will start tracking
      * disclosures again and potentially emitting notifications.
      */
-    public void enable() {
+    public void enable(Context context) {
         synchronized (mEnabledLock) {
             Rlog.d(TAG, "enabled");
             mEnabled = true;
             try {
-                mSerializedWorkQueue.execute(onEnableNotifier());
+                mSerializedWorkQueue.execute(onEnableNotifier(context));
             } catch (RejectedExecutionException e) {
                 Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage());
             }
@@ -136,12 +144,12 @@
      * This can be used to in response to a user disabling the feature to emit notifications.
      * If {@code addDisclosure} is called while in a disabled state, disclosures will be dropped.
      */
-    public void disable() {
+    public void disable(Context context) {
         Rlog.d(TAG, "disabled");
         synchronized (mEnabledLock) {
             mEnabled = false;
             try {
-                mSerializedWorkQueue.execute(onDisableNotifier());
+                mSerializedWorkQueue.execute(onDisableNotifier(context));
             } catch (RejectedExecutionException e) {
                 Rlog.e(TAG, "Failed to schedule onDisableNotifier: " + e.getMessage());
             }
@@ -154,78 +162,202 @@
         }
     }
 
-    @VisibleForTesting
-    public int getCurrentDisclosureCount() {
-        return mDisclosureCount.get();
-    }
-
     /** Get a singleton CellularIdentifierDisclosureNotifier. */
-    public static synchronized CellularIdentifierDisclosureNotifier getInstance() {
+    public static synchronized CellularIdentifierDisclosureNotifier getInstance(
+            CellularNetworkSecuritySafetySource safetySource) {
         if (sInstance == null) {
-            sInstance = new CellularIdentifierDisclosureNotifier();
+            sInstance = new CellularIdentifierDisclosureNotifier(safetySource);
         }
 
         return sInstance;
     }
 
-    private Runnable closeWindow() {
+    private Runnable incrementAndNotify(Context context, int subId) {
         return () -> {
-            Rlog.i(TAG,
-                    "Disclosure window closing. Disclosure count was " + mDisclosureCount.get());
-            mDisclosureCount.set(0);
-        };
-    }
-
-    private Runnable incrementAndNotify() {
-        return () -> {
-            int newCount = mDisclosureCount.incrementAndGet();
-            Rlog.d(TAG, "Emitting notification. New disclosure count " + newCount);
-
-            // To reset the timer for our window, we first cancel an existing timer.
-            boolean cancelled = cancelWindowCloseFuture();
-            Rlog.d(TAG, "Result of attempting to cancel window closing future: " + cancelled);
-
-            try {
-                mWhenWindowCloses =
-                        mSerializedWorkQueue.schedule(
-                                closeWindow(), mWindowCloseDuration, mWindowCloseUnit);
-            } catch (RejectedExecutionException e) {
-                Rlog.e(TAG, "Failed to schedule closeWindow: " + e.getMessage());
+            DisclosureWindow window = mWindows.get(subId);
+            if (window == null) {
+                window = new DisclosureWindow(subId);
+                mWindows.put(subId, window);
             }
+
+            window.increment(context, this);
+
+            int disclosureCount = window.getDisclosureCount();
+
+            Rlog.d(
+                    TAG,
+                    "Emitting notification for subId: "
+                            + subId
+                            + ". New disclosure count "
+                            + disclosureCount);
+
+            mSafetySource.setIdentifierDisclosure(
+                    context,
+                    subId,
+                    disclosureCount,
+                    window.getFirstOpen(),
+                    window.getCurrentEnd());
         };
     }
 
-    private Runnable onDisableNotifier() {
+    private Runnable onDisableNotifier(Context context) {
         return () -> {
-            mDisclosureCount.set(0);
-            cancelWindowCloseFuture();
             Rlog.d(TAG, "On disable notifier");
+            for (DisclosureWindow window : mWindows.values()) {
+                window.close();
+            }
+            mSafetySource.setIdentifierDisclosureIssueEnabled(context, false);
         };
     }
 
-    private Runnable onEnableNotifier() {
+    private Runnable onEnableNotifier(Context context) {
         return () -> {
             Rlog.i(TAG, "On enable notifier");
+            mSafetySource.setIdentifierDisclosureIssueEnabled(context, true);
         };
     }
 
     /**
-     * A helper to cancel the Future that is in charge of closing the disclosure window. This must
-     * only be called from within the single-threaded executor. Calling this method leaves a
-     * completed or cancelled future in mWhenWindowCloses.
-     *
-     * @return boolean indicating whether or not the Future was actually cancelled. If false, this
-     * likely indicates that the disclosure window has already closed.
+     * Get the disclosure count for a given subId. NOTE: This method is not thread safe. Without
+     * external synchronization, one should only call it if there are no pending tasks on the
+     * Executor passed into this class.
      */
-    private boolean cancelWindowCloseFuture() {
-        if (mWhenWindowCloses == null) {
-            return false;
+    @VisibleForTesting
+    public int getCurrentDisclosureCount(int subId) {
+        DisclosureWindow window = mWindows.get(subId);
+        if (window != null) {
+            return window.getDisclosureCount();
         }
 
-        // While we choose not to interrupt a running Future (we pass `false` to the `cancel`
-        // call), we shouldn't ever actually need this functionality because all the work on the
-        // queue is serialized on a single thread. Nothing about the `closeWindow` call is ready
-        // to handle interrupts, though, so this seems like a safer choice.
-        return mWhenWindowCloses.cancel(false);
+        return 0;
+    }
+
+    /**
+     * Get the open time for a given subId. NOTE: This method is not thread safe. Without
+     * external synchronization, one should only call it if there are no pending tasks on the
+     * Executor passed into this class.
+     */
+    @VisibleForTesting
+    public Instant getFirstOpen(int subId) {
+        DisclosureWindow window = mWindows.get(subId);
+        if (window != null) {
+            return window.getFirstOpen();
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the current end time for a given subId. NOTE: This method is not thread safe. Without
+     * external synchronization, one should only call it if there are no pending tasks on the
+     * Executor passed into this class.
+     */
+    @VisibleForTesting
+    public Instant getCurrentEnd(int subId) {
+        DisclosureWindow window = mWindows.get(subId);
+        if (window != null) {
+            return window.getCurrentEnd();
+        }
+
+        return null;
+    }
+
+    /**
+     * A helper class that maintains all state associated with the disclosure window for a single
+     * subId. No methods are thread safe. Callers must implement all synchronization.
+     */
+    private class DisclosureWindow {
+        private int mDisclosureCount;
+        private Instant mWindowFirstOpen;
+        private Instant mLastEvent;
+        private ScheduledFuture<?> mWhenWindowCloses;
+
+        private int mSubId;
+
+        DisclosureWindow(int subId) {
+            mDisclosureCount = 0;
+            mWindowFirstOpen = null;
+            mLastEvent = null;
+            mSubId = subId;
+            mWhenWindowCloses = null;
+        }
+
+        void increment(Context context, CellularIdentifierDisclosureNotifier notifier) {
+
+            mDisclosureCount++;
+
+            Instant now = Instant.now();
+            if (mDisclosureCount == 1) {
+                // Our window was opened for the first time
+                mWindowFirstOpen = now;
+            }
+
+            mLastEvent = now;
+
+            cancelWindowCloseFuture();
+
+            try {
+                mWhenWindowCloses =
+                        notifier.mSerializedWorkQueue.schedule(
+                                closeWindowRunnable(context),
+                                notifier.mWindowCloseDuration,
+                                notifier.mWindowCloseUnit);
+            } catch (RejectedExecutionException e) {
+                Rlog.e(
+                        TAG,
+                        "Failed to schedule closeWindow for subId "
+                                + mSubId
+                                + " :  "
+                                + e.getMessage());
+            }
+        }
+
+        int getDisclosureCount() {
+            return mDisclosureCount;
+        }
+
+        Instant getFirstOpen() {
+            return mWindowFirstOpen;
+        }
+
+        Instant getCurrentEnd() {
+            return mLastEvent;
+        }
+
+        void close() {
+            mDisclosureCount = 0;
+            mWindowFirstOpen = null;
+            mLastEvent = null;
+
+            if (mWhenWindowCloses == null) {
+                return;
+            }
+            mWhenWindowCloses = null;
+        }
+
+        private Runnable closeWindowRunnable(Context context) {
+            return () -> {
+                Rlog.i(
+                        TAG,
+                        "Disclosure window closing for subId "
+                                + mSubId
+                                + ". Disclosure count was "
+                                + getDisclosureCount());
+                close();
+                mSafetySource.clearIdentifierDisclosure(context, mSubId);
+            };
+        }
+
+        private boolean cancelWindowCloseFuture() {
+            if (mWhenWindowCloses == null) {
+                return false;
+            }
+
+            // Pass false to not interrupt a running Future. Nothing about our notifier is ready
+            // for this type of preemption.
+            return mWhenWindowCloses.cancel(false);
+        }
+
     }
 }
+
diff --git a/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java b/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
new file mode 100644
index 0000000..ff09bcb
--- /dev/null
+++ b/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.security;
+
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED;
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED;
+import static android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION;
+import static android.safetycenter.SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION;
+
+import android.annotation.IntDef;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.safetycenter.SafetyCenterManager;
+import android.safetycenter.SafetyEvent;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
+import android.safetycenter.SafetySourceStatus;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * Holds the state needed to report the Safety Center status and issues related to cellular
+ * network security.
+ */
+public class CellularNetworkSecuritySafetySource {
+    private static final String SAFETY_SOURCE_ID = "AndroidCellularNetworkSecurity";
+
+    private static final String NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID = "null_cipher_non_encrypted";
+    private static final String NULL_CIPHER_ISSUE_ENCRYPTED_ID = "null_cipher_encrypted";
+
+    private static final String NULL_CIPHER_ACTION_SETTINGS_ID = "cellular_security_settings";
+    private static final String NULL_CIPHER_ACTION_LEARN_MORE_ID = "learn_more";
+
+    private static final String IDENTIFIER_DISCLOSURE_ISSUE_ID = "identifier_disclosure";
+
+    private static final Intent CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT =
+            new Intent("android.settings.CELLULAR_NETWORK_SECURITY");
+    // TODO(b/321999913): direct to a help page URL e.g.
+    //                    new Intent(Intent.ACTION_VIEW, Uri.parse("https://..."));
+    private static final Intent LEARN_MORE_INTENT = new Intent();
+
+    static final int NULL_CIPHER_STATE_ENCRYPTED = 0;
+    static final int NULL_CIPHER_STATE_NOTIFY_ENCRYPTED = 1;
+    static final int NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED = 2;
+
+    @IntDef(
+        prefix = {"NULL_CIPHER_STATE_"},
+        value = {
+            NULL_CIPHER_STATE_ENCRYPTED,
+            NULL_CIPHER_STATE_NOTIFY_ENCRYPTED,
+            NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface NullCipherState {}
+
+    private static CellularNetworkSecuritySafetySource sInstance;
+
+    private final SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+    private final SubscriptionManagerService mSubscriptionManagerService;
+
+    private boolean mNullCipherStateIssuesEnabled;
+    private HashMap<Integer, Integer> mNullCipherStates = new HashMap<>();
+
+    private boolean mIdentifierDisclosureIssuesEnabled;
+    private HashMap<Integer, IdentifierDisclosure> mIdentifierDisclosures = new HashMap<>();
+
+    /**
+     * Gets a singleton CellularNetworkSecuritySafetySource.
+     */
+    public static synchronized CellularNetworkSecuritySafetySource getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new CellularNetworkSecuritySafetySource(
+                    new SafetyCenterManagerWrapper(context));
+        }
+        return sInstance;
+    }
+
+    @VisibleForTesting
+    public CellularNetworkSecuritySafetySource(
+            SafetyCenterManagerWrapper safetyCenterManagerWrapper) {
+        mSafetyCenterManagerWrapper = safetyCenterManagerWrapper;
+        mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+    }
+
+    /** Enables or disables the null cipher issue and clears any current issues. */
+    public synchronized void setNullCipherIssueEnabled(Context context, boolean enabled) {
+        mNullCipherStateIssuesEnabled = enabled;
+        mNullCipherStates.clear();
+        updateSafetyCenter(context);
+    }
+
+    /** Sets the null cipher issue state for the identified subscription. */
+    public synchronized void setNullCipherState(
+            Context context, int subId, @NullCipherState int nullCipherState) {
+        mNullCipherStates.put(subId, nullCipherState);
+        updateSafetyCenter(context);
+    }
+
+    /** Enables or disables the identifier disclosure issue and clears any current issues. */
+    public synchronized void setIdentifierDisclosureIssueEnabled(Context context, boolean enabled) {
+        mIdentifierDisclosureIssuesEnabled = enabled;
+        mIdentifierDisclosures.clear();
+        updateSafetyCenter(context);
+    }
+
+    /** Sets the identifier disclosure issue state for the identifier subscription. */
+    public synchronized void setIdentifierDisclosure(
+            Context context, int subId, int count, Instant start, Instant end) {
+        IdentifierDisclosure disclosure = new IdentifierDisclosure(count, start, end);
+        mIdentifierDisclosures.put(subId, disclosure);
+        updateSafetyCenter(context);
+    }
+
+    /** Clears the identifier disclosure issue state for the identified subscription. */
+    public synchronized void clearIdentifierDisclosure(Context context, int subId) {
+        mIdentifierDisclosures.remove(subId);
+        updateSafetyCenter(context);
+    }
+
+    /** Refreshed the safety source in response to the identified broadcast. */
+    public synchronized void refresh(Context context, String refreshBroadcastId) {
+        mSafetyCenterManagerWrapper.setRefreshedSafetySourceData(
+                refreshBroadcastId, getSafetySourceData(context));
+    }
+
+    private void updateSafetyCenter(Context context) {
+        mSafetyCenterManagerWrapper.setSafetySourceData(getSafetySourceData(context));
+    }
+
+    private boolean isSafetySourceHidden() {
+        return !mNullCipherStateIssuesEnabled && !mIdentifierDisclosureIssuesEnabled;
+    }
+
+    private SafetySourceData getSafetySourceData(Context context) {
+        if (isSafetySourceHidden()) {
+            // The cellular network security safety source is configured with
+            // initialDisplayState="hidden"
+            return null;
+        }
+
+        Stream<Optional<SafetySourceIssue>> nullCipherIssues =
+                mNullCipherStates.entrySet().stream()
+                        .map(e -> getNullCipherIssue(context, e.getKey(), e.getValue()));
+        Stream<Optional<SafetySourceIssue>> identifierDisclosureIssues =
+                mIdentifierDisclosures.entrySet().stream()
+                        .map(e -> getIdentifierDisclosureIssue(context, e.getKey(), e.getValue()));
+        SafetySourceIssue[] issues = Stream.concat(nullCipherIssues, identifierDisclosureIssues)
+                .flatMap(Optional::stream)
+                .toArray(SafetySourceIssue[]::new);
+
+        SafetySourceData.Builder builder = new SafetySourceData.Builder();
+        int maxSeverity = SEVERITY_LEVEL_INFORMATION;
+        for (SafetySourceIssue issue : issues) {
+            builder.addIssue(issue);
+            maxSeverity = Math.max(maxSeverity, issue.getSeverityLevel());
+        }
+
+        builder.setStatus(
+                new SafetySourceStatus.Builder(
+                        context.getString(R.string.scCellularNetworkSecurityTitle),
+                        context.getString(R.string.scCellularNetworkSecuritySummary),
+                        maxSeverity)
+                    .setPendingIntent(mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                            context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+                    .build());
+        return builder.build();
+    }
+
+    /** Builds the null cipher issue if it's enabled and there are null ciphers to report. */
+    private Optional<SafetySourceIssue> getNullCipherIssue(
+            Context context, int subId, @NullCipherState int state) {
+        if (!mNullCipherStateIssuesEnabled) {
+            return Optional.empty();
+        }
+
+        SubscriptionInfoInternal subInfo =
+                mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+        final SafetySourceIssue.Builder builder;
+        switch (state) {
+            case NULL_CIPHER_STATE_ENCRYPTED:
+                return Optional.empty();
+            case NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED:
+                builder = new SafetySourceIssue.Builder(
+                        NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID + "_" + subId,
+                        context.getString(
+                            R.string.scNullCipherIssueNonEncryptedTitle, subInfo.getDisplayName()),
+                        context.getString(R.string.scNullCipherIssueNonEncryptedSummary),
+                        SEVERITY_LEVEL_RECOMMENDATION,
+                        NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID);
+                break;
+            case NULL_CIPHER_STATE_NOTIFY_ENCRYPTED:
+                builder = new SafetySourceIssue.Builder(
+                        NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID + "_" + subId,
+                        context.getString(
+                            R.string.scNullCipherIssueEncryptedTitle, subInfo.getDisplayName()),
+                        context.getString(R.string.scNullCipherIssueEncryptedSummary),
+                        SEVERITY_LEVEL_INFORMATION,
+                        NULL_CIPHER_ISSUE_ENCRYPTED_ID);
+                break;
+            default:
+                throw new AssertionError();
+        }
+
+        return Optional.of(
+                builder
+                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+                    .addAction(
+                        new SafetySourceIssue.Action.Builder(
+                                NULL_CIPHER_ACTION_SETTINGS_ID,
+                                context.getString(R.string.scNullCipherIssueActionSettings),
+                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                        context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+                            .build())
+                    .addAction(
+                        new SafetySourceIssue.Action.Builder(
+                                NULL_CIPHER_ACTION_LEARN_MORE_ID,
+                                context.getString(R.string.scNullCipherIssueActionLearnMore),
+                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                        context, LEARN_MORE_INTENT))
+                            .build())
+                    .build());
+    }
+
+    /** Builds the identity disclosure issue if it's enabled and there are disclosures to report. */
+    private Optional<SafetySourceIssue> getIdentifierDisclosureIssue(
+            Context context, int subId, IdentifierDisclosure disclosure) {
+        if (!mIdentifierDisclosureIssuesEnabled || disclosure.getDisclosureCount() == 0) {
+            return Optional.empty();
+        }
+
+        SubscriptionInfoInternal subInfo =
+                mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+        return Optional.of(
+                new SafetySourceIssue.Builder(
+                        IDENTIFIER_DISCLOSURE_ISSUE_ID + "_" + subId,
+                        context.getString(R.string.scIdentifierDisclosureIssueTitle),
+                        context.getString(
+                                R.string.scIdentifierDisclosureIssueSummary,
+                                disclosure.getDisclosureCount(),
+                                Date.from(disclosure.getWindowStart()),
+                                Date.from(disclosure.getWindowEnd()),
+                                subInfo.getDisplayName()),
+                        SEVERITY_LEVEL_RECOMMENDATION,
+                        IDENTIFIER_DISCLOSURE_ISSUE_ID)
+                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+                    .addAction(
+                        new SafetySourceIssue.Action.Builder(
+                                NULL_CIPHER_ACTION_SETTINGS_ID,
+                                context.getString(R.string.scNullCipherIssueActionSettings),
+                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                        context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+                            .build())
+                    .addAction(
+                        new SafetySourceIssue.Action.Builder(
+                                NULL_CIPHER_ACTION_LEARN_MORE_ID,
+                                context.getString(R.string.scNullCipherIssueActionLearnMore),
+                                mSafetyCenterManagerWrapper.getActivityPendingIntent(
+                                        context, LEARN_MORE_INTENT))
+                            .build())
+                .build());
+    }
+
+    /** A wrapper around {@link SafetyCenterManager} that can be instrumented in tests. */
+    @VisibleForTesting
+    public static class SafetyCenterManagerWrapper {
+        private final SafetyCenterManager mSafetyCenterManager;
+
+        public SafetyCenterManagerWrapper(Context context) {
+            mSafetyCenterManager = context.getSystemService(SafetyCenterManager.class);
+        }
+
+        /** Retrieve a {@link PendingIntent} that will start a new activity. */
+        public PendingIntent getActivityPendingIntent(Context context, Intent intent) {
+            return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+        }
+
+        /** Set the {@link SafetySourceData} for this safety source. */
+        public void setSafetySourceData(SafetySourceData safetySourceData) {
+            mSafetyCenterManager.setSafetySourceData(
+                    SAFETY_SOURCE_ID,
+                    safetySourceData,
+                    new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build());
+        }
+
+        /** Sets the {@link SafetySourceData} in response to a refresh request. */
+        public void setRefreshedSafetySourceData(
+                String refreshBroadcastId, SafetySourceData safetySourceData) {
+            mSafetyCenterManager.setSafetySourceData(
+                    SAFETY_SOURCE_ID,
+                    safetySourceData,
+                    new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+                            .setRefreshBroadcastId(refreshBroadcastId)
+                            .build());
+        }
+    }
+
+    private static class IdentifierDisclosure {
+        private final int mDisclosureCount;
+        private final Instant mWindowStart;
+        private final Instant mWindowEnd;
+
+        private IdentifierDisclosure(int count, Instant start, Instant end) {
+            mDisclosureCount = count;
+            mWindowStart = start;
+            mWindowEnd  = end;
+        }
+
+        private int getDisclosureCount() {
+            return mDisclosureCount;
+        }
+
+        private Instant getWindowStart() {
+            return mWindowStart;
+        }
+
+        private Instant getWindowEnd() {
+            return mWindowEnd;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof IdentifierDisclosure)) {
+                return false;
+            }
+            IdentifierDisclosure other = (IdentifierDisclosure) o;
+            return mDisclosureCount == other.mDisclosureCount
+                    && Objects.equals(mWindowStart, other.mWindowStart)
+                    && Objects.equals(mWindowEnd, other.mWindowEnd);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mDisclosureCount, mWindowStart, mWindowEnd);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index c6fc23d..aa460d5 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -444,7 +444,7 @@
 
     /**
      * Whether satellite attach for carrier is enabled or disabled by user.
-     * By default, its disabled. It is intended to use integer to fit the database format.
+     * By default, its enabled. It is intended to use integer to fit the database format.
      */
     private final int mIsSatelliteAttachEnabledForCarrier;
 
@@ -1750,7 +1750,7 @@
         /**
          * Whether satellite attach for carrier is enabled by user.
          */
-        private int mIsSatelliteAttachEnabledForCarrier = 0;
+        private int mIsSatelliteAttachEnabledForCarrier = 1;
 
         /**
          * Whether this subscription is used for communicating with non-terrestrial network or not.
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 91c46cb..ddf80a8 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1969,10 +1969,8 @@
 
         enforceTelephonyFeatureWithException(callingPackage, "getActiveSubscriptionInfoList");
 
-        if (isForAllProfiles && !hasAcrossAllUsersPermission()) {
-            //TODO(b/308809058 to determine whether the permission enforcement is needed)
-            loge("getActiveSubscriptionInfoList: "
-                    + callingPackage + " has no appropriate permission.");
+        if (isForAllProfiles) {
+            enforcePermissionAccessAllUserProfiles();
         }
         return getSubscriptionInfoStreamAsUser(isForAllProfiles
                 ? UserHandle.ALL : BINDER_WRAPPER.getCallingUserHandle())
@@ -2013,10 +2011,8 @@
             throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
                     + "carrier privilege");
         }
-        if (isForAllProfiles && !hasAcrossAllUsersPermission()) {
-            //TODO(b/308809058 to determine whether the permission enforcement is needed)
-            loge("getActiveSubInfoCount: "
-                    + callingPackage + " has no appropriate permission.");
+        if (isForAllProfiles) {
+            enforcePermissionAccessAllUserProfiles();
         }
 
         enforceTelephonyFeatureWithException(callingPackage, "getActiveSubInfoCount");
@@ -2025,9 +2021,11 @@
                 ? UserHandle.ALL : BINDER_WRAPPER.getCallingUserHandle()).length;
     }
 
-    /**@return {@code true} if the caller is permitted to see all subscriptions. */
-    private boolean hasAcrossAllUsersPermission() {
-        return hasPermissions(Manifest.permission.INTERACT_ACROSS_USERS,
+    /** @throws SecurityException if caller doesn't have one of the requested permissions. */
+    private void enforcePermissionAccessAllUserProfiles() {
+        if (!mFeatureFlags.enforceSubscriptionUserFilter()) return;
+        enforcePermissions("To access across profiles",
+                Manifest.permission.INTERACT_ACROSS_USERS,
                 Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                 Manifest.permission.INTERACT_ACROSS_PROFILES);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index c4b957e..7dd4093 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -130,7 +130,7 @@
                     + UserHandle.USER_NULL + ","
                     + Telephony.SimInfo.COLUMN_SATELLITE_ENABLED + " INTEGER DEFAULT 0,"
                     + Telephony.SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
-                    + " INTEGER DEFAULT 0, "
+                    + " INTEGER DEFAULT 1, "
                     + Telephony.SimInfo.COLUMN_IS_NTN + " INTEGER DEFAULT 0,"
                     + Telephony.SimInfo.COLUMN_SERVICE_CAPABILITIES + " INTEGER DEFAULT 7"
                     + ");";
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index c1f0c5f..7de75ae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -75,7 +75,7 @@
         mSimulatedCommands.setRadioPower(true, null);
         mPhone.mCi = this.mSimulatedCommands;
 
-        mCTUT = new GsmCdmaCallTracker(mPhone);
+        mCTUT = new GsmCdmaCallTracker(mPhone, mFeatureFlags);
         logd("GsmCdmaCallTracker initiated, waiting for Power on");
         /* Make sure radio state is power on before dial.
          * When radio state changed from off to on, CallTracker
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index cdee4a5..e493a18 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -1510,6 +1510,44 @@
         assertEquals(captor.getValue().what, Phone.EVENT_GET_RADIO_CAPABILITY);
     }
 
+    private void setIsCarrierConfigForIdentifiedCarrier(
+            PersistableBundle carrierConfig, boolean isIdentified) {
+        carrierConfig.putBoolean(
+                CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL,
+                isIdentified);
+    }
+
+    @Test
+    public void testNrCapabilityChanged_firstRequest_incompleteCarrierConfig_changeNeeded() {
+        when(mFeatureFlags.enableCarrierConfigN1Control()).thenReturn(true);
+
+        mPhoneUT.mCi = mMockCi;
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+                new int[]{
+                    CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA});
+
+        mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
+        processAllMessages();
+
+
+        verify(mMockCi, never()).isN1ModeEnabled(any());
+        verify(mMockCi, never()).setN1ModeEnabled(anyBoolean(), any());
+
+        setIsCarrierConfigForIdentifiedCarrier(bundle, true);
+
+        mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
+        processAllMessages();
+
+        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockCi, times(1)).isN1ModeEnabled(messageCaptor.capture());
+        AsyncResult.forMessage(messageCaptor.getValue(), Boolean.TRUE, null);
+        messageCaptor.getValue().sendToTarget();
+        processAllMessages();
+
+        verify(mMockCi, times(1)).setN1ModeEnabled(eq(false), messageCaptor.capture());
+    }
+
     @Test
     public void testNrCapabilityChanged_firstRequest_noChangeNeeded() {
         when(mFeatureFlags.enableCarrierConfigN1Control()).thenReturn(true);
@@ -1520,6 +1558,7 @@
                 new int[]{
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA});
+        setIsCarrierConfigForIdentifiedCarrier(bundle, true);
 
         mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
         processAllMessages();
@@ -1543,6 +1582,7 @@
                 new int[]{
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
                     CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA});
+        setIsCarrierConfigForIdentifiedCarrier(bundle, true);
 
         mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
         processAllMessages();
@@ -1554,9 +1594,6 @@
         processAllMessages();
 
         verify(mMockCi, times(1)).setN1ModeEnabled(eq(true), messageCaptor.capture());
-        AsyncResult.forMessage(messageCaptor.getValue(), Boolean.TRUE, null);
-        messageCaptor.getValue().sendToTarget();
-        processAllMessages();
     }
 
     @Test
@@ -1571,6 +1608,7 @@
         bundle.putIntArray(
                 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
                 new int[]{CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA});
+        setIsCarrierConfigForIdentifiedCarrier(bundle, true);
 
         mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
         processAllMessages();
@@ -1578,9 +1616,6 @@
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mMockCi, times(1)).isN1ModeEnabled(any()); // not called again
         verify(mMockCi, times(1)).setN1ModeEnabled(eq(false), messageCaptor.capture());
-        AsyncResult.forMessage(messageCaptor.getValue(), null, null);
-        messageCaptor.getValue().sendToTarget();
-        processAllMessages();
     }
 
     @Test
@@ -1594,6 +1629,7 @@
                     new int[]{
                         CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA,
                         CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA});
+            setIsCarrierConfigForIdentifiedCarrier(bundle, true);
 
             mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
             processAllMessages();
@@ -1642,9 +1678,6 @@
 
         verify(mMockCi, times(1)).isN1ModeEnabled(any()); // not called again
         verify(mMockCi, times(1)).setN1ModeEnabled(eq(true), messageCaptor.capture());
-        AsyncResult.forMessage(messageCaptor.getValue(), null, null);
-        messageCaptor.getValue().sendToTarget();
-        processAllMessages();
     }
 
     private void setupForWpsCallTest() throws Exception {
@@ -2835,7 +2868,10 @@
 
     @Test
     public void testCellularIdentifierDisclosure_disclosureEventAddedToNotifier() {
+        int phoneId = 0;
+        int subId = 10;
         when(mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()).thenReturn(true);
+        when(mSubscriptionManagerService.getSubId(phoneId)).thenReturn(subId);
 
         Phone phoneUT =
                 new GsmCdmaPhone(
@@ -2843,7 +2879,7 @@
                         mMockCi,
                         mNotifier,
                         true,
-                        0,
+                        phoneId,
                         PhoneConstants.PHONE_TYPE_GSM,
                         mTelephonyComponentFactory,
                         (c, p) -> mImsManager,
@@ -2861,20 +2897,23 @@
                         new AsyncResult(null, disclosure, null)));
         processAllMessages();
 
-        verify(mIdentifierDisclosureNotifier, times(1)).addDisclosure(eq(disclosure));
+        verify(mIdentifierDisclosureNotifier, times(1))
+                .addDisclosure(eq(mContext), eq(subId), eq(disclosure));
     }
 
     @Test
     public void testCellularIdentifierDisclosure_disclosureEventNull() {
+        int phoneId = 4;
+        int subId = 6;
         when(mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()).thenReturn(true);
-
+        when(mSubscriptionManagerService.getSubId(phoneId)).thenReturn(subId);
         Phone phoneUT =
                 new GsmCdmaPhone(
                         mContext,
                         mMockCi,
                         mNotifier,
                         true,
-                        0,
+                        phoneId,
                         PhoneConstants.PHONE_TYPE_GSM,
                         mTelephonyComponentFactory,
                         (c, p) -> mImsManager,
@@ -2886,7 +2925,7 @@
         processAllMessages();
 
         verify(mIdentifierDisclosureNotifier, never())
-                .addDisclosure(any(CellularIdentifierDisclosure.class));
+                .addDisclosure(eq(mContext), eq(subId), any(CellularIdentifierDisclosure.class));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index c9a497e..4c68e26 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -247,6 +247,10 @@
             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }).when(mPhoneMock2).getSubId();
 
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
         // Capture listener to emulate the carrier config change notification used later
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
@@ -885,6 +889,13 @@
         doReturn(true).when(mPhoneMock2).isUserDataEnabled();
         mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
         processAllMessages();
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(eq(1));
+        PersistableBundle bundle2 = new PersistableBundle();
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(eq(2));
+
         sendCarrierConfigChanged(0, 1);
         // Notify carrier config change on phone1 without specifying subId.
         sendCarrierConfigChanged(1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -893,13 +904,9 @@
         verify(mDataSettingsManagerMock2, never()).setDataEnabled(
                 TelephonyManager.DATA_ENABLED_REASON_USER, false, PHONE_PACKAGE);
 
-        // Still notify carrier config without specifying subId2, but this time subController
-        // and CarrierConfigManager have subId 2 active and ready.
-        doReturn(2).when(mSubscriptionManagerService).getSubId(1);
-        CarrierConfigManager cm = (CarrierConfigManager) mContext.getSystemService(
-                mContext.CARRIER_CONFIG_SERVICE);
-        doReturn(new PersistableBundle()).when(cm).getConfigForSubId(2);
-        sendCarrierConfigChanged(1, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        logd("Sending the correct phone id and sub id");
+        bundle2.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        sendCarrierConfigChanged(1, 2);
         processAllMessages();
         // This time user data should be disabled on phone1.
         verify(mDataSettingsManagerMock2).setDataEnabled(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 0869bab..f9985be 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -285,7 +286,9 @@
 
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
         setPhysicalLinkStatus(false);
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -335,7 +338,9 @@
 
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
         setPhysicalLinkStatus(true);
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -621,32 +626,37 @@
         physicalChannelConfigs.add(pcc2);
         doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
 
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
 
         // bands and bandwidths should stay ratcheted even if an empty PCC list is sent
         doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, new ArrayList<>(), null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
 
         // bands and bandwidths should stay ratcheted as long as anchor NR cell is the same
         physicalChannelConfigs.remove(pcc2);
         doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
 
         // bands and bandwidths should no longer be ratcheted if anchor NR cell changes
         // add pcc3 to front of list to ensure anchor NR cell changes from 1 -> 3
         physicalChannelConfigs.add(0, pcc3);
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
 
         physicalChannelConfigs.add(pcc2);
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -680,20 +690,23 @@
         physicalChannelConfigs.add(pcc2);
         doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
 
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
 
         // bands and bandwidths should stay ratcheted even if an empty PCC list is sent
         doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, new ArrayList<>(), null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
 
         // bands and bandwidths should change if PCC list changes
         physicalChannelConfigs.remove(pcc2);
         doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
     }
@@ -729,19 +742,22 @@
         physicalChannelConfigs.add(pcc2);
         doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
 
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
 
         // bands and bandwidths should not stay the same even if an empty PCC list is sent
         doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, new ArrayList<>(), null));
         processAllMessages();
         assertEquals("connected", getCurrentState().getName());
 
         // bands and bandwidths should change if PCC list changes
         doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
         assertEquals("connected_mmwave", getCurrentState().getName());
     }
@@ -769,7 +785,9 @@
         testTransitionToCurrentStateNrConnectedMmwave();
         doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
         setPhysicalLinkStatus(true);
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
 
         processAllMessages();
@@ -806,7 +824,9 @@
 
         doReturn(true).when(mServiceState).isUsingCarrierAggregation();
         doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -834,7 +854,9 @@
         // LTE -> LTE+
         doReturn(true).when(mServiceState).isUsingCarrierAggregation();
         doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -862,7 +884,9 @@
         // LTE -> LTE+
         doReturn(true).when(mServiceState).isUsingCarrierAggregation();
         doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -891,7 +915,9 @@
         testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
         doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
         setPhysicalLinkStatus(false);
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null,
+                        mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
         mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
         processAllMessages();
         assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -992,6 +1018,54 @@
     }
 
     @Test
+    public void testPrimaryTimerNetworkTypeChanged() throws Exception {
+        doAnswer(invocation -> {
+            doReturn(new TelephonyDisplayInfo(
+                    mNetworkTypeController.getDataNetworkType(),
+                    mNetworkTypeController.getOverrideNetworkType(),
+                    false)).when(mDisplayInfoController).getTelephonyDisplayInfo();
+            return null;
+        }).when(mDisplayInfoController).updateTelephonyDisplayInfo();
+        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .build();
+        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+                anyInt(), anyInt());
+        mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+                "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+        sendCarrierConfigChanged();
+
+        assertEquals("connected", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        // trigger 10 second timer after disconnecting from NR advanced
+        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .build();
+        doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+                anyInt(), anyInt());
+        mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+        processAllMessages();
+
+        assertEquals("legacy", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+        // timer expires
+        moveTimeForward(10 * 1000);
+        processAllMessages();
+
+        assertEquals("legacy", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+                mNetworkTypeController.getOverrideNetworkType());
+        assertFalse(mNetworkTypeController.areAnyTimersActive());
+    }
+
+    @Test
     public void testPrimaryTimerDeviceIdleMode() throws Exception {
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
         mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -1369,7 +1443,8 @@
                 .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
                 .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
                 .build());
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
@@ -1379,7 +1454,8 @@
 
         // switch to connected_rrc_idle
         physicalChannelConfigs.clear();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1438,7 +1514,8 @@
                 .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
                 .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
                 .build());
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
@@ -1448,7 +1525,8 @@
 
         // switch to connected_rrc_idle
         physicalChannelConfigs.clear();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1473,7 +1551,8 @@
                 .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
                 .setBand(41)
                 .build());
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
 
         // secondary timer expires
         moveTimeForward(30 * 1000);
@@ -1517,7 +1596,8 @@
                 .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
                 .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
                 .build());
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected", getCurrentState().getName());
@@ -1527,7 +1607,8 @@
 
         // switch to connected_rrc_idle
         physicalChannelConfigs.clear();
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1552,7 +1633,8 @@
                 .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
                 .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
                 .build());
-        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 90105e3..6743d1c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -38,6 +38,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.telephony.ModemInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyRegistryManager;
@@ -55,8 +56,12 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -69,10 +74,24 @@
     PhoneConfigurationManager.MockableInterface mMi;
 
     private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 1;
+    private static final PhoneCapability STATIC_DSDA_CAPABILITY;
     PhoneConfigurationManager mPcm;
     private FeatureFlags mFeatureFlags;
     private TelephonyRegistryManager mMockRegistryManager;
 
+    static {
+        ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true);
+        ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true);
+
+        List<ModemInfo> logicalModemList = new ArrayList<>();
+        logicalModemList.add(modemInfo1);
+        logicalModemList.add(modemInfo2);
+        int[] deviceNrCapabilities = new int[0];
+
+        STATIC_DSDA_CAPABILITY = new PhoneCapability(2, 1, logicalModemList, false,
+                deviceNrCapabilities);
+    }
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -85,7 +104,7 @@
         mPhone.mCi = mMockCi0;
         mCT.mCi = mMockCi0;
         mPhone1.mCi = mMockCi1;
-        doReturn(RIL.RADIO_HAL_VERSION_2_1).when(mMockRadioConfigProxy).getVersion();
+        doReturn(RIL.RADIO_HAL_VERSION_2_2).when(mMockRadioConfigProxy).getVersion();
         mMockRegistryManager = mContext.getSystemService(TelephonyRegistryManager.class);
     }
 
@@ -138,15 +157,7 @@
         init(1);
         assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
 
-        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
-        verify(mMockRadioConfig).getPhoneCapability(captor.capture());
-        Message msg = captor.getValue();
-        AsyncResult.forMessage(msg, PhoneCapability.DEFAULT_DSDS_CAPABILITY, null);
-        msg.sendToTarget();
-        processAllMessages();
-
-        // Not static capability should indicate DSDS capable.
-        assertEquals(PhoneCapability.DEFAULT_DSDS_CAPABILITY, mPcm.getStaticPhoneCapability());
+        setAndVerifyStaticCapability(PhoneCapability.DEFAULT_DSDS_CAPABILITY);
     }
 
     @Test
@@ -211,12 +222,77 @@
         assertTrue(mPcm.getSlotsSupportingSimultaneousCellularCalls().isEmpty());
     }
 
+    /**
+     * If the device uses the older "dsda" multi_sim_config setting, ensure that DSDA is set
+     * statically for that device and subId updates work.
+     */
+    @Test
+    @SmallTest
+    public void testBkwdsCompatSimultaneousCallingDsda() throws Exception {
+        doReturn(true).when(mFeatureFlags).simultaneousCallingIndications();
+        doReturn(RIL.RADIO_HAL_VERSION_2_1).when(mMockRadioConfigProxy).getVersion();
+        doReturn(Optional.of("dsda")).when(mMi).getMultiSimProperty();
+        final int phone0SubId = 2;
+        final int phone1SubId = 3;
+        mPhones = new Phone[]{mPhone, mPhone1};
+        doReturn(0).when(mPhone).getPhoneId();
+        doReturn(1).when(mPhone1).getPhoneId();
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        init(2);
+        doReturn(phone0SubId).when(mPhone).getSubId();
+        doReturn(phone1SubId).when(mPhone1).getSubId();
+        Set<Integer>[] cachedSimultaneousCallingSlots = new Set[]{Collections.emptySet()};
+        mPcm.registerForSimultaneousCellularCallingSlotsChanged(newSlots ->
+                cachedSimultaneousCallingSlots[0] = newSlots);
+
+        mPcm.getStaticPhoneCapability();
+        setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
+        ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> cBCaptor =
+                ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
+        verify(mMockRegistryManager).addOnSubscriptionsChangedListener(cBCaptor.capture(), any());
+        processAllMessages();
+
+        int[] enabledLogicalSlots = {0, 1};
+        HashSet<Integer> expectedSlots = new HashSet<>(2);
+        for (int i : enabledLogicalSlots) {
+            expectedSlots.add(i);
+        }
+        HashSet<Integer> expectedSubIds = new HashSet<>(2);
+        expectedSubIds.add(phone0SubId);
+        expectedSubIds.add(phone1SubId);
+        assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
+        verify(mMockRegistryManager).notifySimultaneousCellularCallingSubscriptionsChanged(
+                eq(expectedSubIds));
+        assertEquals(expectedSlots, cachedSimultaneousCallingSlots[0]);
+
+        // Change sub ID mapping
+        final int phone1SubIdV2 = 4;
+        expectedSubIds.clear();
+        expectedSubIds.add(phone0SubId);
+        expectedSubIds.add(phone1SubIdV2);
+        doReturn(phone1SubIdV2).when(mPhone1).getSubId();
+        cBCaptor.getValue().onSubscriptionsChanged();
+        processAllMessages();
+        verify(mMockRegistryManager, times(2))
+                .notifySimultaneousCellularCallingSubscriptionsChanged(eq(expectedSubIds));
+    }
+
     @Test
     @SmallTest
     public void testUpdateSimultaneousCallingSupportNotifications() throws Exception {
         // retry simultaneous calling tests, but with notifications enabled this time
         doReturn(true).when(mFeatureFlags).simultaneousCallingIndications();
+
+        final int phone0SubId = 2;
+        final int phone1SubId = 3;
+        mPhones = new Phone[]{mPhone, mPhone1};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
         init(2);
+        doReturn(phone0SubId).when(mPhone).getSubId();
+        doReturn(phone1SubId).when(mPhone1).getSubId();
+        Set<Integer>[] cachedSimultaneousCallingSlots = new Set[]{Collections.emptySet()};
+        mPcm.registerForSimultaneousCellularCallingSlotsChanged(newSlots ->
+                cachedSimultaneousCallingSlots[0] = newSlots);
 
         // Simultaneous calling enabled
         mPcm.updateSimultaneousCallingSupport();
@@ -228,13 +304,17 @@
         msg.sendToTarget();
         processAllMessages();
 
-        HashSet<Integer> expectedSlots = new HashSet<>();
+        HashSet<Integer> expectedSlots = new HashSet<>(2);
         for (int i : enabledLogicalSlots) {
             expectedSlots.add(i);
         }
+        HashSet<Integer> expectedSubIds = new HashSet<>(2);
+        expectedSubIds.add(phone0SubId);
+        expectedSubIds.add(phone1SubId);
         assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
         verify(mMockRegistryManager).notifySimultaneousCellularCallingSubscriptionsChanged(
-                eq(expectedSlots));
+                eq(expectedSubIds));
+        assertEquals(expectedSlots, cachedSimultaneousCallingSlots[0]);
 
         // Simultaneous Calling Disabled
         mPcm.updateSimultaneousCallingSupport();
@@ -249,6 +329,59 @@
         assertEquals(Collections.emptySet(), mPcm.getSlotsSupportingSimultaneousCellularCalls());
         verify(mMockRegistryManager, times(2))
                 .notifySimultaneousCellularCallingSubscriptionsChanged(eq(Collections.emptySet()));
+        assertEquals(Collections.emptySet(), cachedSimultaneousCallingSlots[0]);
+    }
+
+    @Test
+    @SmallTest
+    public void testSimultaneousCallingSubIdMappingChanges() throws Exception {
+        doReturn(true).when(mFeatureFlags).simultaneousCallingIndications();
+        final int phone0SubId = 2;
+        final int phone1SubId = 3;
+        mPhones = new Phone[]{mPhone, mPhone1};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        init(2);
+        doReturn(phone0SubId).when(mPhone).getSubId();
+        doReturn(phone1SubId).when(mPhone1).getSubId();
+
+        // Set the capability to DSDA mode to register listener, which will also trigger
+        // simultaneous calling evaluation
+        mPcm.getCurrentPhoneCapability();
+        setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
+        ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> cBCaptor =
+                ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
+        verify(mMockRegistryManager).addOnSubscriptionsChangedListener(cBCaptor.capture(), any());
+
+        int[] enabledLogicalSlots = {0, 1};
+        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
+        Message msg = captor.getValue();
+        // Simultaneous calling enabled
+        AsyncResult.forMessage(msg, enabledLogicalSlots, null);
+        msg.sendToTarget();
+        processAllMessages();
+
+        HashSet<Integer> expectedSlots = new HashSet<>(2);
+        for (int i : enabledLogicalSlots) {
+            expectedSlots.add(i);
+        }
+        HashSet<Integer> expectedSubIds = new HashSet<>(2);
+        expectedSubIds.add(phone0SubId);
+        expectedSubIds.add(phone1SubId);
+        assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
+        verify(mMockRegistryManager).notifySimultaneousCellularCallingSubscriptionsChanged(
+                eq(expectedSubIds));
+
+        // Change sub ID mapping
+        final int phone1SubIdV2 = 4;
+        expectedSubIds.clear();
+        expectedSubIds.add(phone0SubId);
+        expectedSubIds.add(phone1SubIdV2);
+        doReturn(phone1SubIdV2).when(mPhone1).getSubId();
+        cBCaptor.getValue().onSubscriptionsChanged();
+        processAllMessages();
+        verify(mMockRegistryManager, times(2))
+                .notifySimultaneousCellularCallingSubscriptionsChanged(eq(expectedSubIds));
     }
 
     @Test
@@ -429,4 +562,15 @@
         verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
         verify(mMockCi1, times(1)).onSlotActiveStatusChange(anyBoolean());
     }
+
+    private void setAndVerifyStaticCapability(PhoneCapability capability) {
+        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockRadioConfig).getPhoneCapability(captor.capture());
+        Message msg = captor.getValue();
+        AsyncResult.forMessage(msg, capability, null);
+        msg.sendToTarget();
+        processAllMessages();
+
+        assertEquals(capability, mPcm.getStaticPhoneCapability());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 4fa42c9..1a6557b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -260,7 +260,7 @@
         mSatelliteController = Mockito.mock(SatelliteController.class);
         replaceInstance(SatelliteController.class, "sInstance", null,
                 mSatelliteController);
-        doReturn(new ArrayList<>()).when(mSatelliteController).getAllSatellitePlmnsForCarrier(
+        doReturn(new ArrayList<>()).when(mSatelliteController).getSatellitePlmnsForCarrier(
                 anyInt());
 
         mContextFixture.putResource(R.string.kg_text_message_separator, " \u2014 ");
@@ -3387,7 +3387,7 @@
         CellIdentityGsm cellIdentity =
                 new CellIdentityGsm(0, 1, 900, 5, "101", "23", "test", "tst",
                         Collections.emptyList());
-        doReturn(Arrays.asList("10123")).when(mSatelliteController).getAllSatellitePlmnsForCarrier(
+        doReturn(Arrays.asList("10123")).when(mSatelliteController).getSatellitePlmnsForCarrier(
                 anyInt());
         doReturn(satelliteSupportedServiceList).when(mSatelliteController)
                 .getSupportedSatelliteServices(sst.mSubId, "10123");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index b515372..cc33a9e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -125,6 +125,7 @@
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
 import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource;
 import com.android.internal.telephony.security.NullCipherNotifier;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.test.SimulatedCommands;
@@ -285,6 +286,7 @@
     protected ServiceStateStats mServiceStateStats;
     protected SatelliteController mSatelliteController;
     protected DeviceStateHelper mDeviceStateHelper;
+    protected CellularNetworkSecuritySafetySource mSafetySource;
     protected CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier;
     protected DomainSelectionResolver mDomainSelectionResolver;
     protected NullCipherNotifier mNullCipherNotifier;
@@ -560,6 +562,7 @@
         mServiceStateStats = Mockito.mock(ServiceStateStats.class);
         mSatelliteController = Mockito.mock(SatelliteController.class);
         mDeviceStateHelper = Mockito.mock(DeviceStateHelper.class);
+        mSafetySource = Mockito.mock(CellularNetworkSecuritySafetySource.class);
         mIdentifierDisclosureNotifier = Mockito.mock(CellularIdentifierDisclosureNotifier.class);
         mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
         mNullCipherNotifier = Mockito.mock(NullCipherNotifier.class);
@@ -635,7 +638,7 @@
                         nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
                         nullable(Object.class), any(FeatureFlags.class));
         doReturn(mCT).when(mTelephonyComponentFactory)
-                .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
+                .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class), any(FeatureFlags.class));
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
                 .makeIccPhoneBookInterfaceManager(nullable(Phone.class));
         doReturn(mDisplayInfoController).when(mTelephonyComponentFactory)
@@ -676,9 +679,11 @@
                         any(DataServiceManager.class), any(Looper.class),
                         any(FeatureFlags.class),
                         any(DataProfileManager.DataProfileManagerCallback.class));
+        doReturn(mSafetySource).when(mTelephonyComponentFactory)
+                .makeCellularNetworkSecuritySafetySource(any(Context.class));
         doReturn(mIdentifierDisclosureNotifier)
                 .when(mTelephonyComponentFactory)
-                .makeIdentifierDisclosureNotifier();
+                .makeIdentifierDisclosureNotifier(any(CellularNetworkSecuritySafetySource.class));
         doReturn(mNullCipherNotifier)
                 .when(mTelephonyComponentFactory)
                 .makeNullCipherNotifier();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
index 6462d73..5b91aee 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -76,6 +76,7 @@
     private static final int SCORE_TOLERANCE = 100;
     private static final int GOOD_RAT_SIGNAL_SCORE = 200;
     private static final int BAD_RAT_SIGNAL_SCORE = 50;
+    private boolean mIsNonTerrestrialNetwork = false;
     // Mocked
     private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
 
@@ -105,6 +106,17 @@
 
         mPhones = new Phone[]{mPhone, mPhone2};
         for (Phone phone : mPhones) {
+            ServiceState ss = new ServiceState();
+
+            ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .setRegistrationState(
+                            NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING)
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setIsNonTerrestrialNetwork(mIsNonTerrestrialNetwork)
+                    .build());
+
+            doReturn(ss).when(phone).getServiceState();
             doReturn(mSST).when(phone).getServiceStateTracker();
             doReturn(mDisplayInfoController).when(phone).getDisplayInfoController();
             doReturn(mSignalStrengthController).when(phone).getSignalStrengthController();
@@ -155,6 +167,7 @@
                 mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
 
         doReturn(true).when(mFeatureFlags).autoSwitchAllowRoaming();
+        doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
     }
 
     @After
@@ -246,6 +259,31 @@
     }
 
     @Test
+    public void testRoaming_prefer_roam_over_nonTerrestrial() {
+        // DDS -> nDDS: Prefer Roaming over non-terrestrial
+        prepareIdealUsesNonDdsCondition();
+        mIsNonTerrestrialNetwork = true;
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        mIsNonTerrestrialNetwork = false;
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
+
+        // nDDS -> DDS: Prefer Roaming over non-terrestrial
+        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+        mIsNonTerrestrialNetwork = false;
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mIsNonTerrestrialNetwork = true;
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                true/*needValidation*/);
+        mIsNonTerrestrialNetwork = false;
+    }
+
+    @Test
     public void testRoaming_roaming_but_roam_disabled() {
         // Disable RAT + signalStrength base switching.
         doReturn(-1).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
@@ -677,6 +715,7 @@
                 .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                 .setRegistrationState(dataRegState)
                 .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setIsNonTerrestrialNetwork(mIsNonTerrestrialNetwork)
                 .build());
 
         ss.setDataRoamingFromRegistration(dataRegState
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 a2c9724..fa4f8e4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -775,9 +775,10 @@
                         "capabilities=eims, retry_interval=1000, maximum_retries=20",
                         "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|"
                                 + "-3|65543|65547|2252|2253|2254, retry_interval=2500",
-                        "capabilities=mms|supl|cbs, retry_interval=2000",
-                        "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
-                                + "5000|10000|15000|20000|40000|60000|120000|240000|"
+                        "capabilities=mms|supl|cbs|rcs, retry_interval=2000",
+                        "capabilities=internet|enterprise|dun|ims|fota|xcap|mcx|"
+                                + "prioritize_bandwidth|prioritize_latency, retry_interval="
+                                + "2500|3000|5000|10000|15000|20000|40000|60000|120000|240000|"
                                 + "600000|1200000|1800000, maximum_retries=20"
                 });
         mCarrierConfig.putStringArray(
@@ -3947,6 +3948,50 @@
     }
 
     @Test
+    public void testNoGracefulTearDownForEmergencyDataNetwork() throws Exception {
+        setImsRegistered(true);
+
+        mCarrierConfig.putStringArray(CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
+                new String[]{"source=EUTRAN, target=IWLAN, type=disallowed, capabilities=EIMS|IMS",
+                        "source=IWLAN, target=EUTRAN, type=disallowed, capabilities=MMS"});
+        // Force data config manager to reload the carrier config.
+        carrierConfigChanged();
+        processAllMessages();
+
+        // setup emergency data network.
+        NetworkCapabilities netCaps = new NetworkCapabilities();
+        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
+        netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE);
+
+        NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
+                ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST);
+        TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(
+                nativeNetworkRequest, mPhone);
+
+        mDataNetworkControllerUT.addNetworkRequest(networkRequest);
+        processAllMessages();
+
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_EIMS);
+
+        updateTransport(NetworkCapabilities.NET_CAPABILITY_EIMS,
+                AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+        // Verify all data disconnected.
+        verify(mMockedDataNetworkControllerCallback).onAnyDataNetworkExistingChanged(eq(false));
+        verify(mMockedDataNetworkControllerCallback).onPhysicalLinkStatusChanged(
+                eq(DataCallResponse.LINK_STATUS_INACTIVE));
+
+        // A new data network should be connected on IWLAN
+        List<DataNetwork> dataNetworkList = getDataNetworks();
+        assertThat(dataNetworkList).hasSize(1);
+        assertThat(dataNetworkList.get(0).isConnected()).isTrue();
+        assertThat(dataNetworkList.get(0).getNetworkCapabilities().hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_EIMS)).isTrue();
+        assertThat(dataNetworkList.get(0).getTransport())
+                .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+    }
+
+    @Test
     public void testNetworkRequestRemovedBeforeRetry() {
         setFailedSetupDataResponse(mMockedWwanDataServiceManager, DataFailCause.CONGESTION,
                 DataCallResponse.RETRY_DURATION_UNDEFINED, false);
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 edeb191..ad1ba61 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -76,6 +76,7 @@
 
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
 import com.android.internal.telephony.data.DataEvaluation.DataAllowedReason;
 import com.android.internal.telephony.data.DataNetwork.DataNetworkCallback;
@@ -123,7 +124,7 @@
             .setApnName("fake_apn")
             .setUser("user")
             .setPassword("passwd")
-            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
+            .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS)
             .setProtocol(ApnSetting.PROTOCOL_IPV6)
             .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
             .setCarrierEnabled(true)
@@ -134,6 +135,18 @@
             .setMaxConnsTime(789)
             .build();
 
+    private final ApnSetting mMmsApnSetting = new ApnSetting.Builder()
+            .setId(2164)
+            .setOperatorNumeric("12345")
+            .setEntryName("fake_mms_apn")
+            .setApnName("fake_mms_apn")
+            .setApnTypeBitmask(ApnSetting.TYPE_MMS)
+            .setProtocol(ApnSetting.PROTOCOL_IPV6)
+            .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+            .setCarrierEnabled(true)
+            .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN)
+            .build();
+
     private final ApnSetting mImsApnSetting = new ApnSetting.Builder()
             .setId(2163)
             .setOperatorNumeric("12345")
@@ -157,6 +170,11 @@
             .setTrafficDescriptor(new TrafficDescriptor("fake_apn", null))
             .build();
 
+    private final DataProfile mMmsDataProfile = new DataProfile.Builder()
+            .setApnSetting(mMmsApnSetting)
+            .setTrafficDescriptor(new TrafficDescriptor("fake_apn", null))
+            .build();
+
     private final DataProfile mImsDataProfile = new DataProfile.Builder()
             .setApnSetting(mImsApnSetting)
             .setTrafficDescriptor(new TrafficDescriptor("fake_apn", null))
@@ -2203,4 +2221,43 @@
                 mDataNetworkUT, mDataCallSessionStats);
         processAllMessages();
     }
+
+    @Test
+    public void testMmsCapabilityRemovedWhenMmsPreferredOnIwlan() throws Exception {
+        doReturn(true).when(mFeatureFlags).forceIwlanMms();
+        setupDataNetwork();
+
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isTrue();
+
+        ArgumentCaptor<AccessNetworksManagerCallback> accessNetworksManagerCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(AccessNetworksManagerCallback.class);
+        verify(mAccessNetworksManager).registerCallback(
+                accessNetworksManagerCallbackArgumentCaptor.capture());
+
+        // Now QNS prefers MMS on IWLAN
+        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
+                .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+        doReturn(mMmsDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
+                any(TelephonyNetworkRequest.class),
+                    eq(TelephonyManager.NETWORK_TYPE_IWLAN), eq(false), eq(false), eq(false));
+        accessNetworksManagerCallbackArgumentCaptor.getValue()
+                .onPreferredTransportChanged(NetworkCapabilities.NET_CAPABILITY_MMS);
+        processAllMessages();
+
+        // Check if MMS capability is removed.
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isFalse();
+
+        // Now QNS prefers MMS on IWLAN
+        doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
+            .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+        accessNetworksManagerCallbackArgumentCaptor.getValue()
+                .onPreferredTransportChanged(NetworkCapabilities.NET_CAPABILITY_MMS);
+        processAllMessages();
+
+        // Check if MMS capability is added back.
+        assertThat(mDataNetworkUT.getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isTrue();
+    }
 }
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 5d5ef64..e011a60 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -1936,6 +1936,17 @@
         }
 
         replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        for (Phone phone : mPhones) {
+            ServiceState ss = new ServiceState();
+
+            ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .setRegistrationState(
+                            NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING)
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .build());
+            doReturn(ss).when(phone).getServiceState();
+        }
     }
 
     private void initializeCommandInterfacesMock() {
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 cd7aadc..f85bcbe 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -27,15 +27,19 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
+import android.telecom.PhoneAccount;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.DomainSelectionService;
@@ -171,7 +175,116 @@
         IWwanSelectorResultCallback resultCallback =
                 Mockito.mock(IWwanSelectorResultCallback.class);
 
-        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback);
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Integer> eventCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(
+                handlerCaptor.capture(), eventCaptor.capture(), any());
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), any());
+
+        Handler handler = handlerCaptor.getValue();
+        int event = eventCaptor.getValue();
+
+        assertNotNull(handler);
+
+        EmergencyRegResult regResult =
+                new EmergencyRegResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+        handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
+        processAllMessages();
+
+        verify(resultCallback).onComplete(eq(regResult));
+        verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndCancel() throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, null, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        wwanCallback.onRequestEmergencyNetworkScan(new int[] { }, SCAN_TYPE_NO_PREFERENCE,
+                false, Mockito.mock(IWwanSelectorResultCallback.class));
+        processAllMessages();
+
+        verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+        wwanCallback.onCancel();
+        processAllMessages();
+
+        verify(mPhone).cancelEmergencyNetworkScan(eq(false), any());
+    }
+
+    @Test
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanWithResetScan()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_NO_PREFERENCE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                true, resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+        verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
+
+        verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+        Message msg = msgCaptor.getValue();
+
+        assertNotNull(msg);
+
+        AsyncResult unused = AsyncResult.forMessage(msg);
+        msg.sendToTarget();
         processAllMessages();
 
         ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
@@ -199,8 +312,8 @@
     }
 
     @Test
-    @SmallTest
-    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndCancel() throws Exception {
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanWithResetScanDoneAndCancel()
+            throws Exception {
         mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
                 mDomainSelectionController);
 
@@ -210,7 +323,7 @@
 
         DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
                 mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
-                false, 0, null, null, null, null);
+                false, 0, TELECOM_CALL_ID1, null, null, null);
 
         mDsc.selectDomain(attr);
 
@@ -221,17 +334,100 @@
 
         assertNotNull(wwanCallback);
 
-        wwanCallback.onRequestEmergencyNetworkScan(new int[] { },
-                SCAN_TYPE_NO_PREFERENCE, Mockito.mock(IWwanSelectorResultCallback.class));
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_NO_PREFERENCE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                true, resultCallback);
         processAllMessages();
 
+        ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+        verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
+        verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+        Message msg = msgCaptor.getValue();
+
+        assertNotNull(msg);
+
+        // Reset completes.
+        AsyncResult unused = AsyncResult.forMessage(msg);
+        msg.sendToTarget();
+        processAllMessages();
+
+        // Verify that scan is requested.
         verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
         verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
+        // Cancele scan after reset completes.
         wwanCallback.onCancel();
         processAllMessages();
 
+        // Verify scan request is canceled.
         verify(mPhone).cancelEmergencyNetworkScan(eq(false), any());
+        verify(mPhone, times(2)).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanWithResetScanAndCancel()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_NO_PREFERENCE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                true, resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+        verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
+        verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+        Message msg = msgCaptor.getValue();
+
+        assertNotNull(msg);
+
+        // Canceled before reset completes.
+        wwanCallback.onCancel();
+        processAllMessages();
+
+        // Verify there is no additional cancel.
+        verify(mPhone, times(1)).cancelEmergencyNetworkScan(anyBoolean(), any());
+
+        // Reset completes
+        AsyncResult unused = AsyncResult.forMessage(msg);
+        msg.sendToTarget();
+        processAllMessages();
+
+        // Verify there is no scan request after reset completes.
+        verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
     }
 
     @Test
@@ -255,7 +451,7 @@
 
         mDsc.cancelSelection();
 
-        verify(domainSelector).cancelSelection();
+        verify(domainSelector).finishSelection();
     }
 
     @Test
@@ -393,7 +589,8 @@
                 Mockito.mock(IWwanSelectorResultCallback.class);
 
         // 1st scan request from remote service
-        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback);
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback);
         processAllMessages();
 
         ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
@@ -429,7 +626,8 @@
         // 2nd scan request
         IWwanSelectorResultCallback resultCallback2 =
                 Mockito.mock(IWwanSelectorResultCallback.class);
-        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback2);
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback2);
         processAllMessages();
 
         // Verify that triggerEmergencyNetworkScan isn't called
@@ -476,7 +674,8 @@
                 Mockito.mock(IWwanSelectorResultCallback.class);
 
         // 1st scan request from remote service
-        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback);
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback);
         processAllMessages();
 
         ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
@@ -517,7 +716,8 @@
         // 2nd scan request
         IWwanSelectorResultCallback resultCallback2 =
                 Mockito.mock(IWwanSelectorResultCallback.class);
-        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback2);
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback2);
         processAllMessages();
 
         // Verify that triggerEmergencyNetworkScan is called
@@ -594,7 +794,9 @@
                 .setCsDisconnectCause(callFailCause);
 
         if (callId != null) builder.setCallId(callId);
-        if (number != null) builder.setNumber(number);
+        if (number != null) {
+            builder.setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
+        }
         if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
         if (regResult != null) builder.setEmergencyRegResult(regResult);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
index 76de9c9..7797b9b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
@@ -121,7 +121,7 @@
         DomainSelectionService.SelectionAttributes attr =
                 EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                         mPhone.getPhoneId(), mPhone.getSubId(), false,
-                        TELECOM_CALL_ID1, "911", 0, null, regResult);
+                        TELECOM_CALL_ID1, "911", false, 0, null, regResult);
 
         CompletableFuture<Integer> future =
                 mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
@@ -170,7 +170,7 @@
         DomainSelectionService.SelectionAttributes attr =
                 EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                         mPhone.getPhoneId(), mPhone.getSubId(), false,
-                        TELECOM_CALL_ID1, "911", 0, null, regResult);
+                        TELECOM_CALL_ID1, "911", false, 0, null, regResult);
 
         CompletableFuture<Integer> future =
                 mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
@@ -208,7 +208,7 @@
         DomainSelectionService.SelectionAttributes attr =
                 EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                         mPhone.getPhoneId(), mPhone.getSubId(), false,
-                        TELECOM_CALL_ID1, "911", 0, null, regResult);
+                        TELECOM_CALL_ID1, "911", false, 0, null, regResult);
 
         CompletableFuture<Integer> future =
                 mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
@@ -242,7 +242,7 @@
         DomainSelectionService.SelectionAttributes attr =
                 EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                         mPhone.getPhoneId(), mPhone.getSubId(), false,
-                        TELECOM_CALL_ID1, "911", 0, null, regResult);
+                        TELECOM_CALL_ID1, "911", false, 0, null, regResult);
 
         mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
         mTransportCallback.onSelectionTerminated(ERROR_UNSPECIFIED);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
index c25aeb9..4f63be0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
@@ -444,7 +444,7 @@
 
         assertFalse(future.isDone());
         verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
-        verify(mDomainSelector).cancelSelection();
+        verify(mDomainSelector).finishSelection();
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
index f05943f..72d8524 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
@@ -188,10 +188,10 @@
                 NormalCallDomainSelectionConnection.getSelectionAttributes(1, 2,
                         TELECOM_CALL_ID1, "123", false, 10, imsReasonInfo);
 
-        assertEquals(1, attributes.getSlotId());
-        assertEquals(2, attributes.getSubId());
+        assertEquals(1, attributes.getSlotIndex());
+        assertEquals(2, attributes.getSubscriptionId());
         assertEquals(TELECOM_CALL_ID1, attributes.getCallId());
-        assertEquals("123", attributes.getNumber());
+        assertEquals("123", attributes.getAddress().getSchemeSpecificPart());
         assertEquals(false, attributes.isVideoCall());
         assertEquals(false, attributes.isEmergency());
         assertEquals(SELECTOR_TYPE_CALLING, attributes.getSelectorType());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
index b9112be..2a76770 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
@@ -6,3 +6,4 @@
 mkoon@google.com
 seheele@google.com
 radhikaagrawal@google.com
+jdyou@google.com
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
index 05291e2..5799dd8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
@@ -205,7 +205,7 @@
 
         mDsConnection.finishSelection();
 
-        verify(mDomainSelector).cancelSelection();
+        verify(mDomainSelector).finishSelection();
     }
 
     private void setUpTestableLooper() throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
index 27b53ad..b46f8bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -23,6 +23,7 @@
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
+import static com.android.internal.telephony.emergency.EmergencyStateTracker.DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -52,9 +53,11 @@
 import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.EmergencyRegResult;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyCallback;
@@ -151,6 +154,13 @@
                 false /* isRadioOn */);
         setConfigForDdsSwitch(testPhone, null,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
+        ServiceState ss = mock(ServiceState.class);
+        doReturn(ss).when(mSST).getServiceState();
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .build();
+        doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
         // Spy is used to capture consumer in delayDialForDdsSwitch
         EmergencyStateTracker spyEst = spy(emergencyStateTracker);
         CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone, TEST_CALL_ID,
@@ -160,13 +170,77 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(0));
-        // isOkToCall() should return true once radio is on
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+        // isOkToCall() should return true when IN_SERVICE state
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
         when(mSST.isRadioOn()).thenReturn(true);
-        assertTrue(callback.getValue()
+        assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+        nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setRegistrationState(REGISTRATION_STATE_HOME)
+                .build();
+        doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
+
+        assertTrue(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
+        // Once radio on is complete, trigger delay dial
+        callback.getValue().onComplete(null, true);
+        ArgumentCaptor<Consumer<Boolean>> completeConsumer = ArgumentCaptor
+                .forClass(Consumer.class);
+        verify(spyEst).switchDdsDelayed(eq(testPhone), completeConsumer.capture());
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(testPhone.getPhoneId()),
+                eq(150) /* extensionTime */, any());
+        // After dds switch completes successfully, set emergency mode
+        completeConsumer.getValue().accept(true);
+        verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+    }
+
+    /**
+     * Test that the EmergencyStateTracker turns on radio, performs a DDS switch and sets emergency
+     * mode switch when we are not roaming and the carrier only supports SUPL over the data plane.
+     */
+    @Test
+    @SmallTest
+    public void startEmergencyCall_radioOff_turnOnRadioTimeoutSwitchDdsAndSetEmergencyMode() {
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+        // Create test Phones and set radio off
+        Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+                false /* isRadioOn */);
+        setConfigForDdsSwitch(testPhone, null,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
+        ServiceState ss = mock(ServiceState.class);
+        doReturn(ss).when(mSST).getServiceState();
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .build();
+        doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
+        // Spy is used to capture consumer in delayDialForDdsSwitch
+        EmergencyStateTracker spyEst = spy(emergencyStateTracker);
+        CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone, TEST_CALL_ID,
+                false);
+
+        // startEmergencyCall should trigger radio on
+        ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
+                .forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+        // onTimeout should return true when radion on
+        assertFalse(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+        assertFalse(callback.getValue()
+                .onTimeout(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+        when(mSST.isRadioOn()).thenReturn(true);
+
+        assertFalse(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+        assertTrue(callback.getValue()
+                .onTimeout(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
         // Once radio on is complete, trigger delay dial
         callback.getValue().onComplete(null, true);
         ArgumentCaptor<Consumer<Boolean>> completeConsumer = ArgumentCaptor
@@ -199,7 +273,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(0));
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
         // Verify future completes with DisconnectCause.POWER_OFF if radio not ready
         CompletableFuture<Void> unused = future.thenAccept((result) -> {
             assertEquals((Integer) result, (Integer) DisconnectCause.POWER_OFF);
@@ -1008,11 +1082,9 @@
         processAllMessages();
 
         assertTrue(emergencyStateTracker.isInEmergencyMode());
-        assertTrue(emergencyStateTracker.isInEmergencyCall());
-        // Expect: DisconnectCause#NOT_DISCONNECTED.
-        assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
-                Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
-        verify(phone0, never()).setEmergencyMode(anyInt(), any(Message.class));
+        assertFalse(emergencyStateTracker.isInEmergencyCall());
+        assertFalse(future.isDone());
+        verify(phone0).setEmergencyMode(anyInt(), any(Message.class));
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
index 2c5a873..5a6fdc2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
@@ -90,7 +90,7 @@
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
         verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
-        verify(mSatelliteController).unregisterForSatelliteModemStateChanged(anyInt(), any());
+        verify(mSatelliteController).unregisterForModemStateChanged(anyInt(), any());
         verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
                 eq(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
         verify(mSatelliteController, never()).registerForSatelliteModemStateChanged(
@@ -110,7 +110,7 @@
 
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
-        verify(mSatelliteController).unregisterForSatelliteModemStateChanged(anyInt(), any());
+        verify(mSatelliteController).unregisterForModemStateChanged(anyInt(), any());
         verify(mSatelliteController).registerForSatelliteModemStateChanged(anyInt(), any());
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
index 7d6557d..3b362b1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
@@ -41,6 +41,9 @@
 import android.util.ArraySet;
 
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
@@ -66,6 +69,8 @@
     private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
     private Handler mPreciseCallStateHandler;
 
+    private Phone mPassthroughPhone;
+
     @Mock private ImsPhoneCall mForegroundCall;
     @Mock private ImsPhoneCall mBackgroundCall;
     private Call.State mActiveState = ImsPhoneCall.State.ACTIVE;
@@ -90,7 +95,13 @@
         doReturn(mAnyInt).when(mImsPhone).getSubId();
         doReturn(mContextFixture.getCarrierConfigBundle()).when(mCarrierConfigManager)
                 .getConfigForSubId(anyInt(), any());
-        doReturn(mPhone).when(mImsPhone).getDefaultPhone();
+
+        mPassthroughPhone = new GsmCdmaPhone(
+                mContext, mSimulatedCommands, mNotifier, true, 0,
+                PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory,
+                (c, p) -> mImsManager, mFeatureFlags);
+
+        doReturn(mPassthroughPhone).when(mImsPhone).getDefaultPhone();
 
         doReturn(mForegroundCall).when(mImsPhone).getForegroundCall();
         doReturn(mBackgroundCall).when(mImsPhone).getBackgroundCall();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 6496efb..14cff4b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -1603,6 +1603,17 @@
         assertEquals(2, copiedDialArgs.eccCategory);
     }
 
+    @Test
+    @SmallTest
+    public void testCanMakeWifiCall() {
+        mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
+        mImsPhoneUT.setImsRegistered(true);
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).when(mImsCT)
+                .getImsRegistrationTech();
+
+        assertTrue(mImsPhoneUT.canMakeWifiCall());
+    }
+
     private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
         ServiceState ss = new ServiceState();
         ss.setStateOutOfService();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
index 14169a7..f831c98 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
@@ -753,7 +753,7 @@
         assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
         assertEquals(0L, stats.registeredMillis);
         assertEquals(2000L, stats.registeringMillis);
-        assertEquals(0, stats.registeredTimes);
+        assertEquals(1, stats.registeredTimes);
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
index 5ee7e8f..f8827be 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
@@ -76,7 +76,7 @@
         replaceInstance(SatelliteController.class, "sInstance", null,
                 mMockSatelliteController);
         doReturn(Arrays.asList(SATELLITE_PLMN_ARRAY))
-                .when(mMockSatelliteController).getAllSatellitePlmnsForCarrier(anyInt());
+                .when(mMockSatelliteController).getSatellitePlmnsForCarrier(anyInt());
         doReturn(mSatelliteSupportedServiceList).when(mMockSatelliteController)
                 .getSupportedSatelliteServices(SUB_ID, SATELLITE_PLMN);
     }
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 bfe53bc..4c0056c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -83,6 +83,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.AsyncResult;
@@ -95,7 +96,6 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceSpecificException;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -113,6 +113,8 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.R;
 import com.android.internal.telephony.IIntegerConsumer;
@@ -158,7 +160,10 @@
     private static final String TEST_NEXT_SATELLITE_TOKEN = "TEST_NEXT_SATELLITE_TOKEN";
     private static final String[] EMPTY_STRING_ARRAY = {};
     private static final List<String> EMPTY_STRING_LIST = new ArrayList<>();
+    private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
+            "satellite_system_notification_done_key";
     private static final int[] ACTIVE_SUB_IDS = {SUB_ID};
+
     private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
             mCarrierConfigChangedListenerList = new ArrayList<>();
 
@@ -175,6 +180,7 @@
     @Mock private ProvisionMetricsStats mMockProvisionMetricsStats;
     @Mock private SessionMetricsStats mMockSessionMetricsStats;
     @Mock private SubscriptionManagerService mMockSubscriptionManagerService;
+    @Mock private NotificationManager mMockNotificationManager;
     private List<Integer> mIIntegerConsumerResults =  new ArrayList<>();
     @Mock private ISatelliteTransmissionUpdateCallback mStartTransmissionUpdateCallback;
     @Mock private ISatelliteTransmissionUpdateCallback mStopTransmissionUpdateCallback;
@@ -502,6 +508,13 @@
         doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
         doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        doReturn(mSST).when(mPhone).getServiceStateTracker();
+        doReturn(mSST).when(mPhone2).getServiceStateTracker();
+        doReturn(mServiceState).when(mSST).getServiceState();
+        doReturn(Context.NOTIFICATION_SERVICE).when(mContext).getSystemServiceName(
+                NotificationManager.class);
+        doReturn(mMockNotificationManager).when(mContext).getSystemService(
+                Context.NOTIFICATION_SERVICE);
         mSatelliteControllerUT =
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
         verify(mMockSatelliteModemInterface).registerForSatelliteProvisionStateChanged(
@@ -623,10 +636,7 @@
                 mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        provisionSatelliteService();
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
                 SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
@@ -637,10 +647,7 @@
         assertEquals(mSatelliteNextVisibilityTime, mQueriedSatelliteVisibilityTime);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        provisionSatelliteService();
         setUpNullResponseForRequestTimeForNextSatelliteVisibility(
                 SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
@@ -651,10 +658,7 @@
                 mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        provisionSatelliteService();
         setUpNullResponseForRequestTimeForNextSatelliteVisibility(
                 SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
@@ -666,6 +670,55 @@
     }
 
     @Test
+    public void testRadioStateChanged() {
+        mIsSatelliteEnabledSemaphore.drainPermits();
+
+        when(mMockSatelliteModemInterface.isSatelliteServiceConnected()).thenReturn(false);
+        setRadioPower(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, never())
+                .requestIsSatelliteSupported(any(Message.class));
+
+        setRadioPower(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, never())
+                .requestIsSatelliteSupported(any(Message.class));
+
+        when(mMockSatelliteModemInterface.isSatelliteServiceConnected()).thenReturn(true);
+        setRadioPower(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(1))
+                .requestIsSatelliteSupported(any(Message.class));
+
+        setRadioPower(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(2))
+                .requestIsSatelliteSupported(any(Message.class));
+
+        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
+        setRadioPower(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(3))
+                .requestIsSatelliteSupported(any(Message.class));
+
+        setRadioPower(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(4))
+                .requestIsSatelliteSupported(any(Message.class));
+
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setRadioPower(false);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(5))
+                .requestIsSatelliteSupported(any(Message.class));
+
+        setRadioPower(true);
+        processAllMessages();
+        verify(mMockSatelliteModemInterface, times(5))
+                .requestIsSatelliteSupported(any(Message.class));
+    }
+
+    @Test
     public void testRequestSatelliteEnabled() {
         mIsSatelliteEnabledSemaphore.drainPermits();
 
@@ -721,7 +774,6 @@
         verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(true));
         verify(mMockSatelliteSessionController, times(2)).setDemoMode(eq(false));
         verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
-        verify(mMockPointingAppController).startPointingUI(eq(false));
         verify(mMockControllerMetricsStats, times(1)).onSatelliteEnabled();
         verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount();
 
@@ -780,7 +832,6 @@
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertEquals(SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
-        verify(mMockPointingAppController).startPointingUI(eq(false));
         verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(true));
         verify(mMockSatelliteSessionController, times(4)).setDemoMode(eq(false));
         verify(mMockDatagramController, times(4)).setDemoMode(eq(false));
@@ -1013,11 +1064,8 @@
                 (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
+        provisionSatelliteService();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
@@ -1030,11 +1078,8 @@
         verify(mMockPointingAppController).setStartedSatelliteTransmissionUpdates(eq(true));
 
         resetSatelliteControllerUT();
+        provisionSatelliteService();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
@@ -1094,10 +1139,7 @@
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        provisionSatelliteService();
         setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
@@ -1109,11 +1151,8 @@
         verify(mMockPointingAppController).stopSatelliteTransmissionUpdates(any(Message.class));
 
         resetSatelliteControllerUT();
+        provisionSatelliteService();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
@@ -1160,10 +1199,7 @@
 
         resetSatelliteControllerUT();
         boolean isDemoModeEnabled = mSatelliteControllerUT.isDemoModeEnabled();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        provisionSatelliteService();
         mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsDemoModeEnabledResultCode);
@@ -1172,12 +1208,21 @@
 
     @Test
     public void testIsSatelliteEnabled() {
-        assertFalse(mSatelliteControllerUT.isSatelliteEnabled());
         setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
+        assertFalse(mSatelliteControllerUT.isSatelliteEnabled());
         mIsSatelliteEnabledSemaphore.drainPermits();
         mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteEnabledResult(1));
+        assertEquals(
+                SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedIsSatelliteEnabledResultCode);
+
+
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsSatelliteEnabledResultCode);
         assertEquals(mSatelliteControllerUT.isSatelliteEnabled(), mQueriedIsSatelliteEnabled);
     }
 
@@ -1191,12 +1236,13 @@
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_SUCCESS);
 
+        setUpResponseForRequestIsSatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.onSatelliteServiceConnected();
         processAllMessages();
 
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -1229,13 +1275,13 @@
                 logd("onSatelliteModemStateChanged: state=" + state);
             }
         };
-        mSatelliteControllerUT.unregisterForSatelliteModemStateChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForModemStateChanged(SUB_ID, callback);
         verify(mMockSatelliteSessionController, never())
                 .unregisterForSatelliteModemStateChanged(callback);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
 
-        mSatelliteControllerUT.unregisterForSatelliteModemStateChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForModemStateChanged(SUB_ID, callback);
         verify(mMockSatelliteSessionController).unregisterForSatelliteModemStateChanged(callback);
     }
 
@@ -1297,7 +1343,7 @@
                 };
         when(mMockDatagramController.registerForSatelliteDatagram(eq(SUB_ID), eq(callback)))
                 .thenReturn(SATELLITE_RESULT_SUCCESS);
-        int errorCode = mSatelliteControllerUT.registerForSatelliteDatagram(SUB_ID, callback);
+        int errorCode = mSatelliteControllerUT.registerForIncomingDatagram(SUB_ID, callback);
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         verify(mMockDatagramController).registerForSatelliteDatagram(eq(SUB_ID), eq(callback));
     }
@@ -1315,7 +1361,7 @@
                 };
         doNothing().when(mMockDatagramController)
                 .unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback));
-        mSatelliteControllerUT.unregisterForSatelliteDatagram(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForIncomingDatagram(SUB_ID, callback);
         verify(mMockDatagramController).unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback));
     }
 
@@ -1325,7 +1371,7 @@
         SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
 
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
+        mSatelliteControllerUT.sendDatagram(SUB_ID,
                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1341,7 +1387,7 @@
         sendProvisionedStateChangedEvent(false, null);
         processAllMessages();
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
+        mSatelliteControllerUT.sendDatagram(SUB_ID,
                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1355,7 +1401,7 @@
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.sendSatelliteDatagram(SUB_ID,
+        mSatelliteControllerUT.sendDatagram(SUB_ID,
                 SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, datagram, true, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
@@ -1368,7 +1414,7 @@
     @Test
     public void testPollPendingSatelliteDatagrams() {
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
+        mSatelliteControllerUT.pollPendingDatagrams(SUB_ID, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
@@ -1381,7 +1427,7 @@
         sendProvisionedStateChangedEvent(false, null);
         processAllMessages();
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
+        mSatelliteControllerUT.pollPendingDatagrams(SUB_ID, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
@@ -1392,7 +1438,7 @@
         sendProvisionedStateChangedEvent(true, null);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.pollPendingSatelliteDatagrams(SUB_ID, mIIntegerConsumer);
+        mSatelliteControllerUT.pollPendingDatagrams(SUB_ID, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
         verify(mMockDatagramController, times(1)).pollPendingSatelliteDatagrams(anyInt(), any());
@@ -1429,20 +1475,6 @@
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
-                TEST_SATELLITE_TOKEN,
-                testProvisionData, mIIntegerConsumer);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
-        assertNull(cancelRemote);
-
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
@@ -1455,6 +1487,47 @@
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         assertNotNull(cancelRemote);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+
+        // Send provision request again after the device is successfully provisioned
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+                TEST_SATELLITE_TOKEN,
+                testProvisionData, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        assertNull(cancelRemote);
+
+        // Vendor service does not support the request requestIsSatelliteProvisioned. Telephony will
+        // make decision itself
+        resetSatelliteControllerUT();
+        mIIntegerConsumerResults.clear();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(
+                false, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+
+        // Vendor service does not support the requests requestIsSatelliteProvisioned and
+        // provisionSatelliteService. Telephony will make decision itself
+        deprovisionSatelliteService();
+        resetSatelliteControllerUT();
+        mIIntegerConsumerResults.clear();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(
+                false, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+                TEST_SATELLITE_TOKEN,
+                testProvisionData, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        assertNotNull(cancelRemote);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
@@ -1558,24 +1631,32 @@
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
+        provisionSatelliteService();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+
+        // Vendor service does not support deprovisionSatelliteService
+        resetSatelliteControllerUT();
+        provisionSatelliteService();
+        mIIntegerConsumerResults.clear();
+        setUpResponseForDeprovisionSatelliteService(
+                TEST_SATELLITE_TOKEN, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
+        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+                TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
 
         resetSatelliteControllerUT();
+        provisionSatelliteService();
         mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN,
                 SATELLITE_RESULT_INVALID_MODEM_STATE);
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
@@ -1583,12 +1664,13 @@
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
     public void testSupportedSatelliteServices() {
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false);
-        List<String> satellitePlmnList = mSatelliteControllerUT.getAllSatellitePlmnsForCarrier(
+        List<String> satellitePlmnList = mSatelliteControllerUT.getSatellitePlmnsForCarrier(
                 SUB_ID);
         assertEquals(EMPTY_STRING_ARRAY.length, satellitePlmnList.size());
         List<Integer> supportedSatelliteServices =
@@ -1612,7 +1694,7 @@
         TestSatelliteController testSatelliteController =
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
 
-        satellitePlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        satellitePlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         assertTrue(satellitePlmnList.isEmpty());
         supportedSatelliteServices =
                 testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00101");
@@ -1644,7 +1726,7 @@
         }
         processAllMessages();
 
-        satellitePlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        satellitePlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         assertTrue(Arrays.equals(
                 expectedSupportedSatellitePlmns, satellitePlmnList.stream().toArray()));
         supportedSatelliteServices =
@@ -1671,7 +1753,7 @@
         }
         processAllMessages();
 
-        satellitePlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        satellitePlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         assertTrue(satellitePlmnList.isEmpty());
         supportedSatelliteServices =
                 testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
@@ -1716,7 +1798,7 @@
         TestSatelliteController testSatelliteController =
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
         processAllMessages();
-        List<String> carrierPlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(
+        List<String> carrierPlmnList = testSatelliteController.getSatellitePlmnsForCarrier(
                 SUB_ID);
         verify(mMockSatelliteModemInterface, never()).setSatellitePlmn(
                 anyInt(), anyList(), anyList(), any(Message.class));
@@ -1744,7 +1826,7 @@
             );
         }
         processAllMessages();
-        carrierPlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        carrierPlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         verify(mMockSatelliteModemInterface, never()).setSatellitePlmn(
                 anyInt(), anyList(), anyList(), any(Message.class));
         assertTrue(carrierPlmnList.isEmpty());
@@ -1770,7 +1852,7 @@
         }
         processAllMessages();
 
-        carrierPlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        carrierPlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         assertTrue(carrierPlmnList.isEmpty());
         List<String> allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
                 carrierPlmnList, satellitePlmnListFromOverlayConfig);
@@ -1790,7 +1872,7 @@
             );
         }
         processAllMessages();
-        carrierPlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        carrierPlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
                 carrierPlmnList, satellitePlmnListFromOverlayConfig);
         assertEquals(expectedCarrierPlmnList, carrierPlmnList);
@@ -1829,7 +1911,7 @@
             );
         }
         processAllMessages();
-        carrierPlmnList = testSatelliteController.getAllSatellitePlmnsForCarrier(SUB_ID);
+        carrierPlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         assertTrue(carrierPlmnList.isEmpty());
         verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
                 eq(EMPTY_STRING_LIST), eq(EMPTY_STRING_LIST), any(Message.class));
@@ -1855,9 +1937,9 @@
         setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.removeAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
-        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.removeAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(2));
@@ -1866,7 +1948,7 @@
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(1));
 
         Set<Integer> restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(!restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
         assertTrue(!restrictionSet.contains(
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION));
@@ -1876,7 +1958,7 @@
         reset(mMockSatelliteModemInterface);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.addAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
         processAllMessages();
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -1884,7 +1966,7 @@
                 .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
         assertTrue(waitForIIntegerConsumerResult(1));
         restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
 
         // remove satellite restriction reason by user
@@ -1892,13 +1974,13 @@
         reset(mMockSatelliteModemInterface);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.removeAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(!restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
         verify(mMockSatelliteModemInterface, times(1))
                 .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
@@ -1908,13 +1990,13 @@
         reset(mMockSatelliteModemInterface);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.addAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
         verify(mMockSatelliteModemInterface, times(1))
                 .requestSetSatelliteEnabledForCarrier(anyInt(), eq(false), any(Message.class));
@@ -1923,14 +2005,14 @@
         mIIntegerConsumerResults.clear();
         reset(mMockSatelliteModemInterface);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.addAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION, mIIntegerConsumer);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION));
         verify(mMockSatelliteModemInterface, never())
                 .requestSetSatelliteEnabledForCarrier(anyInt(), anyBoolean(), any(Message.class));
@@ -1939,14 +2021,14 @@
         mIIntegerConsumerResults.clear();
         reset(mMockSatelliteModemInterface);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.removeAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION, mIIntegerConsumer);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(!restrictionSet.contains(
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION));
         verify(mMockSatelliteModemInterface, never())
@@ -1956,14 +2038,14 @@
         mIIntegerConsumerResults.clear();
         reset(mMockSatelliteModemInterface);
         setUpResponseForRequestSetSatelliteEnabledForCarrier(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.removeAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         restrictionSet =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(!restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER));
         verify(mMockSatelliteModemInterface, times(1))
                 .requestSetSatelliteEnabledForCarrier(anyInt(), eq(true), any(Message.class));
@@ -1972,7 +2054,7 @@
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false);
 
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.removeSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.removeAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1981,7 +2063,7 @@
         verifyZeroInteractions(mMockSatelliteModemInterface);
 
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.addSatelliteAttachRestrictionForCarrier(SUB_ID,
+        mSatelliteControllerUT.addAttachRestrictionForCarrier(SUB_ID,
                 SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1990,7 +2072,7 @@
         verifyZeroInteractions(mMockSatelliteModemInterface);
 
         Set<Integer> satelliteRestrictionReasons =
-                mSatelliteControllerUT.getSatelliteAttachRestrictionReasonsForCarrier(SUB_ID);
+                mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
         assertTrue(satelliteRestrictionReasons.isEmpty());
     }
 
@@ -2086,9 +2168,7 @@
         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);
+        provisionSatelliteService();
 
         doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
@@ -2100,8 +2180,7 @@
         verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        provisionSatelliteService();
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         // reset cache to NTN_SIGNAL_STRENGTH_NONE
         sendNtnSignalStrengthChangedEvent(NTN_SIGNAL_STRENGTH_NONE, null);
@@ -2176,10 +2255,8 @@
         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);
+        provisionSatelliteService();
         verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
                 SATELLITE_RESULT_SUCCESS);
         verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
@@ -2269,10 +2346,7 @@
         // 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);
+        provisionSatelliteService();
         setUpResponseForStartSendingNtnSignalStrength(expectedResult);
         sendCmdStartSendingNtnSignalStrengthChangedEvent(true);
         processAllMessages();
@@ -2466,23 +2540,20 @@
                     }
                 };
 
-        int errorCode = mSatelliteControllerUT.registerForSatelliteCapabilitiesChanged(SUB_ID,
+        int errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
                 callback);
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
 
         setUpResponseForRequestIsSatelliteSupported(false,
                 SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForSatelliteCapabilitiesChanged(SUB_ID,
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
                 callback);
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
 
         resetSatelliteControllerUT();
-        setUpResponseForRequestIsSatelliteProvisioned(true,
-                SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForSatelliteCapabilitiesChanged(SUB_ID,
+        provisionSatelliteService();
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
                 callback);
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         SatelliteCapabilities expectedCapabilities = mSatelliteCapabilities;
@@ -2499,7 +2570,7 @@
                 semaphore, 1, "testRegisterForSatelliteCapabilitiesChanged"));
         assertTrue(expectedCapabilities.equals(satelliteCapabilities[0]));
 
-        mSatelliteControllerUT.unregisterForSatelliteCapabilitiesChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForCapabilitiesChanged(SUB_ID, callback);
         expectedCapabilities = mSatelliteCapabilities;
         sendSatelliteCapabilitiesChangedEvent(expectedCapabilities, null);
         processAllMessages();
@@ -2528,21 +2599,21 @@
                     }
                 };
 
-        int errorCode = mSatelliteControllerUT.registerForSatelliteCapabilitiesChanged(SUB_ID,
+        int errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
                 callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
 
         setUpResponseForRequestIsSatelliteSupported(false,
                 SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
-        errorCode = mSatelliteControllerUT.registerForSatelliteCapabilitiesChanged(SUB_ID,
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
                 callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
 
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
-        errorCode = mSatelliteControllerUT.registerForSatelliteCapabilitiesChanged(SUB_ID,
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
                 callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
 
@@ -2555,12 +2626,14 @@
 
     @Test
     public void testSatelliteCommunicationRestrictionForEntitlement() throws Exception {
+        logd("testSatelliteCommunicationRestrictionForEntitlement");
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
 
         mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
                 true);
-        replaceInstance(SatelliteController.class, "mCarrierSatelliteEnabled",
-                mSatelliteControllerUT, false);
+        SparseBooleanArray satelliteEnabledPerCarrier = new SparseBooleanArray();
+        replaceInstance(SatelliteController.class, "mSatelliteEntitlementStatusPerCarrier",
+                mSatelliteControllerUT, satelliteEnabledPerCarrier);
 
         mIIntegerConsumerResults.clear();
         reset(mMockSatelliteModemInterface);
@@ -2575,7 +2648,7 @@
 
         // Verify call the requestSetSatelliteEnabledForCarrier to enable the satellite when
         // satellite service is enabled by entitlement server.
-        mSatelliteControllerUT.updateSatelliteEntitlementStatus(SUB_ID, true, new ArrayList<>(),
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true, new ArrayList<>(),
                 mIIntegerConsumer);
         processAllMessages();
 
@@ -2595,7 +2668,7 @@
         doReturn(mIsSatelliteServiceSupported)
                 .when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.updateSatelliteEntitlementStatus(SUB_ID, false, new ArrayList<>(),
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, new ArrayList<>(),
                 mIIntegerConsumer);
         processAllMessages();
 
@@ -2605,6 +2678,236 @@
                 .requestSetSatelliteEnabledForCarrier(anyInt(), eq(false), any(Message.class));
     }
 
+    @Test
+    public void testPassSatellitePlmnToModemAfterUpdateSatelliteEntitlementStatus()
+            throws Exception {
+        logd("testPassSatellitePlmnToModemAfterUpdateSatelliteEntitlementStatus");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        List<String> overlayConfigPlmnList =  new ArrayList<>();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+
+        // If the PlmnListPerCarrier and the overlay config plmn list are empty verify passing to
+        // the modem.
+        List<String> entitlementPlmnList = new ArrayList<>();
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
+                entitlementPlmnList, mIIntegerConsumer);
+
+        List<String> plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(
+                SUB_ID);
+        List<String> allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
+                plmnListPerCarrier, overlayConfigPlmnList);
+
+        assertEquals(new ArrayList<>(), plmnListPerCarrier);
+        assertEquals(new ArrayList<>(), allSatellitePlmnList);
+        verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
+                eq(plmnListPerCarrier), eq(allSatellitePlmnList), any(Message.class));
+
+        // If the PlmnListPerCarrier and the overlay config plmn list are exist verify passing
+        // the modem.
+        entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "00103"}).toList();
+        overlayConfigPlmnList =
+                Arrays.stream(new String[]{"00101", "00102", "00104"}).toList();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true,
+                entitlementPlmnList, mIIntegerConsumer);
+
+        plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
+        allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
+                plmnListPerCarrier, overlayConfigPlmnList);
+
+        assertEquals(entitlementPlmnList, plmnListPerCarrier);
+        verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
+                eq(plmnListPerCarrier), eq(allSatellitePlmnList), any(Message.class));
+
+        // If the PlmnListPerCarrier and the overlay config plmn list are exist verify passing
+        // the modem.
+        reset(mMockSatelliteModemInterface);
+        entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "00103"}).toList();
+        Map<Integer, Map<String, Set<Integer>>>
+                satelliteServicesSupportedByCarriers = new HashMap<>();
+        List<String> carrierConfigPlmnList = Arrays.stream(new String[]{"00105", "00106"}).toList();
+        Map<String, Set<Integer>> plmnAndService = new HashMap<>();
+        plmnAndService.put(carrierConfigPlmnList.get(0), new HashSet<>(Arrays.asList(3, 5)));
+        plmnAndService.put(carrierConfigPlmnList.get(1), new HashSet<>(Arrays.asList(3)));
+        satelliteServicesSupportedByCarriers.put(SUB_ID, plmnAndService);
+        replaceInstance(SatelliteController.class, "mSatelliteServicesSupportedByCarriers",
+                mSatelliteControllerUT, satelliteServicesSupportedByCarriers);
+        overlayConfigPlmnList = Arrays.stream(new String[]{"00101", "00102", "00104"}).toList();
+        replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig",
+                mSatelliteControllerUT, overlayConfigPlmnList);
+        List<String> mergedPlmnList = entitlementPlmnList;
+
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true,
+                entitlementPlmnList, mIIntegerConsumer);
+
+        plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
+        allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
+                plmnListPerCarrier, overlayConfigPlmnList);
+
+        assertEquals(mergedPlmnList, plmnListPerCarrier);
+        verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
+                eq(plmnListPerCarrier), eq(allSatellitePlmnList), any(Message.class));
+    }
+
+    @Test
+    public void testUpdatePlmnListPerCarrier() throws Exception {
+        logd("testUpdatePlmnListPerCarrier");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier",
+                mSatelliteControllerUT, new SparseArray<>());
+        replaceInstance(SatelliteController.class, "mSatelliteServicesSupportedByCarriers",
+                mSatelliteControllerUT, new HashMap<>());
+        SparseArray<List<String>> entitlementPlmnListPerCarrier = new SparseArray<>();
+        replaceInstance(SatelliteController.class, "mEntitlementPlmnListPerCarrier",
+                mSatelliteControllerUT, entitlementPlmnListPerCarrier);
+
+        // If the carrier config and the entitlement plmn list are empty, verify whether an empty
+        // list is returned.
+        mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                new PersistableBundle());
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+
+        List<String> plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(
+                SUB_ID);
+        assertEquals(new ArrayList<>(), plmnListPerCarrier);
+
+        // If the carrier config list is empty and the entitlement plmn list is exists, verify
+        // whether the entitlement list is returned.
+        entitlementPlmnListPerCarrier.clear();
+        List<String> entitlementPlmnList = Arrays.asList("00101", "00102", "00104");
+        entitlementPlmnListPerCarrier.put(SUB_ID, entitlementPlmnList);
+        List<String> expectedPlmnListPerCarrier = entitlementPlmnList;
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+
+        plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
+        assertEquals(expectedPlmnListPerCarrier, plmnListPerCarrier);
+
+        // If the carrier config list is exists and the entitlement plmn list is empty, verify
+        // whether the carrier config list is returned.
+        entitlementPlmnListPerCarrier.clear();
+        entitlementPlmnList = new ArrayList<>();
+        entitlementPlmnListPerCarrier.put(SUB_ID, entitlementPlmnList);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+        PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
+        List<String> carrierConfigPlmnList = Arrays.asList("00102", "00103", "00105");
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                carrierConfigPlmnList.get(0), new int[]{2});
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                carrierConfigPlmnList.get(1), new int[]{1, 3});
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                carrierConfigPlmnList.get(2), new int[]{2});
+        mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                carrierSupportedSatelliteServicesPerProvider);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+
+        expectedPlmnListPerCarrier = carrierConfigPlmnList;
+        plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
+        assertEquals(expectedPlmnListPerCarrier.stream().sorted().toList(),
+                plmnListPerCarrier.stream().sorted().toList());
+
+
+        // If the carrier config and the entitlement plmn list are exist, verify whether the
+        // entitlement list is returned.
+        entitlementPlmnList = Arrays.asList("00101", "00102", "00104");
+        entitlementPlmnListPerCarrier.put(SUB_ID, entitlementPlmnList);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        processAllMessages();
+
+        expectedPlmnListPerCarrier = entitlementPlmnList;
+        plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(
+                SUB_ID);
+        assertEquals(expectedPlmnListPerCarrier.stream().sorted().toList(),
+                plmnListPerCarrier.stream().sorted().toList());
+    }
+
+    @Test
+    public void testEntitlementStatus() throws Exception {
+        logd("testEntitlementStatus");
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        SparseBooleanArray satelliteEnabledPerCarrier = new SparseBooleanArray();
+        replaceInstance(SatelliteController.class, "mSatelliteEntitlementStatusPerCarrier",
+                mSatelliteControllerUT, satelliteEnabledPerCarrier);
+
+        // Change SUB_ID's EntitlementStatus to true
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true, new ArrayList<>(),
+                mIIntegerConsumer);
+
+        assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID));
+        assertEquals(false, satelliteEnabledPerCarrier.get(SUB_ID1));
+
+        // Change SUB_ID1's EntitlementStatus to true
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID1, true, new ArrayList<>(),
+                mIIntegerConsumer);
+
+        assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID));
+        assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1));
+
+        // Change SUB_ID's EntitlementStatus to false
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, new ArrayList<>(),
+                mIIntegerConsumer);
+
+        assertEquals(false, satelliteEnabledPerCarrier.get(SUB_ID));
+        assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1));
+    }
+
+    @Test
+    public void testHandleEventServiceStateChanged() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        // Do nothing when the satellite is not connected
+        doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork();
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        assertEquals(false,
+                mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+        verify(mMockNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(), any());
+
+        // Check sending a system notification when the satellite is connected
+        doReturn(true).when(mServiceState).isUsingNonTerrestrialNetwork();
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
+                any());
+        assertEquals(true,
+                mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+
+        // Check don't display again after displayed already a system notification.
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
+                any());
+    }
+
     private void resetSatelliteControllerUTEnabledState() {
         logd("resetSatelliteControllerUTEnabledState");
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
@@ -2670,7 +2973,8 @@
                 ? null : new SatelliteException(error);
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
-            AsyncResult.forMessage(message, isSatelliteEnabled, exception);
+            int[] enabled = new int[] {isSatelliteEnabled ? 1 : 0};
+            AsyncResult.forMessage(message, enabled, exception);
             message.sendToTarget();
             return null;
         }).when(mMockSatelliteModemInterface).requestIsSatelliteEnabled(any(Message.class));
@@ -2943,7 +3247,7 @@
             try {
                 if (!mSatelliteAllowedSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
                     loge("Timeout to receive "
-                            + "requestIsSatelliteCommunicationAllowedForCurrentLocation()"
+                            + "requestIsCommunicationAllowedForCurrentLocation()"
                             + " callback");
                     return false;
                 }
@@ -3178,13 +3482,45 @@
                 throw new AssertionError();
             }
         } else {
-            ServiceSpecificException ex = assertThrows(ServiceSpecificException.class,
+            RemoteException ex = assertThrows(RemoteException.class,
                     () -> mSatelliteControllerUT.registerForNtnSignalStrengthChanged(subId,
                             callback));
-            assertEquals(expectedError, ex.errorCode);
+            assertTrue("The cause is not IllegalStateException",
+                    ex.getCause() instanceof IllegalStateException);
         }
     }
 
+    private void provisionSatelliteService() {
+        String mText = "This is test provision data.";
+        byte[] testProvisionData = mText.getBytes();
+        ICancellationSignal cancelRemote;
+        mIIntegerConsumerResults.clear();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
+                SATELLITE_RESULT_SUCCESS);
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+                TEST_SATELLITE_TOKEN,
+                testProvisionData, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        assertNotNull(cancelRemote);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+    }
+
+    private void deprovisionSatelliteService() {
+        mIIntegerConsumerResults.clear();
+        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+                TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+    }
+
     private static void loge(String message) {
         Rlog.e(TAG, message);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index 6d3bb4e..a1c2cfc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
@@ -571,6 +572,35 @@
                 SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         clearInvocations(mMockDatagramController);
 
+        // Start receiving datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to TRANSFERRING state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        clearInvocations(mMockDatagramController);
+
+        // Receiving datagrams is successful and done.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
         // Wait for timeout
         moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
index 21678d4..8841c7a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
@@ -16,15 +16,28 @@
 
 package com.android.internal.telephony.security;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static junit.framework.Assert.assertEquals;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
 import android.telephony.CellularIdentifierDisclosure;
 
 import com.android.internal.telephony.TestExecutorService;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.InOrder;
 
 import java.util.concurrent.TimeUnit;
 
@@ -33,7 +46,12 @@
     // 15 minutes and 100 milliseconds. Can be used to advance time in a test executor far enough
     // to (hopefully, if the code is behaving) close a disclosure window.
     private static final long WINDOW_CLOSE_ADVANCE_MILLIS = (15 * 60 * 1000) + 100;
+    private static final int SUB_ID_1 = 1;
+    private static final int SUB_ID_2 = 2;
     private CellularIdentifierDisclosure mDislosure;
+    private CellularNetworkSecuritySafetySource mSafetySource;
+    private Context mContext;
+    private InOrder mInOrder;
 
     @Before
     public void setUp() {
@@ -43,33 +61,41 @@
                         CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
                         "001001",
                         false);
+        mSafetySource = mock(CellularNetworkSecuritySafetySource.class);
+        mInOrder = inOrder(mSafetySource);
     }
 
     @Test
     public void testInitializeDisabled() {
         TestExecutorService executor = new TestExecutorService();
         CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
 
         assertFalse(notifier.isEnabled());
+        verify(mSafetySource, never()).setIdentifierDisclosureIssueEnabled(any(), anyBoolean());
     }
 
     @Test
     public void testDisableAddDisclosureNop() {
         TestExecutorService executor = new TestExecutorService();
         CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
 
         assertFalse(notifier.isEnabled());
-        notifier.addDisclosure(mDislosure);
-        assertEquals(0, notifier.getCurrentDisclosureCount());
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        verify(mSafetySource, never())
+                .setIdentifierDisclosure(any(), anyInt(), anyInt(), any(), any());
     }
 
     @Test
     public void testAddDisclosureEmergencyNop() {
         TestExecutorService executor = new TestExecutorService();
         CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
         CellularIdentifierDisclosure emergencyDisclosure =
                 new CellularIdentifierDisclosure(
                         CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
@@ -77,64 +103,163 @@
                         "001001",
                         true);
 
-        notifier.enable();
-        notifier.addDisclosure(emergencyDisclosure);
+        notifier.enable(mContext);
+        notifier.addDisclosure(mContext, SUB_ID_1, emergencyDisclosure);
 
-        assertEquals(0, notifier.getCurrentDisclosureCount());
+        assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        verify(mSafetySource, never())
+                .setIdentifierDisclosure(any(), anyInt(), anyInt(), any(), any());
     }
 
     @Test
     public void testAddDisclosureCountIncrements() {
         TestExecutorService executor = new TestExecutorService();
         CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
 
-        notifier.enable();
-        notifier.addDisclosure(mDislosure);
-        notifier.addDisclosure(mDislosure);
-        notifier.addDisclosure(mDislosure);
+        notifier.enable(mContext);
 
-        assertEquals(3, notifier.getCurrentDisclosureCount());
+        for (int i = 0; i < 3; i++) {
+            notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        }
+
+        assertEquals(3, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(3), any(), any());
+    }
+
+    @Test
+    public void testSingleDisclosureStartAndEndTimesAreEqual() {
+        TestExecutorService executor = new TestExecutorService();
+        CellularIdentifierDisclosureNotifier notifier =
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+
+        notifier.enable(mContext);
+
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+
+        assertEquals(1, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        assertTrue(notifier.getFirstOpen(SUB_ID_1).equals(notifier.getCurrentEnd(SUB_ID_1)));
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+    }
+
+    @Test
+    public void testMultipleDisclosuresTimeWindows() {
+        TestExecutorService executor = new TestExecutorService();
+        CellularIdentifierDisclosureNotifier notifier =
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+
+        notifier.enable(mContext);
+
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        try {
+            Thread.sleep(50);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+
+        assertEquals(2, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        assertTrue(notifier.getFirstOpen(SUB_ID_1).isBefore(notifier.getCurrentEnd(SUB_ID_1)));
+        verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
     }
 
     @Test
     public void testAddDisclosureThenWindowClose() {
         TestExecutorService executor = new TestExecutorService();
         CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
 
         // One round of disclosures
-        notifier.enable();
-        notifier.addDisclosure(mDislosure);
-        notifier.addDisclosure(mDislosure);
-        assertEquals(2, notifier.getCurrentDisclosureCount());
+        notifier.enable(mContext);
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        assertEquals(2, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
 
         // Window close should reset the counter
         executor.advanceTime(WINDOW_CLOSE_ADVANCE_MILLIS);
-        assertEquals(0, notifier.getCurrentDisclosureCount());
+        assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
 
         // A new disclosure should increment as normal
-        notifier.addDisclosure(mDislosure);
-        assertEquals(1, notifier.getCurrentDisclosureCount());
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        assertEquals(1, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
     }
 
     @Test
     public void testDisableClosesWindow() {
         TestExecutorService executor = new TestExecutorService();
         CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
 
         // One round of disclosures
-        notifier.enable();
-        notifier.addDisclosure(mDislosure);
-        notifier.addDisclosure(mDislosure);
-        assertEquals(2, notifier.getCurrentDisclosureCount());
+        notifier.enable(mContext);
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        assertEquals(2, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
 
-        notifier.disable();
+        notifier.disable(mContext);
         assertFalse(notifier.isEnabled());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosureIssueEnabled(any(), eq(false));
 
         // We're disabled now so no disclosures should open the disclosure window
-        notifier.addDisclosure(mDislosure);
-        assertEquals(0, notifier.getCurrentDisclosureCount());
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        mInOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testMultipleSubIdsTrackedIndependently() {
+        TestExecutorService executor = new TestExecutorService();
+        CellularIdentifierDisclosureNotifier notifier =
+                new CellularIdentifierDisclosureNotifier(
+                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+
+        notifier.enable(mContext);
+        for (int i = 0; i < 3; i++) {
+            notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+        }
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(3), any(), any());
+
+        for (int i = 0; i < 4; i++) {
+            notifier.addDisclosure(mContext, SUB_ID_2, mDislosure);
+        }
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(1), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(2), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(3), any(), any());
+        mInOrder.verify(mSafetySource, times(1))
+                .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(4), any(), any());
+
+        assertEquals(3, notifier.getCurrentDisclosureCount(SUB_ID_1));
+        assertEquals(4, notifier.getCurrentDisclosureCount(SUB_ID_2));
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
new file mode 100644
index 0000000..169a57c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.security;
+
+import static com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.NULL_CIPHER_STATE_ENCRYPTED;
+import static com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.NULL_CIPHER_STATE_NOTIFY_ENCRYPTED;
+import static com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.safetycenter.SafetySourceData;
+import android.util.Singleton;
+
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.TestApplication;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.SafetyCenterManagerWrapper;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.time.Instant;
+
+public final class CellularNetworkSecuritySafetySourceTest extends TelephonyTest {
+
+    private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+
+        // unmock ActivityManager to be able to register receiver, create real PendingIntents.
+        restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
+        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
+
+        SubscriptionInfoInternal info0 = new SubscriptionInfoInternal.Builder()
+                .setId(0)
+                .setDisplayName("fake_name0")
+                .build();
+        doReturn(info0).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(0));
+        SubscriptionInfoInternal info1 = new SubscriptionInfoInternal.Builder()
+                .setId(1)
+                .setDisplayName("fake_name1")
+                .build();
+        doReturn(info1).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(1));
+
+        mContextFixture.putResource(R.string.scCellularNetworkSecurityTitle, "fake");
+        mContextFixture.putResource(R.string.scCellularNetworkSecuritySummary, "fake");
+        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedTitle, "fake %1$s");
+        mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedSummary, "fake");
+        mContextFixture.putResource(R.string.scNullCipherIssueEncryptedTitle, "fake %1$s");
+        mContextFixture.putResource(R.string.scNullCipherIssueEncryptedSummary, "fake");
+        mContextFixture.putResource(R.string.scIdentifierDisclosureIssueTitle, "fake");
+        mContextFixture.putResource(
+                R.string.scIdentifierDisclosureIssueSummary, "fake %1$d %2$tr %3$tr %4$s");
+        mContextFixture.putResource(R.string.scNullCipherIssueActionSettings, "fake");
+        mContextFixture.putResource(R.string.scNullCipherIssueActionLearnMore, "fake");
+
+        mSafetyCenterManagerWrapper = mock(SafetyCenterManagerWrapper.class);
+        doAnswer(inv -> getActivityPendingIntent(inv.getArgument(1)))
+                .when(mSafetyCenterManagerWrapper)
+                .getActivityPendingIntent(any(Context.class), any(Intent.class));
+
+        mSafetySource = new CellularNetworkSecuritySafetySource(mSafetyCenterManagerWrapper);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private PendingIntent getActivityPendingIntent(Intent intent) {
+        Context context = TestApplication.getAppContext();
+        return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    @Test
+    public void disableNullCipherIssue_nullData() {
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, false);
+
+        verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(isNull());
+    }
+
+    @Test
+    public void enableNullCipherIssue_statusWithoutIssues() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+
+        verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(data.capture());
+        assertThat(data.getValue().getStatus()).isNotNull();
+        assertThat(data.getValue().getIssues()).isEmpty();
+    }
+
+    @Test
+    public void setNullCipherState_encrypted_statusWithoutIssue() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_ENCRYPTED);
+
+        verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(1).getIssues()).isEmpty();
+    }
+
+    @Test
+    public void setNullCipherState_notifyEncrypted_statusWithIssue() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_ENCRYPTED);
+
+        verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+    }
+
+    @Test
+    public void setNullCipherState_notifyNonEncrypted_statusWithIssue() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+
+        verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+    }
+
+    @Test
+    public void setNullCipherState_multipleNonEncrypted_statusWithTwoIssues() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+        mSafetySource.setNullCipherState(mContext, 1, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+
+        verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(2).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(2).getIssues()).hasSize(2);
+    }
+
+    @Test
+    public void disableIdentifierDisclosueIssue_nullData() {
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, false);
+
+        verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(isNull());
+    }
+
+    @Test
+    public void enableIdentifierDisclosueIssue_statusWithoutIssues() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+
+        verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(data.capture());
+        assertThat(data.getValue().getStatus()).isNotNull();
+        assertThat(data.getValue().getIssues()).isEmpty();
+    }
+
+    @Test
+    public void setIdentifierDisclosure_singleDisclosure_statusWithIssue() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+        mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+
+        verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+    }
+
+    @Test
+    public void setIdentifierDisclosure_multipleDisclosures_statusWithTwoIssues() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+        mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+        mSafetySource.setIdentifierDisclosure(mContext, 1, 3, Instant.now(), Instant.now());
+
+        verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(2).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(2).getIssues()).hasSize(2);
+    }
+
+    @Test
+    public void multipleIssuesKinds_statusWithTwoIssues() {
+        ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+        mSafetySource.setNullCipherIssueEnabled(mContext, true);
+        mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+        mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+        mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+
+        verify(mSafetyCenterManagerWrapper, times(4)).setSafetySourceData(data.capture());
+        assertThat(data.getAllValues().get(3).getStatus()).isNotNull();
+        assertThat(data.getAllValues().get(3).getIssues()).hasSize(2);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
index 3697cbe..0aeaad3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -63,7 +63,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -79,7 +78,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Telephony;
@@ -1328,9 +1326,17 @@
         // Test getActiveSubIdList, System
         assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false/*visible only*/))
                 .isEqualTo(new int[]{subId1, subId2});
-        // Test get getActiveSubInfoCount
+        // Test get getActiveSubInfoCount - forAllProfiles: false
         assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
                 CALLING_PACKAGE, CALLING_FEATURE, false)).isEqualTo(1);
+        // Test get getActiveSubInfoCount - forAllProfiles: true
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionManagerServiceUT.getActiveSubInfoCount(
+                        CALLING_PACKAGE, CALLING_FEATURE, true));
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
+        assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isEqualTo(2);
+        mContextFixture.removeCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
         // Test getActiveSubscriptionInfo
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfo(
                 subId1, CALLING_PACKAGE, CALLING_FEATURE).getSubscriptionId()).isEqualTo(subId1);
@@ -1350,11 +1356,21 @@
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoForSimSlotIndex(
                 1, CALLING_PACKAGE, CALLING_FEATURE).getSubscriptionId())
                 .isEqualTo(subId2);
-        // Test getActiveSubscriptionInfoList
+        // Test getActiveSubscriptionInfoList - forAllProfiles: false
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
                 CALLING_PACKAGE, CALLING_FEATURE, false)
                 .stream().map(SubscriptionInfo::getSubscriptionId)
                 .toList()).isEqualTo(List.of(subId1));
+        // Test getActiveSubscriptionInfoList - forAllProfiles: true
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+                        CALLING_PACKAGE, CALLING_FEATURE, true));
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
+        assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+                        CALLING_PACKAGE, CALLING_FEATURE, true)
+                .stream().map(SubscriptionInfo::getSubscriptionId)
+                .toList()).isEqualTo(List.of(subId1, subId2));
+        mContextFixture.removeCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
         // Test getAllSubInfoList
         assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList(CALLING_PACKAGE,
                 CALLING_FEATURE).stream().map(SubscriptionInfo::getSubscriptionId).toList())
@@ -1446,9 +1462,17 @@
         // Test getActiveSubIdList, System
         assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false/*visible only*/))
                 .isEqualTo(new int[]{subId1, subId2});
-        // Test get getActiveSubInfoCount
+        // Test get getActiveSubInfoCount- forAllProfiles: false
         assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
                 CALLING_PACKAGE, CALLING_FEATURE, false)).isEqualTo(1);
+        // Test get getActiveSubInfoCount - forAllProfiles: true
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionManagerServiceUT.getActiveSubInfoCount(
+                        CALLING_PACKAGE, CALLING_FEATURE, true));
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
+        assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isEqualTo(2);
+        mContextFixture.removeCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
         // Test getActiveSubscriptionInfo
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfo(
                 subId1, CALLING_PACKAGE, CALLING_FEATURE).getSubscriptionId()).isEqualTo(subId1);
@@ -1468,11 +1492,21 @@
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoForSimSlotIndex(
                 1, CALLING_PACKAGE, CALLING_FEATURE).getSubscriptionId())
                 .isEqualTo(subId2);
-        // Test getActiveSubscriptionInfoList
+        // Test getActiveSubscriptionInfoList - forAllProfiles: false
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
                         CALLING_PACKAGE, CALLING_FEATURE, false).stream()
                 .map(SubscriptionInfo::getSubscriptionId)
                 .toList()).isEqualTo(List.of(subId1));
+        // Test getActiveSubscriptionInfoList - forAllProfiles: true
+        assertThrows(SecurityException.class,
+                () -> mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+                        CALLING_PACKAGE, CALLING_FEATURE, true));
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
+        assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
+                        CALLING_PACKAGE, CALLING_FEATURE, true)
+                .stream().map(SubscriptionInfo::getSubscriptionId)
+                .toList()).isEqualTo(List.of(subId1, subId2));
+        mContextFixture.removeCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_PROFILES);
         // Test getAllSubInfoList
         assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList(CALLING_PACKAGE,
                 CALLING_FEATURE).stream().map(SubscriptionInfo::getSubscriptionId).toList())