Merge "Update to check if the s2cellfile is null" into main
diff --git a/flags/calling.aconfig b/flags/calling.aconfig
index e67ebc6..f8de8b2 100644
--- a/flags/calling.aconfig
+++ b/flags/calling.aconfig
@@ -5,6 +5,7 @@
   namespace: "telephony"
   description: "APIs that are used to notify simultaneous calling changes to other applications."
   bug: "297446980"
+  is_exported: true
 }
 
 flag {
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index d09259e..b2cc904 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -62,3 +62,10 @@
     description: "This flag updates roaming state to set wfc mode"
     bug:"317298331"
 }
+
+flag {
+    name: "enable_sip_subscribe_retry"
+    namespace: "telephony"
+    description: "This flag controls whether framework supports SIP subscribe retry or not"
+    bug:"297023230"
+}
diff --git a/flags/telephony.aconfig b/flags/telephony.aconfig
index d59b249..9ef70b1 100644
--- a/flags/telephony.aconfig
+++ b/flags/telephony.aconfig
@@ -26,4 +26,18 @@
     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
+}
+
+flag {
+    name: "minimal_telephony_cdm_check"
+    namespace: "telephony"
+    description: "This flag disables Calling/Data/Messaging features if their respective feature is missing"
+    bug: "310710841"
+}
+
+flag {
+    name: "minimal_telephony_managers_conditional_on_features"
+    namespace: "telephony"
+    description: "This flag enables checking for telephony features before initializing corresponding managers"
+    bug: "310710841"
+}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 5517bc6..9113514 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Bundle;
@@ -159,6 +160,12 @@
     public GsmCdmaCallTracker(GsmCdmaPhone phone, FeatureFlags featureFlags) {
         super(featureFlags);
 
+        if (mFeatureFlags.minimalTelephonyCdmCheck()
+                && !phone.getContext().getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_TELEPHONY_CALLING)) {
+            throw new UnsupportedOperationException("GsmCdmaCallTracker requires calling");
+        }
+
         this.mPhone = phone;
         mCi = phone.mCi;
         mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
@@ -1492,7 +1499,7 @@
 
         switch (msg.what) {
             case EVENT_POLL_CALLS_RESULT:
-                Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
+                if (DBG_POLL) Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
 
                 if (msg == mLastRelevantPoll) {
                     if (DBG_POLL) log(
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 911eee5..de7ebd6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -42,6 +42,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.database.SQLException;
 import android.hardware.radio.modem.ImeiInfo;
 import android.net.Uri;
@@ -370,9 +371,11 @@
                 SignalStrengthController.class.getName()).makeSignalStrengthController(this);
         mSST = mTelephonyComponentFactory.inject(ServiceStateTracker.class.getName())
                 .makeServiceStateTracker(this, this.mCi, featureFlags);
-        mEmergencyNumberTracker = mTelephonyComponentFactory
-                .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
-                        this, this.mCi);
+        if (hasCalling()) {
+            mEmergencyNumberTracker = mTelephonyComponentFactory
+                    .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
+                            this, this.mCi, mFeatureFlags);
+        }
         mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
                 .makeDeviceStateMonitor(this, mFeatureFlags);
 
@@ -412,9 +415,11 @@
 
         mCallWaitingController = new CallWaitingController(this);
 
-        loadTtyMode();
+        if (hasCalling()) {
+            loadTtyMode();
 
-        CallManager.getInstance().registerPhone(this);
+            CallManager.getInstance().registerPhone(this);
+        }
 
         mSubscriptionsChangedListener =
                 new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -464,13 +469,21 @@
         }
     };
 
+    private boolean hasCalling() {
+        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        return mContext.getPackageManager().hasSystemFeature(
+            PackageManager.FEATURE_TELEPHONY_CALLING);
+    }
+
     private void initOnce(CommandsInterface ci) {
         if (ci instanceof SimulatedRadioControl) {
             mSimulatedRadioControl = (SimulatedRadioControl) ci;
         }
 
-        mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName())
-                .makeGsmCdmaCallTracker(this, mFeatureFlags);
+        if (hasCalling()) {
+            mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName())
+                    .makeGsmCdmaCallTracker(this, mFeatureFlags);
+        }
         mIccPhoneBookIntManager = mTelephonyComponentFactory
                 .inject(IccPhoneBookInterfaceManager.class.getName())
                 .makeIccPhoneBookInterfaceManager(this);
@@ -693,7 +706,7 @@
         unregisterForIccRecordEvents();
         registerForIccRecordEvents();
 
-        mCT.updatePhoneType();
+        if (mCT != null) mCT.updatePhoneType();
 
         int radioState = mCi.getRadioState();
         if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
@@ -753,6 +766,8 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @Override
     public PhoneConstants.State getState() {
+        if (!hasCalling()) return PhoneConstants.State.IDLE;
+
         if (mImsPhone != null) {
             PhoneConstants.State imsState = mImsPhone.getState();
             if (imsState != PhoneConstants.State.IDLE) {
@@ -837,6 +852,7 @@
 
     @Override
     public boolean isDataSuspended() {
+        if (mCT == null) return false;
         return mCT.mState != PhoneConstants.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed();
     }
 
@@ -884,7 +900,7 @@
 
     @Override
     public boolean isInEmergencyCall() {
-        if (isPhoneTypeGsm()) {
+        if (!hasCalling() || isPhoneTypeGsm()) {
             return false;
         } else {
             return mCT.isInEmergencyCall();
@@ -893,7 +909,7 @@
 
     @Override
     protected void setIsInEmergencyCall() {
-        if (!isPhoneTypeGsm()) {
+        if (!hasCalling() && !isPhoneTypeGsm()) {
             mCT.setIsInEmergencyCall();
         }
     }
@@ -985,6 +1001,7 @@
 
     @Override
     public void acceptCall(int videoState) throws CallStateException {
+        if (!hasCalling()) throw new CallStateException();
         Phone imsPhone = mImsPhone;
         if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) {
             imsPhone.acceptCall(videoState);
@@ -995,6 +1012,7 @@
 
     @Override
     public void rejectCall() throws CallStateException {
+        if (!hasCalling()) throw new CallStateException();
         mCT.rejectCall();
     }
 
@@ -1025,6 +1043,7 @@
 
     @Override
     public boolean canConference() {
+        if (!hasCalling()) return false;
         if (mImsPhone != null && mImsPhone.canConference()) {
             return true;
         }
@@ -1075,12 +1094,13 @@
 
     @Override
     public void clearDisconnected() {
+        if (!hasCalling()) return;
         mCT.clearDisconnected();
     }
 
     @Override
     public boolean canTransfer() {
-        if (isPhoneTypeGsm()) {
+        if (hasCalling() && isPhoneTypeGsm()) {
             return mCT.canTransfer();
         } else {
             loge("canTransfer: not possible in CDMA");
@@ -1090,7 +1110,7 @@
 
     @Override
     public void explicitCallTransfer() {
-        if (isPhoneTypeGsm()) {
+        if (hasCalling() && isPhoneTypeGsm()) {
             mCT.explicitCallTransfer();
         } else {
             loge("explicitCallTransfer: not possible in CDMA");
@@ -1104,11 +1124,13 @@
 
     @Override
     public GsmCdmaCall getBackgroundCall() {
+        if (!hasCalling()) return null;
         return mCT.mBackgroundCall;
     }
 
     @Override
     public Call getRingingCall() {
+        if (!hasCalling()) return null;
         Phone imsPhone = mImsPhone;
         // It returns the ringing call of ImsPhone if the ringing call of GSMPhone isn't ringing.
         // In CallManager.registerPhone(), it always registers ringing call of ImsPhone, because
@@ -1184,7 +1206,7 @@
 
     private boolean handleCallDeflectionIncallSupplementaryService(
             String dialString) {
-        if (dialString.length() > 1) {
+        if (!hasCalling() || dialString.length() > 1) {
             return false;
         }
 
@@ -1209,7 +1231,7 @@
     private boolean handleCallWaitingIncallSupplementaryService(String dialString) {
         int len = dialString.length();
 
-        if (len > 2) {
+        if (!hasCalling() || len > 2) {
             return false;
         }
 
@@ -1429,6 +1451,9 @@
     @Override
     public Connection dial(String dialString, @NonNull DialArgs dialArgs,
             Consumer<Phone> chosenPhoneConsumer) throws CallStateException {
+        if (!hasCalling()) {
+            throw new CallStateException("Calling feature is not supported!");
+        }
         if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) {
             throw new CallStateException("Sending UUS information NOT supported in CDMA!");
         }
@@ -2148,7 +2173,9 @@
 
     @Override
     public int getEmergencyNumberDbVersion() {
-        return getEmergencyNumberTracker().getEmergencyNumberDbVersion();
+        EmergencyNumberTracker tracker = getEmergencyNumberTracker();
+        if (tracker == null) return -1;
+        return tracker.getEmergencyNumberDbVersion();
     }
 
     @Override
@@ -3134,6 +3161,8 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void syncClirSetting() {
+        if (!hasCalling()) return;
+
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         migrateClirSettingIfNeeded(sp);
 
@@ -3339,8 +3368,10 @@
                 if (b != null) {
                     updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(b);
                     updateCdmaRoamingSettingsAfterCarrierConfigChanged(b);
-                    updateNrSettingsAfterCarrierConfigChanged(b);
-                    updateVoNrSettings(b);
+                    if (hasCalling()) {
+                        updateNrSettingsAfterCarrierConfigChanged(b);
+                        updateVoNrSettings(b);
+                    }
                     updateSsOverCdmaSupported(b);
                     updateCarrierN1ModeSupported(b);
                 } else {
@@ -4912,6 +4943,7 @@
      * Handler of RIL Voice Radio Technology changed event.
      */
     private void onVoiceRegStateOrRatChanged(int vrs, int vrat) {
+        if (!hasCalling()) return;
         logd("onVoiceRegStateOrRatChanged");
         mCT.dispatchCsCallRadioTech(getCsCallRadioTech(vrs, vrat));
     }
@@ -5113,6 +5145,8 @@
      * Load the current TTY mode in GsmCdmaPhone based on Telecom and UI settings.
      */
     private void loadTtyMode() {
+        if (!hasCalling()) return;
+
         int ttyMode = TelecomManager.TTY_MODE_OFF;
         TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
         if (telecomManager != null) {
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 8488ab0..aaeba23 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -35,6 +35,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -52,6 +53,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.ArrayUtils;
@@ -122,6 +124,7 @@
 
     protected final Context mContext;
     private final SubscriptionManagerService mSubscriptionManagerService;
+    private final @NonNull FeatureFlags mFeatureFlags;
 
     // Keep a record of active primary (non-opportunistic) subscription list.
     @NonNull private List<Integer> mPrimarySubList = new ArrayList<>();
@@ -201,10 +204,11 @@
     /**
      * Init instance of MultiSimSettingController.
      */
-    public static MultiSimSettingController init(Context context) {
+    public static MultiSimSettingController init(Context context,
+            @NonNull FeatureFlags featureFlags) {
         synchronized (MultiSimSettingController.class) {
             if (sInstance == null) {
-                sInstance = new MultiSimSettingController(context);
+                sInstance = new MultiSimSettingController(context, featureFlags);
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -213,9 +217,10 @@
     }
 
     @VisibleForTesting
-    public MultiSimSettingController(Context context) {
+    public MultiSimSettingController(Context context, @NonNull FeatureFlags featureFlags) {
         mContext = context;
         mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+        mFeatureFlags = featureFlags;
 
         // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change.
         TelephonyManager telephonyManager = ((TelephonyManager) mContext.getSystemService(
@@ -239,6 +244,24 @@
                         onCarrierConfigChanged(slotIndex, subId));
     }
 
+    private boolean hasCalling() {
+        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        return mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_CALLING);
+    }
+
+    private boolean hasData() {
+        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        return mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_DATA);
+    }
+
+    private boolean hasMessaging() {
+        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        return mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_MESSAGING);
+    }
+
     /**
      * Notify MOBILE_DATA of a subscription is changed.
      */
@@ -606,35 +629,43 @@
                 || mActiveModemCount == 1)) {
             int subId = mPrimarySubList.get(0);
             if (DBG) log("updateDefaultValues: to only primary sub " + subId);
-            mSubscriptionManagerService.setDefaultDataSubId(subId);
-            mSubscriptionManagerService.setDefaultVoiceSubId(subId);
-            mSubscriptionManagerService.setDefaultSmsSubId(subId);
+            if (hasData()) mSubscriptionManagerService.setDefaultDataSubId(subId);
+            if (hasCalling()) mSubscriptionManagerService.setDefaultVoiceSubId(subId);
+            if (hasMessaging()) mSubscriptionManagerService.setDefaultSmsSubId(subId);
             sendDefaultSubConfirmedNotification(subId);
             return;
         }
 
         if (DBG) log("updateDefaultValues: records: " + mPrimarySubList);
 
-        boolean dataSelected, voiceSelected, smsSelected;
+        boolean dataSelected = false;
+        boolean voiceSelected = false;
+        boolean smsSelected = false;
 
-        // Update default data subscription.
-        if (DBG) log("updateDefaultValues: Update default data subscription");
-        dataSelected = updateDefaultValue(mPrimarySubList,
-                mSubscriptionManagerService.getDefaultDataSubId(),
-                mSubscriptionManagerService::setDefaultDataSubId);
+        if (hasData()) {
+            // Update default data subscription.
+            if (DBG) log("updateDefaultValues: Update default data subscription");
+            dataSelected = updateDefaultValue(mPrimarySubList,
+                    mSubscriptionManagerService.getDefaultDataSubId(),
+                    mSubscriptionManagerService::setDefaultDataSubId);
+        }
 
-        // Update default voice subscription.
-        if (DBG) log("updateDefaultValues: Update default voice subscription");
-        voiceSelected = updateDefaultValue(mPrimarySubList,
-                mSubscriptionManagerService.getDefaultVoiceSubId(),
-                mSubscriptionManagerService::setDefaultVoiceSubId);
+        if (hasCalling()) {
+            // Update default voice subscription.
+            if (DBG) log("updateDefaultValues: Update default voice subscription");
+            voiceSelected = updateDefaultValue(mPrimarySubList,
+                    mSubscriptionManagerService.getDefaultVoiceSubId(),
+                    mSubscriptionManagerService::setDefaultVoiceSubId);
+        }
 
-        // Update default sms subscription.
-        if (DBG) log("updateDefaultValues: Update default sms subscription");
-        smsSelected = updateDefaultValue(mPrimarySubList,
-                mSubscriptionManagerService.getDefaultSmsSubId(),
-                mSubscriptionManagerService::setDefaultSmsSubId,
-                mIsAskEverytimeSupportedForSms);
+        if (hasMessaging()) {
+            // Update default sms subscription.
+            if (DBG) log("updateDefaultValues: Update default sms subscription");
+            smsSelected = updateDefaultValue(mPrimarySubList,
+                    mSubscriptionManagerService.getDefaultSmsSubId(),
+                    mSubscriptionManagerService::setDefaultSmsSubId,
+                    mIsAskEverytimeSupportedForSms);
+        }
 
         boolean autoFallbackEnabled = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_voice_data_sms_auto_fallback);
@@ -1023,11 +1054,11 @@
 
         int autoDefaultSubId = primarySubList.get(0);
 
-        if ((primarySubList.size() == 1) && !smsSelected) {
+        if (hasMessaging() && (primarySubList.size() == 1) && !smsSelected) {
             mSubscriptionManagerService.setDefaultSmsSubId(autoDefaultSubId);
         }
 
-        if ((primarySubList.size() == 1) && !voiceSelected) {
+        if (hasCalling() && (primarySubList.size() == 1) && !voiceSelected) {
             mSubscriptionManagerService.setDefaultVoiceSubId(autoDefaultSubId);
         }
 
@@ -1036,13 +1067,15 @@
         log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
                 + " next active subId " + autoDefaultSubId);
 
-        // If earlier user selected DDS is now available, set that as DDS subId.
-        if (primarySubList.contains(userPrefDataSubId)
-                && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId)
-                && (defaultDataSubId != userPrefDataSubId)) {
-            mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId);
-        } else if (!dataSelected) {
-            mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId);
+        if (hasData()) {
+            // If earlier user selected DDS is now available, set that as DDS subId.
+            if (primarySubList.contains(userPrefDataSubId)
+                    && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId)
+                    && (defaultDataSubId != userPrefDataSubId)) {
+                mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId);
+            } else if (!dataSelected) {
+                mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId);
+            }
         }
 
         if (DBG) {
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 68d24a2..67ca1e1 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -26,6 +26,7 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation;
 import android.telephony.CarrierConfigManager;
@@ -194,6 +195,7 @@
     private boolean mIsPhysicalChannelConfigOn;
     private boolean mIsPrimaryTimerActive;
     private boolean mIsSecondaryTimerActive;
+    private long mSecondaryTimerExpireTimestamp;
     private boolean mIsTimerResetEnabledForLegacyStateRrcIdle;
     /** Carrier config to reset timers when mccmnc changes */
     private boolean mIsTimerResetEnabledOnPlmnChanges;
@@ -220,6 +222,7 @@
 
     // Cached copies below to prevent race conditions
     @NonNull private ServiceState mServiceState;
+    /** Used to track link status to be DORMANT or ACTIVE */
     @Nullable private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
 
     // Ratchet physical channel config fields to prevent 5G/5G+ flickering
@@ -666,6 +669,7 @@
                 case EVENT_SECONDARY_TIMER_EXPIRED:
                     if (DBG) log("Secondary timer expired for state: " + mSecondaryTimerState);
                     mIsSecondaryTimerActive = false;
+                    mSecondaryTimerExpireTimestamp = 0;
                     mSecondaryTimerState = "";
                     updateTimers();
                     mLastShownNrDueToAdvancedBand = false;
@@ -1251,6 +1255,8 @@
     private void updatePhysicalChannelConfigs(List<PhysicalChannelConfig> physicalChannelConfigs) {
         boolean isPccListEmpty = physicalChannelConfigs == null || physicalChannelConfigs.isEmpty();
         if (isPccListEmpty && isUsingPhysicalChannelConfigForRrcDetection()) {
+            // Clear mPrimaryCellChangedWhileIdle to allow later potential one-off PCI change.
+            // Update link status to be DORMANT, but keep ratcheted bands.
             log("Physical channel configs updated: not updating PCC fields for empty PCC list "
                     + "indicating RRC idle.");
             mPrimaryCellChangedWhileIdle = false;
@@ -1310,6 +1316,7 @@
                         + mLastAnchorNrCellId + " -> " + anchorNrCellId);
                 mPrimaryCellChangedWhileIdle = true;
                 mLastAnchorNrCellId = anchorNrCellId;
+                reduceSecondaryTimerIfNeeded();
                 return;
             }
             if (mRatchetPccFieldsForSameAnchorNrCell) {
@@ -1330,6 +1337,27 @@
         }
     }
 
+    /**
+     * Called when PCI change, specifically during idle state.
+     */
+    private void reduceSecondaryTimerIfNeeded() {
+        if (!mIsSecondaryTimerActive || mNrAdvancedBandsSecondaryTimer <= 0) return;
+        // Secondary timer is active, so we must have a valid secondary rule right now.
+        OverrideTimerRule secondaryRule = mOverrideTimerRules.get(mPrimaryTimerState);
+        if (secondaryRule != null) {
+            int secondaryDuration = secondaryRule.getSecondaryTimer(mSecondaryTimerState);
+            long durationMillis = secondaryDuration * 1000L;
+            if ((mSecondaryTimerExpireTimestamp - SystemClock.uptimeMillis()) > durationMillis) {
+                if (DBG) log("Due to PCI change, reduce the secondary timer to " + durationMillis);
+                removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
+                sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, mSecondaryTimerState,
+                        durationMillis);
+            }
+        } else {
+            loge("!! Secondary timer is active, but found no rule for " + mPrimaryTimerState);
+        }
+    }
+
     private void transitionWithTimerTo(IState destState) {
         String destName = destState.getName();
         if (mIsPrimaryTimerActive) {
@@ -1369,7 +1397,9 @@
             mSecondaryTimerState = currentName;
             mPreviousState = currentName;
             mIsSecondaryTimerActive = true;
-            sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, destState, duration * 1000L);
+            long durationMillis = duration * 1000L;
+            mSecondaryTimerExpireTimestamp = SystemClock.uptimeMillis() + durationMillis;
+            sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, destState, durationMillis);
         }
         mIsPrimaryTimerActive = false;
         transitionTo(getCurrentState());
@@ -1434,6 +1464,7 @@
             }
             removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
             mIsSecondaryTimerActive = false;
+            mSecondaryTimerExpireTimestamp = 0;
             mSecondaryTimerState = "";
             transitionToCurrentState();
             return;
@@ -1464,6 +1495,7 @@
         removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
         mIsPrimaryTimerActive = false;
         mIsSecondaryTimerActive = false;
+        mSecondaryTimerExpireTimestamp = 0;
         mPrimaryTimerState = "";
         mSecondaryTimerState = "";
 
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index c5bc428..803fb19 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -212,7 +212,7 @@
                         Looper.myLooper(), featureFlags);
 
                 TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class.
-                        getName()).initMultiSimSettingController(context);
+                        getName()).initMultiSimSettingController(context, featureFlags);
 
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_EUICC)) {
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 5177adb..8b3be1e 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -31,6 +31,7 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.radio.V1_0.IRadio;
 import android.hardware.radio.V1_0.RadioError;
 import android.hardware.radio.V1_0.RadioIndicationType;
@@ -271,6 +272,13 @@
 
     static final String[] HIDL_SERVICE_NAME = {"slot1", "slot2", "slot3"};
 
+    private static final Map<String, Integer> FEATURES_TO_SERVICES = Map.ofEntries(
+            Map.entry(PackageManager.FEATURE_TELEPHONY_CALLING, HAL_SERVICE_VOICE),
+            Map.entry(PackageManager.FEATURE_TELEPHONY_DATA, HAL_SERVICE_DATA),
+            Map.entry(PackageManager.FEATURE_TELEPHONY_MESSAGING, HAL_SERVICE_MESSAGING),
+            Map.entry(PackageManager.FEATURE_TELEPHONY_IMS, HAL_SERVICE_IMS)
+    );
+
     public static List<TelephonyHistogram> getTelephonyRILTimingHistograms() {
         List<TelephonyHistogram> list;
         synchronized (sRilTimeHistograms) {
@@ -621,6 +629,7 @@
             if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) {
                 riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId]
                         + " is disabled");
+                return null;
             } else {
                 try {
                     mRadioProxy = android.hardware.radio.V1_6.IRadio.getService(
@@ -662,6 +671,7 @@
                     mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId);
                     riljLoge("getRadioProxy: set mRadioProxy for "
                             + HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
+                    return null;
                 }
             }
         } catch (RemoteException e) {
@@ -718,6 +728,9 @@
     public synchronized RadioServiceProxy getRadioServiceProxy(int service) {
         if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service);
         if ((service >= HAL_SERVICE_IMS) && !isRadioServiceSupported(service)) {
+            riljLogw("getRadioServiceProxy: " + serviceToString(service) + " for "
+                    + HIDL_SERVICE_NAME[mPhoneId] + " is not supported\n"
+                    + android.util.Log.getStackTraceString(new RuntimeException()));
             return mServiceProxies.get(service);
         }
         if (!mIsCellularSupported) {
@@ -733,7 +746,9 @@
         try {
             if (mMockModem == null && mDisabledRadioServices.get(service).contains(mPhoneId)) {
                 riljLoge("getRadioServiceProxy: " + serviceToString(service) + " for "
-                        + HIDL_SERVICE_NAME[mPhoneId] + " is disabled");
+                        + HIDL_SERVICE_NAME[mPhoneId] + " is disabled\n"
+                        + android.util.Log.getStackTraceString(new RuntimeException()));
+                return null;
             } else {
                 IBinder binder;
                 switch (service) {
@@ -944,7 +959,8 @@
                     mDisabledRadioServices.get(service).add(mPhoneId);
                     mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
                     riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for "
-                            + HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
+                            + HIDL_SERVICE_NAME[mPhoneId] + " as disabled\n"
+                            + android.util.Log.getStackTraceString(new RuntimeException()));
                 }
             }
         } catch (RemoteException e) {
@@ -1094,9 +1110,16 @@
             tdc.registerRIL(this);
         }
 
+        validateFeatureFlags();
+
         // Set radio callback; needed to set RadioIndication callback (should be done after
         // wakelock stuff is initialized above as callbacks are received on separate binder threads)
         for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
+            if (!isRadioServiceSupported(service)) {
+                riljLog("Not initializing " + serviceToString(service) + " (not supported)");
+                continue;
+            }
+
             if (service == HAL_SERVICE_RADIO) {
                 getRadioProxy();
             } else {
@@ -1161,6 +1184,26 @@
         return false;
     }
 
+    private void validateFeatureFlags() {
+        PackageManager pm = mContext.getPackageManager();
+        for (var entry : FEATURES_TO_SERVICES.entrySet()) {
+            String feature = entry.getKey();
+            int service = entry.getValue();
+
+            boolean hasFeature = pm.hasSystemFeature(feature);
+            boolean hasService = isRadioServiceSupported(service);
+
+            if (hasFeature && !hasService) {
+                riljLoge("Feature " + feature + " is declared, but service "
+                        + serviceToString(service) + " is missing");
+            }
+            if (!hasFeature && hasService) {
+                riljLoge("Service " + serviceToString(service) + " is available, but feature "
+                        + feature + " is not declared");
+            }
+        }
+    }
+
     private boolean isRadioBugDetectionEnabled() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ENABLE_RADIO_BUG_DETECTION, 1) != 0;
diff --git a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
index bab4d12..4d9196e 100644
--- a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
+++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -86,6 +86,13 @@
         register();
     }
 
+    private void requestCapabilities() {
+        if (mRadioInterfaceCapabilities != null) return;
+
+        mRadioConfig.getHalDeviceCapabilities(obtainMessage(
+                EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
+    }
+
     /**
      * Gets the radio interface capabilities for the device
      */
@@ -95,8 +102,7 @@
             // Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null
             synchronized (mLockRadioInterfaceCapabilities) {
                 if (mRadioInterfaceCapabilities == null) {
-                    mRadioConfig.getHalDeviceCapabilities(
-                            obtainMessage(EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
+                    requestCapabilities();
                     try {
                         if (Looper.myLooper() != getLooper()) {
                             mLockRadioInterfaceCapabilities.wait(2000);
@@ -158,7 +164,7 @@
         switch (msg.what) {
             case Phone.EVENT_RADIO_AVAILABLE:
             case Phone.EVENT_RADIO_ON:
-                getCapabilities();
+                requestCapabilities();
                 break;
             case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE:
                 setupCapabilities((AsyncResult) msg.obj);
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 04776b1..5da4b12 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -305,8 +305,9 @@
     /**
      * Create a new EmergencyNumberTracker.
      */
-    public EmergencyNumberTracker makeEmergencyNumberTracker(Phone phone, CommandsInterface ci) {
-        return new EmergencyNumberTracker(phone, ci);
+    public EmergencyNumberTracker makeEmergencyNumberTracker(Phone phone, CommandsInterface ci,
+            @NonNull FeatureFlags featureFlags) {
+        return new EmergencyNumberTracker(phone, ci, featureFlags);
     }
 
     private static final boolean USE_NEW_NITZ_STATE_MACHINE = true;
@@ -507,8 +508,9 @@
      * @param c The context.
      * @return The multi sim settings controller instance.
      */
-    public MultiSimSettingController initMultiSimSettingController(Context c) {
-        return MultiSimSettingController.init(c);
+    public MultiSimSettingController initMultiSimSettingController(Context c,
+            @NonNull FeatureFlags featureFlags) {
+        return MultiSimSettingController.init(c, featureFlags);
     }
 
     /**
@@ -571,9 +573,11 @@
      * @return The data settings manager instance.
      */
     public @NonNull DataSettingsManager makeDataSettingsManager(@NonNull Phone phone,
-            @NonNull DataNetworkController dataNetworkController, @NonNull Looper looper,
+            @NonNull DataNetworkController dataNetworkController,
+            @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
             @NonNull DataSettingsManager.DataSettingsManagerCallback callback) {
-        return new DataSettingsManager(phone, dataNetworkController, looper, callback);
+        return new DataSettingsManager(phone, dataNetworkController, featureFlags, looper,
+                callback);
     }
 
     /** Create CellularNetworkSecuritySafetySource. */
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 97f8c5a..0dbbc5c 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -1180,12 +1180,14 @@
 
             mPhone.getServiceStateTracker().registerForCssIndicatorChanged(
                     getHandler(), EVENT_CSS_INDICATOR_CHANGED, null);
-            mPhone.getCallTracker().registerForVoiceCallStarted(
-                    getHandler(), EVENT_VOICE_CALL_STARTED, null);
-            mPhone.getCallTracker().registerForVoiceCallEnded(
-                    getHandler(), EVENT_VOICE_CALL_ENDED, null);
+            if (mPhone.getCallTracker() != null) {
+                mPhone.getCallTracker().registerForVoiceCallStarted(
+                        getHandler(), EVENT_VOICE_CALL_STARTED, null);
+                mPhone.getCallTracker().registerForVoiceCallEnded(
+                        getHandler(), EVENT_VOICE_CALL_ENDED, null);
+            }
             // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
-            if (mPhone.getImsPhone() != null) {
+            if (mPhone.getImsPhone() != null && mPhone.getImsPhone().getCallTracker() != null) {
                 mPhone.getImsPhone().getCallTracker().registerForVoiceCallStarted(
                         getHandler(), EVENT_VOICE_CALL_STARTED, null);
                 mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
@@ -1223,12 +1225,14 @@
             }
 
             // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
-            if (mPhone.getImsPhone() != null) {
+            if (mPhone.getImsPhone() != null && mPhone.getImsPhone().getCallTracker() != null) {
                 mPhone.getImsPhone().getCallTracker().unregisterForVoiceCallStarted(getHandler());
                 mPhone.getImsPhone().getCallTracker().unregisterForVoiceCallEnded(getHandler());
             }
-            mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
-            mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
+            if (mPhone.getCallTracker() != null) {
+                mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
+                mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
+            }
 
             mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler());
             TelephonyManager tm = mPhone.getContext().getSystemService(TelephonyManager.class);
@@ -2514,7 +2518,8 @@
             newSuspendedState = true;
             // Check voice/data concurrency.
         } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()
-                && mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+                && mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+                && mPhone.getCallTracker() != null) {
             newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE;
         }
 
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 1501b58..70d3b23 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicyManager;
@@ -426,6 +427,12 @@
         }
     };
 
+    private boolean hasCalling() {
+        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        return mPhone.getContext().getPackageManager().hasSystemFeature(
+            PackageManager.FEATURE_TELEPHONY_CALLING);
+    }
+
     /**
      * The sorted network request list by priority. The highest priority network request stays at
      * the head of the list. The highest priority is 100, the lowest is 0.
@@ -861,7 +868,7 @@
 
         mDataSettingsManager = TelephonyComponentFactory.getInstance().inject(
                 DataSettingsManager.class.getName())
-                .makeDataSettingsManager(mPhone, this, looper,
+                .makeDataSettingsManager(mPhone, this, mFeatureFlags, looper,
                         new DataSettingsManagerCallback(this::post) {
                             @Override
                             public void onDataEnabledChanged(boolean enabled,
@@ -1045,16 +1052,18 @@
                     }
                 }, this::post);
 
-        // Register for call ended event for voice/data concurrent not supported case. It is
-        // intended to only listen for events from the same phone as most of the telephony modules
-        // are designed as per-SIM basis. For DSDS call ended on non-DDS sub, the frameworks relies
-        // on service state on DDS sub change from out-of-service to in-service to trigger data
-        // retry.
-        mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
-        // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
-        if (mPhone.getImsPhone() != null) {
-            mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
-                    this, EVENT_VOICE_CALL_ENDED, null);
+        if (hasCalling()) {
+            // Register for call ended event for voice/data concurrent not supported case. It is
+            // intended to only listen for events from the same phone as most of the telephony
+            // modules are designed as per-SIM basis. For DSDS call ended on non-DDS sub, the
+            // frameworks relies on service state on DDS sub change from out-of-service to
+            // in-service to trigger data retry.
+            mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
+            // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
+            if (mPhone.getImsPhone() != null) {
+                mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
+                        this, EVENT_VOICE_CALL_ENDED, null);
+            }
         }
         mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICE_CONFIG_CHANGED, null);
         mPhone.mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
@@ -1569,7 +1578,7 @@
         }
 
         // Check CS call state and see if concurrent voice/data is allowed.
-        if (mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE
+        if (hasCalling() && mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE
                 && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
             evaluation.addDataDisallowedReason(
                     DataDisallowedReason.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index e54f6d3..51e5b7d 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -47,6 +48,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SettingsObserver;
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -57,6 +59,7 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
@@ -88,6 +91,7 @@
     private static final int EVENT_INITIALIZE = 11;
 
     private final Phone mPhone;
+    private final @NonNull FeatureFlags mFeatureFlags;
     private final ContentResolver mResolver;
     private final SettingsObserver mSettingsObserver;
     private final String mLogTag;
@@ -178,10 +182,12 @@
      * @param callback Data settings manager callback.
      */
     public DataSettingsManager(@NonNull Phone phone,
-            @NonNull DataNetworkController dataNetworkController, @NonNull Looper looper,
+            @NonNull DataNetworkController dataNetworkController,
+            @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
             @NonNull DataSettingsManagerCallback callback) {
         super(looper);
         mPhone = phone;
+        mFeatureFlags = Objects.requireNonNull(featureFlags);
         mLogTag = "DSMGR-" + mPhone.getPhoneId();
         log("DataSettingsManager created.");
         mSubId = mPhone.getSubId();
@@ -264,6 +270,12 @@
         }
     }
 
+    private boolean hasCalling() {
+        if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+        return mPhone.getContext().getPackageManager().hasSystemFeature(
+            PackageManager.FEATURE_TELEPHONY_CALLING);
+    }
+
     /**
      * Called when needed to register for all events that data network controller is interested.
      */
@@ -281,9 +293,12 @@
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
                 EVENT_PROVISIONING_DATA_ENABLED_CHANGED);
-        mPhone.getCallTracker().registerForVoiceCallStarted(this, EVENT_CALL_STATE_CHANGED, null);
-        mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_CALL_STATE_CHANGED, null);
-        if (mPhone.getImsPhone() != null) {
+        if (hasCalling()) {
+            mPhone.getCallTracker().registerForVoiceCallStarted(this, EVENT_CALL_STATE_CHANGED,
+                    null);
+            mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_CALL_STATE_CHANGED, null);
+        }
+        if (hasCalling() && mPhone.getImsPhone() != null) {
             mPhone.getImsPhone().getCallTracker().registerForVoiceCallStarted(
                     this, EVENT_CALL_STATE_CHANGED, null);
             mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index e2418c5..02dd613 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -16,10 +16,12 @@
 
 package com.android.internal.telephony.emergency;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Environment;
@@ -48,6 +50,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.EmergencyNumberStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.PersistAtomsProto;
@@ -102,6 +105,7 @@
 
     private final CommandsInterface mCi;
     private final Phone mPhone;
+    private final @NonNull FeatureFlags mFeatureFlags;
     private int mPhoneId;
     private String mCountryIso;
     private String mLastKnownEmergencyCountryIso = "";
@@ -173,10 +177,20 @@
         }
     };
 
-    public EmergencyNumberTracker(Phone phone, CommandsInterface ci) {
+    public EmergencyNumberTracker(Phone phone, CommandsInterface ci,
+            @NonNull FeatureFlags featureFlags) {
+        Context ctx = phone.getContext();
+
         mPhone = phone;
         mCi = ci;
-        mResources = mPhone.getContext().getResources();
+        mFeatureFlags = featureFlags;
+        mResources = ctx.getResources();
+
+        if (mFeatureFlags.minimalTelephonyCdmCheck()
+                && !ctx.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_TELEPHONY_CALLING)) {
+            throw new UnsupportedOperationException("EmergencyNumberTracker requires calling");
+        }
 
         if (mPhone != null) {
             mPhoneId = phone.getPhoneId();
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index b5a052d..dcb3b20 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -3580,14 +3580,13 @@
                 ImsPhoneConnection conn = findConnection(imsCall);
                 // Since onCallInitiating and onCallProgressing reset mPendingMO,
                 // we can't depend on mPendingMO.
-                if ((reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
-                        || reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED
-                        || reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)
-                        && conn != null) {
+                if (conn != null) {
                     logi("onCallStartFailed eccCategory=" + eccCategory);
-                    if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
-                            || reasonInfo.getExtraCode()
-                                    == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY) {
+                    int reason = reasonInfo.getCode();
+                    int extraCode = reasonInfo.getExtraCode();
+                    if ((reason == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+                            && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)
+                            || (reason == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL)) {
                         conn.setNonDetectableEmergencyCallInfo(eccCategory);
                     }
                     conn.setImsReasonInfo(reasonInfo);
diff --git a/src/java/com/android/internal/telephony/metrics/CellularSecurityTransparencyStats.java b/src/java/com/android/internal/telephony/metrics/CellularSecurityTransparencyStats.java
new file mode 100644
index 0000000..f77474f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/CellularSecurityTransparencyStats.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import android.telephony.CellularIdentifierDisclosure;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.telephony.Rlog;
+
+/**
+ * Facilitates writing stats relating to cellular transparency features. Delegates the actual
+ * writing of stats out to {@link TelephonyStatsLog}.
+ */
+public class CellularSecurityTransparencyStats {
+
+    private static final String LOG_TAG = "CellularSecurityTransparencyStats";
+    private static final String LOG_DESCRIPTOR_SIM_MCC = "SIM MCC";
+    private static final String LOG_DESCRIPTOR_SIM_MNC = "SIM MNC";
+    private static final String LOG_DESCRIPTOR_DISCLOSURE_MCC = "disclosure MCC";
+    private static final String LOG_DESCRIPTOR_DISCLOSURE_MNC = "disclosure MNC";
+    private static final int DEFAULT_PLMN_PART = -1;
+
+    /**
+     * Log an identifier disclosure to be written out to {@link TelephonyStatsLog}
+     */
+    public void logIdentifierDisclosure(CellularIdentifierDisclosure disclosure, String simMcc,
+            String simMnc, boolean notificationsEnabled) {
+
+        int mcc = parsePlmnPartOrDefault(simMcc, LOG_DESCRIPTOR_SIM_MCC);
+        int mnc = parsePlmnPartOrDefault(simMnc, LOG_DESCRIPTOR_SIM_MNC);
+
+        int disclosureMcc = DEFAULT_PLMN_PART;
+        int disclosureMnc = DEFAULT_PLMN_PART;
+        String plmn = disclosure.getPlmn();
+        if (plmn != null) {
+            String[] plmnParts = plmn.split("-");
+            if (plmnParts.length == 2) {
+                disclosureMcc = parsePlmnPartOrDefault(plmnParts[0], LOG_DESCRIPTOR_DISCLOSURE_MCC);
+                disclosureMnc = parsePlmnPartOrDefault(plmnParts[1], LOG_DESCRIPTOR_DISCLOSURE_MNC);
+            }
+        }
+
+        writeIdentifierDisclosure(mcc, mnc, disclosureMcc, disclosureMnc,
+                disclosure.getCellularIdentifier(), disclosure.getNasProtocolMessage(),
+                disclosure.isEmergency(), notificationsEnabled);
+
+    }
+
+    private int parsePlmnPartOrDefault(String input, String logDescriptor) {
+        try {
+            return Integer.parseInt(input);
+        } catch (NumberFormatException e) {
+            Rlog.d(LOG_TAG, "Failed to parse " + logDescriptor + ": " + input);
+        }
+
+        return DEFAULT_PLMN_PART;
+    }
+
+    /**
+     * Write identifier disclosure data out to {@link TelephonyStatsLog}. This method is split
+     * out to enable testing, since {@link TelephonyStatsLog} is a final static class.
+     */
+    @VisibleForTesting
+    public void writeIdentifierDisclosure(int mcc, int mnc, int disclosureMcc, int disclosureMnc,
+            int identifier, int protocolMessage, boolean isEmergency,
+            boolean areNotificationsEnabled) {
+        TelephonyStatsLog.write(TelephonyStatsLog.CELLULAR_IDENTIFIER_DISCLOSED, mcc, mnc,
+                disclosureMcc, disclosureMnc, identifier, protocolMessage, isEmergency,
+                areNotificationsEnabled);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index a315f1e..856045f 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -138,14 +138,6 @@
     private static final long MIN_COOLDOWN_MILLIS =
             DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
 
-    /**
-     * Sets atom pull cool down to 4 minutes for userdebug build.
-     *
-     * <p>Applies to certain atoms: CellularServiceState.
-     */
-    private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
-            DBG ? 10L * MILLIS_PER_SECOND :
-                    IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : 23L * MILLIS_PER_HOUR;
 
     /**
      * Buckets with less than these many calls will be dropped.
@@ -166,6 +158,13 @@
     private static final long CELL_SERVICE_DURATION_BUCKET_MILLIS =
             DBG || IS_DEBUGGABLE ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
 
+    /**
+     * Sets atom pull cool down to 4 minutes for userdebug build, 5 hours for user build.
+     *
+     * <p>Applies to certain atoms: CellularServiceState, DataCallSession,
+     * ImsRegistrationTermination.
+     */
+    private final long mPowerCorrelatedMinCooldownMillis;
     private final PersistAtomsStorage mStorage;
     private final DeviceStateHelper mDeviceStateHelper;
     private final StatsManager mStatsManager;
@@ -234,6 +233,9 @@
 
         mAirplaneModeStats = new AirplaneModeStats(context);
         mDefaultNetworkMonitor = new DefaultNetworkMonitor(context, featureFlags);
+        mPowerCorrelatedMinCooldownMillis = DBG ? 10L * MILLIS_PER_SECOND :
+                IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : (long) context.getResources().getInteger(
+                com.android.internal.R.integer.config_metrics_pull_cooldown_millis);
     }
 
     /**
@@ -516,7 +518,8 @@
     private int pullDataCallSession(List<StatsEvent> data) {
         // Include ongoing data call segments
         concludeDataCallSessionStats();
-        DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS);
+        DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(
+                mPowerCorrelatedMinCooldownMillis);
         if (dataCallSessions != null) {
             Arrays.stream(dataCallSessions)
                     .forEach(dataCall -> data.add(buildStatsEvent(dataCall)));
@@ -544,8 +547,8 @@
     private int pullCellularServiceState(List<StatsEvent> data) {
         // Include the latest durations
         concludeServiceStateStats();
-        CellularServiceState[] persistAtoms =
-                mStorage.getCellularServiceStates(CELL_SERVICE_MIN_COOLDOWN_MILLIS);
+        CellularServiceState[] persistAtoms = mStorage.getCellularServiceStates(
+                mPowerCorrelatedMinCooldownMillis);
         if (persistAtoms != null) {
             // list is already shuffled when instances were inserted
             Arrays.stream(persistAtoms)
@@ -573,8 +576,8 @@
     }
 
     private int pullImsRegistrationTermination(List<StatsEvent> data) {
-        ImsRegistrationTermination[] persistAtoms =
-                mStorage.getImsRegistrationTerminations(MIN_COOLDOWN_MILLIS);
+        ImsRegistrationTermination[] persistAtoms = mStorage.getImsRegistrationTerminations(
+                mPowerCorrelatedMinCooldownMillis);
         if (persistAtoms != null) {
             // list is already shuffled when instances were inserted
             Arrays.stream(persistAtoms)
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 2f28575..92d9372 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -93,6 +93,7 @@
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.uwb.UwbManager;
@@ -2518,6 +2519,12 @@
             logd("getSatellitePlmnsForCarrier: carrierEnabledSatelliteFlag is disabled");
             return new ArrayList<>();
         }
+
+        if (!isSatelliteSupportedViaCarrier(subId)) {
+            logd("Satellite for carrier is not supported.");
+            return new ArrayList<>();
+        }
+
         synchronized (mSupportedSatelliteServicesLock) {
             return mMergedPlmnListPerCarrier.get(subId, new ArrayList<>()).stream().toList();
         }
@@ -2596,21 +2603,21 @@
     }
 
     /**
-     * @return {@code true} if any subscription on the device is connected to satellite,
-     * {@code false} otherwise.
+     * @return {@code Pair<true, subscription ID>} if any subscription on the device is connected to
+     * satellite, {@code Pair<false, null>} otherwise.
      */
-    private boolean isUsingNonTerrestrialNetworkViaCarrier() {
+    private Pair<Boolean, Integer> isUsingNonTerrestrialNetworkViaCarrier() {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             logd("isUsingNonTerrestrialNetwork: carrierEnabledSatelliteFlag is disabled");
-            return false;
+            return new Pair<>(false, null);
         }
         for (Phone phone : PhoneFactory.getPhones()) {
             ServiceState serviceState = phone.getServiceState();
             if (serviceState != null && serviceState.isUsingNonTerrestrialNetwork()) {
-                return true;
+                return new Pair<>(true, phone.getSubId());
             }
         }
-        return false;
+        return new Pair<>(false, null);
     }
 
     /**
@@ -2624,7 +2631,7 @@
                     + " is disabled");
             return false;
         }
-        if (isUsingNonTerrestrialNetworkViaCarrier()) {
+        if (isUsingNonTerrestrialNetworkViaCarrier().first) {
             return true;
         }
         for (Phone phone : PhoneFactory.getPhones()) {
@@ -3996,7 +4003,13 @@
     }
 
     private void determineSystemNotification() {
-        if (isUsingNonTerrestrialNetworkViaCarrier()) {
+        if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("determineSystemNotification: carrierEnabledSatelliteFlag is disabled");
+            return;
+        }
+
+        Pair<Boolean, Integer> isNtn = isUsingNonTerrestrialNetworkViaCarrier();
+        if (isNtn.first) {
             if (mSharedPreferences == null) {
                 try {
                     mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
@@ -4006,18 +4019,18 @@
                 }
             }
             if (mSharedPreferences == null) {
-                loge("handleEventServiceStateChanged: Cannot get default shared preferences");
+                loge("determineSystemNotification: Cannot get default shared preferences");
                 return;
             }
             if (!mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)) {
-                showSatelliteSystemNotification();
+                showSatelliteSystemNotification(isNtn.second);
                 mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY,
                         true).apply();
             }
         }
     }
 
-    private void showSatelliteSystemNotification() {
+    private void showSatelliteSystemNotification(int subId) {
         logd("showSatelliteSystemNotification");
         final NotificationChannel notificationChannel = new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID,
@@ -4055,6 +4068,7 @@
 
         // Add action to invoke Satellite setting activity in Settings.
         Intent intentSatelliteSetting = new Intent(ACTION_SATELLITE_SETTING);
+        intentSatelliteSetting.putExtra("sub_id", subId);
         PendingIntent pendingIntentSatelliteSetting = PendingIntent.getActivity(mContext, 0,
                 intentSatelliteSetting, PendingIntent.FLAG_IMMUTABLE);
 
diff --git a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
index 4540b8a..8591e86 100644
--- a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
+++ b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
@@ -21,6 +21,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.CellularSecurityTransparencyStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.telephony.Rlog;
 
 import java.time.Instant;
@@ -62,13 +65,13 @@
     // 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;
+    private SubscriptionManagerService mSubscriptionManagerService;
+    private CellularSecurityTransparencyStats mCellularSecurityTransparencyStats;
 
     public CellularIdentifierDisclosureNotifier(CellularNetworkSecuritySafetySource safetySource) {
-        this(
-                Executors.newSingleThreadScheduledExecutor(),
-                DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
-                TimeUnit.MINUTES,
-                safetySource);
+        this(Executors.newSingleThreadScheduledExecutor(), DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
+                TimeUnit.MINUTES, safetySource, SubscriptionManagerService.getInstance(),
+                new CellularSecurityTransparencyStats());
     }
 
     /**
@@ -83,12 +86,16 @@
             ScheduledExecutorService notificationQueue,
             long windowCloseDuration,
             TimeUnit windowCloseUnit,
-            CellularNetworkSecuritySafetySource safetySource) {
+            CellularNetworkSecuritySafetySource safetySource,
+            SubscriptionManagerService subscriptionManagerService,
+            CellularSecurityTransparencyStats cellularSecurityTransparencyStats) {
         mSerializedWorkQueue = notificationQueue;
         mWindowCloseDuration = windowCloseDuration;
         mWindowCloseUnit = windowCloseUnit;
         mWindows = new HashMap<>();
         mSafetySource = safetySource;
+        mSubscriptionManagerService = subscriptionManagerService;
+        mCellularSecurityTransparencyStats = cellularSecurityTransparencyStats;
     }
 
     /**
@@ -98,6 +105,8 @@
     public void addDisclosure(Context context, int subId, CellularIdentifierDisclosure disclosure) {
         Rlog.d(TAG, "Identifier disclosure reported: " + disclosure);
 
+        logDisclosure(subId, disclosure);
+
         synchronized (mEnabledLock) {
             if (!mEnabled) {
                 Rlog.d(TAG, "Skipping disclosure because notifier was disabled.");
@@ -123,6 +132,31 @@
         } // end mEnabledLock
     }
 
+    private void logDisclosure(int subId, CellularIdentifierDisclosure disclosure) {
+        try {
+            mSerializedWorkQueue.execute(runLogDisclosure(subId, disclosure));
+        } catch (RejectedExecutionException e) {
+            Rlog.e(TAG, "Failed to schedule runLogDisclosure: " + e.getMessage());
+        }
+    }
+
+    private Runnable runLogDisclosure(int subId,
+            CellularIdentifierDisclosure disclosure) {
+        return () -> {
+            SubscriptionInfoInternal subInfo =
+                    mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+            String mcc = null;
+            String mnc = null;
+            if (subInfo != null) {
+                mcc = subInfo.getMcc();
+                mnc = subInfo.getMnc();
+            }
+
+            mCellularSecurityTransparencyStats.logIdentifierDisclosure(disclosure, mcc, mnc,
+                    isEnabled());
+        };
+    }
+
     /**
      * Re-enable if previously disabled. This means that {@code addDisclsoure} will start tracking
      * disclosures again and potentially emitting notifications.
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 0459bf6..84e84d9 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -1799,6 +1799,8 @@
         }
         pw.decreaseIndent();
         pw.println();
+        mCarrierServiceBindHelper.dump(fd, pw, args);
+        pw.println();
         pw.println("sLocalLog= ");
         pw.increaseIndent();
         mPinStorage.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 0457971..a22f075 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -1290,6 +1290,11 @@
             return -1;
         }
 
+        if (mUiccApplications[index] == null) {
+            loge("App index " + index + " is invalid");
+            return -1;
+        }
+
         if (mUiccApplications[index].getType() != expectedAppType
                 && mUiccApplications[index].getType() != altExpectedAppType) {
             loge("App index " + index + " is invalid since it's not "
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index 776715c..41fd45a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -16,10 +16,12 @@
 
 package com.android.internal.telephony;
 
+import android.content.pm.PackageManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.telephony.gsm.SmsMessage;
 import com.android.internal.util.HexDump;
@@ -28,6 +30,12 @@
 
 public class GsmSmsTest extends AndroidTestCase {
 
+    private boolean hasMessaging() {
+        final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext()
+                .getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING);
+    }
+
     @SmallTest
     public void testAddressing() throws Exception {
         String pdu = "07914151551512f2040B916105551511f100006060605130308A04D4F29C0E";
@@ -258,8 +266,8 @@
 
     @SmallTest
     public void testFragmentText() throws Exception {
-        boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
-                TelephonyManager.PHONE_TYPE_GSM);
+        boolean isGsmPhoneWithMessaging = (TelephonyManager.getDefault().getPhoneType()
+                == TelephonyManager.PHONE_TYPE_GSM) && hasMessaging();
 
         // Valid 160 character 7-bit text.
         String text = "123456789012345678901234567890123456789012345678901234567890" +
@@ -271,7 +279,7 @@
         assertEquals(1, ted.codeUnitSize);
         assertEquals(0, ted.languageTable);
         assertEquals(0, ted.languageShiftTable);
-        if (isGsmPhone) {
+        if (isGsmPhoneWithMessaging) {
             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
             assertEquals(1, fragments.size());
         }
@@ -286,7 +294,7 @@
         assertEquals(1, ted.codeUnitSize);
         assertEquals(0, ted.languageTable);
         assertEquals(0, ted.languageShiftTable);
-        if (isGsmPhone) {
+        if (isGsmPhoneWithMessaging) {
             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
             assertEquals(2, fragments.size());
             assertEquals(text, fragments.get(0) + fragments.get(1));
@@ -297,8 +305,8 @@
 
     @SmallTest
     public void testFragmentTurkishText() throws Exception {
-        boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
-                TelephonyManager.PHONE_TYPE_GSM);
+        boolean isGsmPhoneWithMessaging = (TelephonyManager.getDefault().getPhoneType()
+                == TelephonyManager.PHONE_TYPE_GSM) && hasMessaging();
 
         int[] oldTables = GsmAlphabet.getEnabledSingleShiftTables();
         int[] turkishTable = { 1 };
@@ -313,7 +321,7 @@
         assertEquals(1, ted.codeUnitSize);
         assertEquals(0, ted.languageTable);
         assertEquals(1, ted.languageShiftTable);
-        if (isGsmPhone) {
+        if (isGsmPhoneWithMessaging) {
             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
             assertEquals(1, fragments.size());
             assertEquals(text, fragments.get(0));
@@ -329,7 +337,7 @@
         assertEquals(1, ted.codeUnitSize);
         assertEquals(0, ted.languageTable);
         assertEquals(1, ted.languageShiftTable);
-        if (isGsmPhone) {
+        if (isGsmPhoneWithMessaging) {
             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
             assertEquals(2, fragments.size());
             assertEquals(text, fragments.get(0) + fragments.get(1));
@@ -347,7 +355,7 @@
         assertEquals(1, ted.codeUnitSize);
         assertEquals(0, ted.languageTable);
         assertEquals(1, ted.languageShiftTable);
-        if (isGsmPhone) {
+        if (isGsmPhoneWithMessaging) {
             ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
             assertEquals(3, fragments.size());
             assertEquals(text, fragments.get(0) + fragments.get(1) + fragments.get(2));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index 4c68e26..a7e9604 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -255,7 +255,7 @@
         // Capture listener to emulate the carrier config change notification used later
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
-        mMultiSimSettingControllerUT = new MultiSimSettingController(mContext);
+        mMultiSimSettingControllerUT = new MultiSimSettingController(mContext, mFeatureFlags);
         processAllMessages();
         verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
                 listenerArgumentCaptor.capture());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index b854535..e08abd9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -1550,7 +1550,7 @@
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.areAnyTimersActive());
 
-        // switch to connected_rrc_idle before primary timer expires
+        // empty PCC, switch to connected_rrc_idle before primary timer expires
         physicalChannelConfigs.clear();
         mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
                 new AsyncResult(null, physicalChannelConfigs, null));
@@ -1571,16 +1571,40 @@
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.areAnyTimersActive());
 
-        // 5 seconds passed during connected_mmwave -> connected_rrc_idle secondary timer
-        moveTimeForward(5 * 1000);
+        // Verify secondary timer is still active after 6 seconds passed during
+        // connected_mmwave -> connected_rrc_idle secondary timer, should still keep the primary
+        // state icon.
+        moveTimeForward((5 + 1) * 1000);
         processAllMessages();
         assertEquals("connected_rrc_idle", getCurrentState().getName());
         assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.areAnyTimersActive());
+    }
 
-        // secondary timer expired
-        moveTimeForward(15 * 1000);
+    @Test
+    public void testSecondaryTimerAdvanceBandReduceOnPciChange() throws Exception {
+        // The advance band secondary timer has been running for 6 seconds, 20 - 6 seconds are left.
+        testSecondaryTimerAdvanceBand();
+
+        // PCI changed from 1 to 2 for the first while the timer is running.
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, List.of(
+                        new PhysicalChannelConfig.Builder()
+                                .setPhysicalCellId(2)
+                                .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+                                .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+                                .build()), null));
+        processAllMessages();
+
+        // Verify the first PCI change is exempted from triggering state change.
+        assertEquals("connected_rrc_idle", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+                mNetworkTypeController.getOverrideNetworkType());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+        // Verify the timer has been reduced from 20 - 6s(advance band) to 5s(regular).
+        moveTimeForward(5 * 1000);
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 88c16ae..673acbc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -577,6 +577,8 @@
         mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
         mNullCipherNotifier = Mockito.mock(NullCipherNotifier.class);
 
+        doReturn(true).when(mFeatureFlags).minimalTelephonyCdmCheck();
+
         TelephonyManager.disableServiceHandleCaching();
         PropertyInvalidatedCache.disableForTestMode();
         // For testing do not allow Log.WTF as it can cause test process to crash
@@ -640,7 +642,7 @@
                         nullable(CommandsInterface.class), nullable(FeatureFlags.class));
         doReturn(mEmergencyNumberTracker).when(mTelephonyComponentFactory)
                 .makeEmergencyNumberTracker(nullable(Phone.class),
-                        nullable(CommandsInterface.class));
+                        nullable(CommandsInterface.class), any(FeatureFlags.class));
         doReturn(getTestEmergencyNumber()).when(mEmergencyNumberTracker)
                 .getEmergencyNumber(any());
         doReturn(mUiccProfile).when(mTelephonyComponentFactory)
@@ -821,6 +823,12 @@
         doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
         doReturn(true).when(mTelephonyManager).isDataCapable();
 
+        mContextFixture.addSystemFeature(PackageManager.FEATURE_TELECOM);
+        mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING);
+        mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_DATA);
+        mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC);
+        mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING);
+
         doReturn(TelephonyManager.PHONE_TYPE_GSM).when(mTelephonyManager).getPhoneType();
         doReturn(mServiceState).when(mSST).getServiceState();
         doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
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 00e6680..9423551 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -872,7 +872,7 @@
         mMockSubInfo = Mockito.mock(SubscriptionInfo.class);
         mFeatureFlags = Mockito.mock(FeatureFlags.class);
         when(mTelephonyComponentFactory.makeDataSettingsManager(any(Phone.class),
-                any(DataNetworkController.class), any(Looper.class),
+                any(DataNetworkController.class), any(FeatureFlags.class), any(Looper.class),
                 any(DataSettingsManager.DataSettingsManagerCallback.class))).thenCallRealMethod();
         doReturn(mMockedImsMmTelManager).when(mMockedImsManager).getImsMmTelManager(anyInt());
         doReturn(mMockedImsRcsManager).when(mMockedImsManager).getImsRcsManager(anyInt());
@@ -1857,7 +1857,8 @@
         boolean isDataEnabled = mDataNetworkControllerUT.getDataSettingsManager().isDataEnabled();
         doReturn(mDataNetworkControllerUT.getDataSettingsManager())
                 .when(mPhone).getDataSettingsManager();
-        MultiSimSettingController controller = Mockito.spy(new MultiSimSettingController(mContext));
+        MultiSimSettingController controller = Mockito.spy(new MultiSimSettingController(mContext,
+                mFeatureFlags));
         doReturn(true).when(controller).isCarrierConfigLoadedForAllSub();
         replaceInstance(MultiSimSettingController.class, "sInstance", null, controller);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
index fc1bf0d..3f18a3a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -77,7 +77,7 @@
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
 
         mDataSettingsManagerUT = new DataSettingsManager(mPhone, mDataNetworkController,
-                Looper.myLooper(), mMockedDataSettingsManagerCallback);
+                mFeatureFlags, Looper.myLooper(), mMockedDataSettingsManagerCallback);
         logd("DataSettingsManagerTest -Setup!");
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
index c47eb3b..10dbfea 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
@@ -51,7 +51,6 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyTest;
 
 import com.google.i18n.phonenumbers.ShortNumberInfo;
@@ -61,6 +60,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 
 import java.io.BufferedInputStream;
@@ -139,7 +139,7 @@
         mShortNumberInfo = mock(ShortNumberInfo.class);
         mCarrierConfigManagerMock = mock(CarrierConfigManager.class);
 
-        mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext());
+        mContext = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
         mMockContext = mock(Context.class);
         mResources = mock(Resources.class);
 
@@ -151,9 +151,14 @@
         doReturn(1).when(mPhone2).getPhoneId();
         doReturn(SUB_ID_PHONE_2).when(mPhone2).getSubId();
 
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mPackageManager).when(mMockContext).getPackageManager();
+
         initializeEmergencyNumberListTestSamples();
-        mEmergencyNumberTrackerMock = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
-        mEmergencyNumberTrackerMock2 = new EmergencyNumberTracker(mPhone2, mSimulatedCommands);
+        mEmergencyNumberTrackerMock = new EmergencyNumberTracker(mPhone, mSimulatedCommands,
+                mFeatureFlags);
+        mEmergencyNumberTrackerMock2 = new EmergencyNumberTracker(mPhone2, mSimulatedCommands,
+                mFeatureFlags);
         doReturn(mEmergencyNumberTrackerMock2).when(mPhone2).getEmergencyNumberTracker();
         mEmergencyNumberTrackerMock.DBG = true;
 
@@ -171,10 +176,13 @@
     public void tearDown() throws Exception {
         // Set back to single sim mode
         setSinglePhone();
-        Path target = Paths.get(mLocalDownloadDirectory.getPath(), EMERGENCY_NUMBER_DB_OTA_FILE);
-        Files.deleteIfExists(target);
-        mLocalDownloadDirectory.delete();
-        mLocalDownloadDirectory = null;
+        if (mLocalDownloadDirectory != null) {
+            Path target = Paths.get(mLocalDownloadDirectory.getPath(),
+                    EMERGENCY_NUMBER_DB_OTA_FILE);
+            Files.deleteIfExists(target);
+            mLocalDownloadDirectory.delete();
+            mLocalDownloadDirectory = null;
+        }
         mEmergencyNumberTrackerMock = null;
         mEmergencyNumberTrackerMock2 = null;
         mEmergencyNumberListTestSample.clear();
@@ -361,12 +369,12 @@
     @Test
     public void testRegistrationForCountryChangeIntent() throws Exception {
         EmergencyNumberTracker localEmergencyNumberTracker;
-        Context spyContext = spy(mContext);
-        doReturn(spyContext).when(mPhone).getContext();
+        Mockito.clearInvocations(mContext);
         ArgumentCaptor<IntentFilter> intentCaptor = ArgumentCaptor.forClass(IntentFilter.class);
 
-        localEmergencyNumberTracker = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
-        verify(spyContext, times(1)).registerReceiver(any(), intentCaptor.capture());
+        localEmergencyNumberTracker = new EmergencyNumberTracker(mPhone, mSimulatedCommands,
+                mFeatureFlags);
+        verify(mContext, times(1)).registerReceiver(any(), intentCaptor.capture());
         IntentFilter ifilter = intentCaptor.getValue();
         assertTrue(ifilter.hasAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED));
     }
@@ -543,7 +551,7 @@
                 com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
 
         EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
-                mPhone, mSimulatedCommands);
+                mPhone, mSimulatedCommands, mFeatureFlags);
         emergencyNumberTrackerMock.sendMessage(
                 emergencyNumberTrackerMock.obtainMessage(
                         1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
@@ -616,7 +624,7 @@
                 com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
 
         EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
-                mPhone, mSimulatedCommands);
+                mPhone, mSimulatedCommands, mFeatureFlags);
         emergencyNumberTrackerMock.sendMessage(
                 emergencyNumberTrackerMock.obtainMessage(
                         1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
@@ -720,7 +728,7 @@
                 com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
 
         EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
-                mPhone, mSimulatedCommands);
+                mPhone, mSimulatedCommands, mFeatureFlags);
         emergencyNumberTrackerMock.sendMessage(
                 emergencyNumberTrackerMock.obtainMessage(
                         1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
@@ -843,7 +851,7 @@
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
         EmergencyNumberTracker localEmergencyNumberTracker =
-                new EmergencyNumberTracker(mPhone, mSimulatedCommands);
+                new EmergencyNumberTracker(mPhone, mSimulatedCommands, mFeatureFlags);
         verify(mCarrierConfigManagerMock)
                 .registerCarrierConfigChangeListener(any(), listenerArgumentCaptor.capture());
         CarrierConfigManager.CarrierConfigChangeListener carrierConfigChangeListener =
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 79b1ada..6056112 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -2497,6 +2497,34 @@
     }
 
     @Test
+    @SmallTest
+    public void testDomainSelectionEmergencyPermFailure() {
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        startOutgoingCall();
+        ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+        mImsCallListener.onCallStartFailed(mSecondImsCall,
+                new ImsReasonInfo(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE, -1));
+        processAllMessages();
+        assertNotNull(c.getImsReasonInfo());
+        assertEquals(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE,
+                c.getImsReasonInfo().getCode());
+    }
+
+    @Test
+    @SmallTest
+    public void testDomainSelectionEmergencyTempFailure() {
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        startOutgoingCall();
+        ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+        mImsCallListener.onCallStartFailed(mSecondImsCall,
+                new ImsReasonInfo(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE, -1));
+        processAllMessages();
+        assertNotNull(c.getImsReasonInfo());
+        assertEquals(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE,
+                c.getImsReasonInfo().getCode());
+    }
+
+    @Test
     public void testUpdateImsCallStatusIncoming() throws Exception {
         // Incoming call
         ImsPhoneConnection connection = setupRingingConnection();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/CellularSecurityTransparencyStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/CellularSecurityTransparencyStatsTest.java
new file mode 100644
index 0000000..2149de0
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/CellularSecurityTransparencyStatsTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.metrics;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.CellularIdentifierDisclosure;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class CellularSecurityTransparencyStatsTest {
+
+    private CellularSecurityTransparencyStats mCellularSecurityStats;
+
+    @Before
+    public void setUp() throws Exception {
+        mCellularSecurityStats = spy(new CellularSecurityTransparencyStats());
+    }
+
+    @Test
+    public void testLogIdentifierDisclosure_NullSimPlmn() {
+        CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "123-456", true);
+
+        mCellularSecurityStats.logIdentifierDisclosure(disclosure, null, null, true);
+
+        verify(mCellularSecurityStats).writeIdentifierDisclosure(-1, -1, 123, 456,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+                true);
+    }
+
+    @Test
+    public void testLogIdentifierDisclosure_badSimPlmn() {
+        CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "123-456", true);
+
+        mCellularSecurityStats.logIdentifierDisclosure(disclosure, "INCORRECTLY", "FORMATTED",
+                true);
+
+        verify(mCellularSecurityStats).writeIdentifierDisclosure(-1, -1, 123, 456,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+                true);
+    }
+
+    @Test
+    public void testLogIdentifierDisclosure_badDisclosurePlmn() {
+        CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "INCORRECT", true);
+
+        mCellularSecurityStats.logIdentifierDisclosure(disclosure, "123", "456", true);
+
+        verify(mCellularSecurityStats).writeIdentifierDisclosure(123, 456, -1, -1,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+                true);
+    }
+
+    @Test
+    public void testLogIdentifierDisclosure_expectedGoodData() {
+        CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "999-666", true);
+
+        mCellularSecurityStats.logIdentifierDisclosure(disclosure, "123", "456", true);
+
+        verify(mCellularSecurityStats).writeIdentifierDisclosure(123, 456, 999, 666,
+                CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+                CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+                true);
+
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 0426737..35f1bb7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -70,8 +70,8 @@
                     .setCoolDownMillis(24L * 3600L * 1000L)
                     .build();
     private static final long MIN_COOLDOWN_MILLIS = 23L * 3600L * 1000L;
-    private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
-            IS_DEBUGGABLE ? 4L *  60L * 1000L : MIN_COOLDOWN_MILLIS;
+    private static final long POWER_CORRELATED_MIN_COOLDOWN_MILLIS =
+            IS_DEBUGGABLE ? 4L *  60L * 1000L : 5L * 3600L * 1000L;
     private static final long MIN_CALLS_PER_BUCKET = 5L;
 
     // NOTE: these fields are currently 32-bit internally and padded to 64-bit by TelephonyManager
@@ -402,6 +402,9 @@
     @SmallTest
     public void onPullAtom_cellularServiceState_tooFrequent() throws Exception {
         doReturn(null).when(mPersistAtomsStorage).getCellularServiceStates(anyLong());
+        mContextFixture.putIntResource(
+                com.android.internal.R.integer.config_metrics_pull_cooldown_millis,
+                (int) POWER_CORRELATED_MIN_COOLDOWN_MILLIS);
         List<StatsEvent> actualAtoms = new ArrayList<>();
 
         int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
@@ -409,7 +412,7 @@
         assertThat(actualAtoms).hasSize(0);
         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
         verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(
-                eq(CELL_SERVICE_MIN_COOLDOWN_MILLIS));
+                eq(POWER_CORRELATED_MIN_COOLDOWN_MILLIS));
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
 
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 f1a8de9..aae6a2f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -1736,6 +1736,8 @@
 
         // Trigger carrier config changed with carrierEnabledSatelliteFlag enabled
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
         for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
                 : mCarrierConfigChangedListenerList) {
             pair.first.execute(() -> pair.second.onCarrierConfigChanged(
@@ -2723,8 +2725,8 @@
         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.
+        // If the PlmnListPerCarrier and the overlay config plmn list are exist but
+        // KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is false, verify passing to the modem.
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "00103"}).toList();
         overlayConfigPlmnList =
                 Arrays.stream(new String[]{"00101", "00102", "00104"}).toList();
@@ -2735,6 +2737,22 @@
                 entitlementPlmnList, mIIntegerConsumer);
 
         plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
+        assertEquals(new ArrayList<>(), plmnListPerCarrier);
+        allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
+                entitlementPlmnList, overlayConfigPlmnList);
+        verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
+                eq(entitlementPlmnList), eq(allSatellitePlmnList), any(Message.class));
+
+        // If the PlmnListPerCarrier and the overlay config plmn list are exist and
+        // KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is true verify passing the modem.
+        reset(mMockSatelliteModemInterface);
+        mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                true);
+
+        mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true,
+                entitlementPlmnList, mIIntegerConsumer);
+
+        plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
         allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
                 plmnListPerCarrier, overlayConfigPlmnList);
 
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 8841c7a..aba8164 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
@@ -29,12 +29,17 @@
 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.content.Context;
 import android.telephony.CellularIdentifierDisclosure;
 
 import com.android.internal.telephony.TestExecutorService;
+import com.android.internal.telephony.metrics.CellularSecurityTransparencyStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InOrder;
@@ -50,6 +55,9 @@
     private static final int SUB_ID_2 = 2;
     private CellularIdentifierDisclosure mDislosure;
     private CellularNetworkSecuritySafetySource mSafetySource;
+    private CellularSecurityTransparencyStats mStats;
+    private TestExecutorService mExecutor;
+    private SubscriptionManagerService mSubscriptionManagerService;
     private Context mContext;
     private InOrder mInOrder;
 
@@ -62,15 +70,16 @@
                         "001001",
                         false);
         mSafetySource = mock(CellularNetworkSecuritySafetySource.class);
+        mStats = mock(CellularSecurityTransparencyStats.class);
+        mExecutor = new TestExecutorService();
+        mSubscriptionManagerService = mock(SubscriptionManagerService.class);
+        mContext = mock(Context.class);
         mInOrder = inOrder(mSafetySource);
     }
 
     @Test
     public void testInitializeDisabled() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
 
         assertFalse(notifier.isEnabled());
         verify(mSafetySource, never()).setIdentifierDisclosureIssueEnabled(any(), anyBoolean());
@@ -78,10 +87,7 @@
 
     @Test
     public void testDisableAddDisclosureNop() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
 
         assertFalse(notifier.isEnabled());
         notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
@@ -92,10 +98,8 @@
 
     @Test
     public void testAddDisclosureEmergencyNop() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
+
         CellularIdentifierDisclosure emergencyDisclosure =
                 new CellularIdentifierDisclosure(
                         CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
@@ -113,11 +117,7 @@
 
     @Test
     public void testAddDisclosureCountIncrements() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
-
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
         notifier.enable(mContext);
 
         for (int i = 0; i < 3; i++) {
@@ -135,28 +135,20 @@
 
     @Test
     public void testSingleDisclosureStartAndEndTimesAreEqual() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
-
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
         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)));
+        Assert.assertEquals(notifier.getFirstOpen(SUB_ID_1), 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);
-
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
         notifier.enable(mContext);
 
         notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
@@ -175,10 +167,7 @@
 
     @Test
     public void testAddDisclosureThenWindowClose() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
 
         // One round of disclosures
         notifier.enable(mContext);
@@ -191,7 +180,7 @@
                 .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
 
         // Window close should reset the counter
-        executor.advanceTime(WINDOW_CLOSE_ADVANCE_MILLIS);
+        mExecutor.advanceTime(WINDOW_CLOSE_ADVANCE_MILLIS);
         assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
 
         // A new disclosure should increment as normal
@@ -203,10 +192,7 @@
 
     @Test
     public void testDisableClosesWindow() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
 
         // One round of disclosures
         notifier.enable(mContext);
@@ -231,10 +217,7 @@
 
     @Test
     public void testMultipleSubIdsTrackedIndependently() {
-        TestExecutorService executor = new TestExecutorService();
-        CellularIdentifierDisclosureNotifier notifier =
-                new CellularIdentifierDisclosureNotifier(
-                        executor, 15, TimeUnit.MINUTES, mSafetySource);
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
 
         notifier.enable(mContext);
         for (int i = 0; i < 3; i++) {
@@ -262,4 +245,27 @@
         assertEquals(3, notifier.getCurrentDisclosureCount(SUB_ID_1));
         assertEquals(4, notifier.getCurrentDisclosureCount(SUB_ID_2));
     }
+
+    @Test
+    public void testLogDisclsoure() {
+        String mcc = "100";
+        String mnc = "200";
+
+        CellularIdentifierDisclosureNotifier notifier = getNotifier();
+        SubscriptionInfoInternal subInfoMock = mock(SubscriptionInfoInternal.class);
+        when(mSubscriptionManagerService.getSubscriptionInfoInternal(SUB_ID_1)).thenReturn(
+                subInfoMock);
+        when(subInfoMock.getMcc()).thenReturn(mcc);
+        when(subInfoMock.getMnc()).thenReturn(mnc);
+
+        notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+
+        verify(mStats, times(1)).logIdentifierDisclosure(mDislosure, mcc, mnc,
+                mDislosure.isEmergency());
+    }
+
+    private CellularIdentifierDisclosureNotifier getNotifier() {
+        return new CellularIdentifierDisclosureNotifier(mExecutor, 15, TimeUnit.MINUTES,
+                mSafetySource, mSubscriptionManagerService, mStats);
+    }
 }