Merge "Google Contrigution item #12 - subitem #2, opt [VZW-Skylo] apply Idle Mode Scanning for Terrestrial Network" into main
diff --git a/Android.bp b/Android.bp
index c3b4373..da32208 100644
--- a/Android.bp
+++ b/Android.bp
@@ -116,4 +116,7 @@
         obfuscate: false,
         proguard_flags_files: ["proguard.flags"],
     },
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
index 4806789..825ea78 100644
--- a/flags/satellite.aconfig
+++ b/flags/satellite.aconfig
@@ -61,4 +61,12 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
+}
+
+# OWNER=rambowang TARGET=25Q2
+flag {
+  name: "satellite_state_change_listener"
+  namespace: "telephony"
+  description: "Introduce SatelliteManager APIs for carrier apps to monitor satellite state change"
+  bug: "357638490"
 }
\ No newline at end of file
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 0000000..491d013
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha08" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha08">
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IEuiccCardController permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, &quot;Requires DUMP&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/opt/telephony/src/java/com/android/internal/telephony/euicc/EuiccCardController.java"
+            line="1525"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IEuiccController permission check can be converted to @EnforcePermission annotation"
+        errorLine1="        mContext.enforceCallingPermission(Manifest.permission.MASTER_CLEAR,"
+        errorLine2="        ^">
+        <location
+            file="frameworks/opt/telephony/src/java/com/android/internal/telephony/euicc/EuiccController.java"
+            line="1679"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="IEuiccController permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, &quot;Requires DUMP&quot;);"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/opt/telephony/src/java/com/android/internal/telephony/euicc/EuiccController.java"
+            line="1812"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="SimpleManualPermissionEnforcement"
+        message="ISub permission check should be converted to @EnforcePermission annotation"
+        errorLine1="        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,"
+        errorLine2="        ^">
+        <location
+            file="frameworks/opt/telephony/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java"
+            line="4800"
+            column="9"/>
+    </issue>
+
+</issues>
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 47c701c..1aaa1d3 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -26,6 +26,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.LinkCapacityEstimate;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
@@ -340,6 +341,13 @@
                 sender.getSubId(), eligible);
     }
 
+    @Override
+    public void notifyCarrierRoamingNtnAvailableServicesChanged(
+            Phone sender, @NetworkRegistrationInfo.ServiceType int[] availableServices) {
+        mTelephonyRegistryMgr.notifyCarrierRoamingNtnAvailableServicesChanged(
+                sender.getSubId(), availableServices);
+    }
+
     /**
      * Convert the {@link Call.State} enum into the PreciseCallState.PRECISE_CALL_STATE_* constants
      * for the public API.
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 0106516..011e67b 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -66,6 +66,7 @@
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Pair;
 
@@ -759,6 +760,11 @@
             return Intents.RESULT_SMS_HANDLED;
         }
 
+        if (isMtSmsPollingMessage(smsb)) {
+            log("Received MT SMS polling message. Ignored.");
+            return Intents.RESULT_SMS_HANDLED;
+        }
+
         int result = dispatchMessageRadioSpecific(smsb, smsSource, token);
 
         // In case of error, add to metrics. This is not required in case of success, as the
@@ -1982,6 +1988,17 @@
         sendMessage(EVENT_BROADCAST_COMPLETE);
     }
 
+    private boolean isMtSmsPollingMessage(@NonNull SmsMessageBase smsb) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()
+                || !mContext.getResources().getBoolean(R.bool.config_enabled_mt_sms_polling)) {
+            return false;
+        }
+        String mtSmsPollingText = mContext.getResources()
+                .getString(R.string.config_mt_sms_polling_text);
+        return !TextUtils.isEmpty(mtSmsPollingText)
+                && mtSmsPollingText.equals(smsb.getMessageBody());
+    }
+
     /** Checks whether the flag to skip new message notification is set in the bitmask returned
      *  from the carrier app.
      */
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 61f1d45..1a8c046 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -5368,6 +5368,15 @@
         mNotifier.notifyCarrierRoamingNtnEligibleStateChanged(this, eligible);
     }
 
+    /**
+     * Notify external listeners that carrier roaming non-terrestrial available services changed.
+     * @param availableServices The list of the supported services.
+     */
+    public void notifyCarrierRoamingNtnAvailableServicesChanged(
+            @NetworkRegistrationInfo.ServiceType int[] availableServices) {
+        mNotifier.notifyCarrierRoamingNtnAvailableServicesChanged(this, availableServices);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Phone: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index a43ff72..f652370 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -26,6 +26,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.LinkCapacityEstimate;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseDataConnectionState;
@@ -164,4 +165,8 @@
 
     /** Notify eligibility to connect to carrier roaming non-terrestrial network changed. */
     void notifyCarrierRoamingNtnEligibleStateChanged(Phone sender, boolean eligible);
+
+    /** Notify carrier roaming non-terrestrial available services changed. */
+    void notifyCarrierRoamingNtnAvailableServicesChanged(
+            Phone sender, @NetworkRegistrationInfo.ServiceType int[] availableServices);
 }
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index b60ded5..023680a 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Binder;
@@ -46,6 +47,7 @@
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
@@ -65,6 +67,7 @@
 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
 import com.android.internal.telephony.satellite.DatagramDispatcher;
 import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.internal.R;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -245,13 +248,15 @@
         public final long messageId;
         public final boolean skipShortCodeCheck;
         public final long uniqueMessageId;
+        public final boolean isMtSmsPolling;
 
         public PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage,
                 int callingUser, String destAddr, String scAddr,
                 ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
                 boolean isForVvm, byte[] data, int destPort, ArrayList<String> texts,
                 Uri messageUri, boolean persistMessage, int priority, boolean expectMore,
-                int validityPeriod, long messageId, boolean skipShortCodeCheck) {
+                int validityPeriod, long messageId, boolean skipShortCodeCheck,
+                boolean isMtSmsPolling) {
             this.type = type;
             this.tracker = tracker;
             this.callingPackage = callingPackage;
@@ -278,6 +283,7 @@
             } else {
                 this.uniqueMessageId = getNextUniqueMessageId();
             }
+            this.isMtSmsPolling = isMtSmsPolling;
         }
 
         public static long getNextUniqueMessageId() {
@@ -834,7 +840,7 @@
                         null, UserHandle.USER_NULL, null, null,
                         null, null, false, null, 0,
                         null, null, false,
-                        0, false, 0, 0L, false);
+                        0, false, 0, 0L, false, false);
                 args.arg3 = "sendRetrySms";
                 sendMessage(obtainMessage(EVENT_REQUEST_DOMAIN_SELECTION, args));
                 return;
@@ -1622,7 +1628,7 @@
                             destAddr, scAddr, asArrayList(sentIntent),
                             asArrayList(deliveryIntent), isForVvm, data, destPort, null,
                             null, false, 0, false, 0,
-                            0L, false),
+                            0L, false, false),
                     "sendData");
             return;
         }
@@ -1858,7 +1864,7 @@
                 callingPkg, callingUser, destAddr, scAddr, asArrayList(sentIntent),
                 asArrayList(deliveryIntent), isForVvm, null, 0, asArrayList(text),
                 messageUri, persistMessage, priority, expectMore, validityPeriod, messageId,
-                skipShortCodeCheck);
+                skipShortCodeCheck, false);
 
         if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
             // Send P2P SMS using carrier roaming NB IOT NTN
@@ -2023,7 +2029,7 @@
         PendingRequest pendingRequest = new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null,
                 callingPkg, callingUser, destAddr, scAddr, sentIntents, deliveryIntents, false,
                 null, 0, parts, messageUri, persistMessage, priority, expectMore,
-                validityPeriod, messageId, false);
+                validityPeriod, messageId, false, false);
 
         if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
             // Send multipart P2P SMS using carrier roaming NB IOT NTN
@@ -2200,7 +2206,7 @@
      */
     public void sendCarrierRoamingNbIotNtnText(@NonNull PendingRequest request) {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
-            logd("onSendCarrierRoamingNbIotNtnTextError: carrier roaming nb iot ntn "
+            logd("sendCarrierRoamingNbIotNtnText: carrier roaming nb iot ntn "
                     + "feature flag is disabled");
             return;
         }
@@ -2228,6 +2234,42 @@
         sendMessage(obtainMessage(EVENT_SEND_TEXT_OVER_NTN_ERROR, pendingRequest));
     }
 
+    /**
+     * This API should be used only by {@link DatagramDispatcher} to send MT SMS Polling message
+     * over non-terrestrial network.
+     * To enable users to receive incoming messages, the device needs to send an MO SMS to itself
+     * to trigger SMSC to send all pending SMS to the particular subscription.
+     */
+    public void sendMtSmsPollingMessage() {
+        if (!SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
+            logd("sendMtSmsPollingMessage: not in roaming nb iot ntn");
+            return;
+        }
+
+        SubscriptionManager subscriptionManager = (SubscriptionManager) mContext
+                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        String destAddr = subscriptionManager.getPhoneNumber(mPhone.getSubId());
+        if (TextUtils.isEmpty(destAddr)) {
+            logd("sendMtSmsPollingMessage: destAddr is null or empty.");
+            return;
+        }
+
+        String mtSmsPollingText = mContext.getResources()
+                .getString(R.string.config_mt_sms_polling_text);
+        if (TextUtils.isEmpty(mtSmsPollingText)) {
+            logd("sendMtSmsPollingMessage: mtSmsPollingText is null or empty.");
+            return;
+        }
+
+        String callingPackage = mContext.getPackageName();
+        PendingRequest pendingRequest = new PendingRequest(PendingRequest.TYPE_TEXT, null,
+                callingPackage, Binder.getCallingUserHandle().getIdentifier(), destAddr,
+                getSmscAddressFromUSIMWithPhoneIdentity(callingPackage), asArrayList(null),
+                asArrayList(null), false, null, 0, asArrayList(mtSmsPollingText), null, false, 0,
+                false, 5, 0L, true, true);
+
+        DatagramDispatcher.getInstance().sendSms(pendingRequest);
+    }
 
     public interface SmsInjectionCallback {
         void onSmsInjectedResult(int result);
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 42aa2aa..1517064 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -16,9 +16,12 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE;
@@ -75,6 +78,7 @@
     private static final int EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT = 7;
     private static final int CMD_SEND_SMS = 8;
     private static final int EVENT_SEND_SMS_DONE = 9;
+    private static final int EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT = 10;
     private static final Long TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE = TimeUnit.SECONDS.toMillis(10);
     @NonNull private static DatagramDispatcher sInstance;
     @NonNull private final Context mContext;
@@ -130,6 +134,15 @@
     private int mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN;
     @Nullable private PersistentLogger mPersistentLogger = null;
 
+    @GuardedBy("mLock")
+    private int mModemState = SATELLITE_MODEM_STATE_UNKNOWN;
+    @GuardedBy("mLock")
+    private boolean mHasEnteredConnectedState = false;
+    @GuardedBy("mLock")
+    private boolean mShouldPollMtSms = false;
+    @GuardedBy("mLock")
+    private boolean mIsMtSmsPollingThrottled = false;
+
     /**
      * Create the DatagramDispatcher singleton instance.
      * @param context The Context to use to create the DatagramDispatcher.
@@ -402,6 +415,16 @@
                 break;
             }
 
+            case EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT: {
+                synchronized (mLock) {
+                    mIsMtSmsPollingThrottled = false;
+                    if (mIsAligned && mModemState == SATELLITE_MODEM_STATE_CONNECTED) {
+                        sendMtSmsPollingMessage();
+                    }
+                }
+                break;
+            }
+
             default:
                 plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
                 break;
@@ -491,6 +514,10 @@
             mIsAligned = isAligned;
             plogd("setDeviceAlignedWithSatellite: " + mIsAligned);
             if (isAligned && mIsDemoMode) handleEventSatelliteAligned();
+            if (isAligned && !mIsMtSmsPollingThrottled
+                    && mModemState == SATELLITE_MODEM_STATE_CONNECTED) {
+                sendMtSmsPollingMessage();
+            }
         }
     }
 
@@ -759,6 +786,7 @@
      */
     public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
         synchronized (mLock) {
+            mModemState = state;
             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
                     || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
                 plogd("onSatelliteModemStateChanged: cleaning up resources");
@@ -767,10 +795,22 @@
                 sendPendingMessages();
             }
 
-            if (state == SATELLITE_MODEM_STATE_CONNECTED
-                    && isDatagramWaitForConnectedStateTimerStarted()) {
-                stopDatagramWaitForConnectedStateTimer();
-                sendPendingMessages();
+            if (state == SATELLITE_MODEM_STATE_CONNECTED) {
+                mHasEnteredConnectedState = true;
+                if (isDatagramWaitForConnectedStateTimerStarted()) {
+                    stopDatagramWaitForConnectedStateTimer();
+                    sendPendingMessages();
+                }
+                if (mIsAligned && !mIsMtSmsPollingThrottled) {
+                    sendMtSmsPollingMessage();
+                }
+            }
+
+            if (state == SATELLITE_MODEM_STATE_NOT_CONNECTED) {
+                if (mHasEnteredConnectedState) {
+                    mHasEnteredConnectedState = false;
+                    mShouldPollMtSms = shouldPollMtSms();
+                }
             }
         }
     }
@@ -803,6 +843,10 @@
         mSendSatelliteDatagramRequest = null;
         mIsAligned = false;
         mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN;
+        mModemState = SATELLITE_MODEM_STATE_UNKNOWN;
+        mHasEnteredConnectedState = false;
+        mShouldPollMtSms = false;
+        stopMtSmsPollingThrottle();
     }
 
     /** @return {@code true} if already sent an emergency datagram during a session. */
@@ -1060,7 +1104,8 @@
         synchronized (mLock) {
             // Add SMS to pending list
             mPendingSmsMap.put(messageId, pendingSms);
-            int datagramType = SatelliteManager.DATAGRAM_TYPE_SMS;
+            int datagramType = pendingSms.isMtSmsPolling ?
+                    DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS;
             mLastSendRequestDatagramType = datagramType;
 
             if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) {
@@ -1106,7 +1151,9 @@
 
             mSendingInProgress = true;
             PendingRequest pendingRequest = pendingSms.iterator().next().getValue();
-            mDatagramController.updateSendStatus(subId, DATAGRAM_TYPE_SMS,
+            mDatagramController.updateSendStatus(subId,
+                    pendingRequest.isMtSmsPolling ?
+                            DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
                     getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
             sendMessage(obtainMessage(CMD_SEND_SMS, pendingRequest));
@@ -1170,8 +1217,9 @@
     private void handleEventSendSmsDone(int subId, long messageId, boolean success) {
         synchronized (mLock) {
             mSendingInProgress = false;
-            mPendingSmsMap.remove(messageId);
-            int datagramType = DATAGRAM_TYPE_SMS;
+            PendingRequest pendingSms = mPendingSmsMap.remove(messageId);
+            int datagramType = pendingSms != null && pendingSms.isMtSmsPolling
+                    ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS  : DATAGRAM_TYPE_SMS;
 
             plogd("handleEventSendSmsDone subId=" + subId + " messageId=" + messageId
                     + " success=" + success);
@@ -1180,6 +1228,9 @@
                 mDatagramController.updateSendStatus(subId, datagramType,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
                         getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
+                if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS) {
+                    startMtSmsPollingThrottle();
+                }
             } else {
                 mDatagramController.updateSendStatus(subId, datagramType,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
@@ -1196,6 +1247,67 @@
         }
     }
 
+    private boolean isEnabledMtSmsPolling() {
+        return mContext.getResources().getBoolean(R.bool.config_enabled_mt_sms_polling);
+    }
+
+    private long getMtSmsPollingThrottleMillis() {
+        return mContext.getResources().getInteger(
+                R.integer.config_mt_sms_polling_throttle_millis);
+    }
+
+    private boolean shouldPollMtSms() {
+        return isEnabledMtSmsPolling()
+                && SatelliteController.getInstance().isInCarrierRoamingNbIotNtn();
+    }
+
+    @GuardedBy("mLock")
+    private void sendMtSmsPollingMessage() {
+        if (!mShouldPollMtSms) {
+            return;
+        }
+
+        plogd("sendMtSmsPollingMessage");
+        mShouldPollMtSms = false;
+
+        for (Entry<Long, PendingRequest> entry : mPendingSmsMap.entrySet()) {
+            PendingRequest pendingRequest = entry.getValue();
+            if (pendingRequest.isMtSmsPolling) {
+                plogd("sendMtSmsPollingMessage: mPendingSmsMap already has the polling message.");
+                return;
+            }
+        }
+
+        Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone();
+        if (satellitePhone == null) {
+            ploge("sendMtSmsPollingMessage: satellitePhone is null.");
+            return;
+        }
+
+        SmsDispatchersController smsDispatchersController =
+                satellitePhone.getSmsDispatchersController();
+        if (smsDispatchersController == null) {
+            ploge("sendMtSmsPollingMessage: smsDispatchersController is null.");
+            return;
+        }
+
+        smsDispatchersController.sendMtSmsPollingMessage();
+    }
+
+    @GuardedBy("mLock")
+    private void startMtSmsPollingThrottle() {
+        plogd("startMtSmsPollingThrottle");
+        mIsMtSmsPollingThrottled = true;
+        sendMessageDelayed(obtainMessage(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT),
+                getMtSmsPollingThrottleMillis());
+    }
+
+    @GuardedBy("mLock")
+    private void stopMtSmsPollingThrottle() {
+        mIsMtSmsPollingThrottled = false;
+        removeMessages(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT);
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
index dfc7919..4b53178 100644
--- a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
+++ b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
@@ -52,7 +52,8 @@
                 logd("Registered to satellite PLMN " + satellitePlmn);
                 networkRegistrationInfo.setIsNonTerrestrialNetwork(true);
                 networkRegistrationInfo.setAvailableServices(
-                        satelliteController.getSupportedSatelliteServices(subId, satellitePlmn));
+                        satelliteController.getSupportedSatelliteServicesForPlmn(
+                                subId, satellitePlmn));
                 break;
             }
         }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 089a0ad..353bf54 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -75,6 +75,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -1741,12 +1742,13 @@
             case EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT: {
                 synchronized (mSatellitePhoneLock) {
                     mNtnEligibilityHysteresisTimedOut = true;
-                    boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
-                    plogd("EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT:"
-                            + " isCarrierRoamingNtnEligible=" + eligible);
-                    if (eligible) {
-                        requestIsSatelliteAllowedForCurrentLocation();
-                    }
+                }
+
+                boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
+                plogd("EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT:"
+                        + " isCarrierRoamingNtnEligible=" + eligible);
+                if (eligible) {
+                    requestIsSatelliteAllowedForCurrentLocation();
                 }
                 break;
             }
@@ -1817,8 +1819,8 @@
                     mIsWifiConnected = (boolean) ar.result;
                     plogd("EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: mIsWifiConnected="
                             + mIsWifiConnected);
-                    handleStateChangedForCarrierRoamingNtnEligibility();
                 }
+                handleStateChangedForCarrierRoamingNtnEligibility();
                 break;
             }
             case EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT: {
@@ -3369,7 +3371,7 @@
      * the satellite network {@code plmn}.
      */
     @NonNull
-    public List<Integer> getSupportedSatelliteServices(int subId, String plmn) {
+    public List<Integer> getSupportedSatelliteServicesForPlmn(int subId, String plmn) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             logd("getSupportedSatelliteServices: carrierEnabledSatelliteFlag is disabled");
             return new ArrayList<>();
@@ -4707,6 +4709,7 @@
             if (activeSubIds != null) {
                 for (int subId : activeSubIds) {
                     updateSupportedSatelliteServices(subId);
+                    handleCarrierRoamingNtnAvailableServicesChanged(subId);
                 }
             } else {
                 loge("updateSupportedSatelliteServicesForActiveSubscriptions: "
@@ -5465,11 +5468,11 @@
             return;
         }
 
-        synchronized (mSatellitePhoneLock) {
-            boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
-            plogd("handleStateChangedForCarrierRoamingNtnEligibility: "
-                    + "isCarrierRoamingNtnEligible=" + eligible);
+        boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
+        plogd("handleStateChangedForCarrierRoamingNtnEligibility: "
+                + "isCarrierRoamingNtnEligible=" + eligible);
 
+        synchronized (mSatellitePhoneLock) {
             if (eligible) {
                 if (shouldStartNtnEligibilityHysteresisTimer(eligible)) {
                     startNtnEligibilityHysteresisTimer();
@@ -6820,7 +6823,7 @@
             @NetworkRegistrationInfo.ServiceType int serviceType) {
         List<String> satellitePlmnList = getSatellitePlmnsForCarrier(subId);
         for (String satellitePlmn : satellitePlmnList) {
-            if (getSupportedSatelliteServices(subId, satellitePlmn).contains(serviceType)) {
+            if (getSupportedSatelliteServicesForPlmn(subId, satellitePlmn).contains(serviceType)) {
                 return true;
             }
         }
@@ -7008,7 +7011,8 @@
         return result;
     }
 
-    private String getConfigSatelliteGatewayServicePackage() {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected String getConfigSatelliteGatewayServicePackage() {
         if (!mChangeIntentComponent) {
             return getStringFromOverlayConfig(
                     R.string.config_satellite_gateway_service_package);
@@ -7079,8 +7083,10 @@
             } else {
                 mOverrideNtnEligibility.set(state);
             }
-            if (this.mSatellitePhone != null) {
-                updateLastNotifiedNtnEligibilityAndNotify(state);
+            synchronized (mSatellitePhoneLock) {
+                if (this.mSatellitePhone != null) {
+                    updateLastNotifiedNtnEligibilityAndNotify(state);
+                }
             }
         }
         return true;
@@ -7107,4 +7113,100 @@
             }
         }
     }
+
+    private void handleCarrierRoamingNtnAvailableServicesChanged(int subId) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("handleCarrierRoamingNtnAvailableServicesChanged: "
+                    + "carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+        updateLastNotifiedNtnAvailableServicesAndNotify(subId);
+    }
+
+    private void updateLastNotifiedNtnAvailableServicesAndNotify(int subId) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("notifyNtnAvailableServices: carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+        synchronized (mSatellitePhoneLock) {
+            if (mSatellitePhone == null) {
+                plogd("updateLastNotifiedNtnAvailableServicesAndNotify: phone is null");
+                return;
+            }
+            if (isSatelliteSupportedViaCarrier(subId)) {
+                int[] services = getSupportedSatelliteServicesForCarrier(subId);
+                if (isP2PSmsDisallowedOnCarrierRoamingNtn(mSatellitePhone)) {
+                    services = Arrays.stream(services).filter(
+                            value -> value != NetworkRegistrationInfo.SERVICE_TYPE_SMS).toArray();
+                }
+                mSatellitePhone.notifyCarrierRoamingNtnAvailableServicesChanged(services);
+            } else {
+                mSatellitePhone.notifyCarrierRoamingNtnAvailableServicesChanged(new int[0]);
+            }
+        }
+    }
+
+    /**
+     * Whether the P2P SMS over carrier roaming satellite is disallowed or not.
+     *
+     * @param phone phone object
+     * return {@code true} when the phone does not support P2P SMS over carrier roaming satellite
+     *        {@code false} otherwise
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isP2PSmsDisallowedOnCarrierRoamingNtn(@NonNull Phone phone) {
+        int subId = phone.getSubId();
+        int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId);
+        if (carrierRoamingNtnConnectType == CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
+            // Manual Connected
+            plogd("isP2PSmsDisallowedOnCarrierRoamingNtn: manual connect");
+            String msgPackageName = Telephony.Sms.getDefaultSmsPackage(mContext);
+            String sgPackageName = getConfigSatelliteGatewayServicePackage();
+            if (!isApplicationSupportsP2P(msgPackageName)
+                    || !isApplicationSupportsP2P(sgPackageName)) {
+                plogd("isP2PSmsSupportedOnCarrierRoamingNtn APKs do not supports P2P");
+                return true;
+            }
+        }
+        plogd("isP2PSmsDisallowedOnCarrierRoamingNtn [phoneId="
+                + phone.getPhoneId() + "]: P2P is supported");
+        return false;
+    }
+
+    @NonNull
+    private int[] getSupportedSatelliteServicesForCarrier(int subId) {
+        PersistableBundle config = getPersistableBundle(subId);
+        int[] availableServices = config.getIntArray(
+                KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY);
+        if (availableServices == null) {
+            logd("getSupportedSatelliteServicesForCarrier: defaultCapabilities is null");
+            return new int[0];
+        }
+        logd("getSupportedSatelliteServicesForCarrier: subId=" + subId
+                + ", return default values " + Arrays.toString(availableServices));
+        return availableServices;
+    }
+
+/**
+ * Whether application supports the P2P SMS to connect to carrier roaming non-terrestrial network.
+ *
+ * @param packageName application's default package name
+ * return {@code true} when the application supports P2P SMS over the roaming satellite
+ */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isApplicationSupportsP2P(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        try {
+            applicationInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            logd("isApplicationSupportsP2P pkgName: " + packageName + " is not installed.");
+        }
+        if (applicationInfo == null || applicationInfo.metaData == null) {
+            logd("isApplicationSupportsP2P pkgName: " + packageName + " meta-data info is empty.");
+            return false;
+        }
+        return applicationInfo.metaData.getBoolean(
+                SatelliteManager.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 4138df3..3d92e2a 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -40,18 +40,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AlarmManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.AsyncResult;
 import android.os.Build;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
 import android.telephony.ServiceState;
@@ -183,8 +187,15 @@
     @Nullable private PersistentLogger mPersistentLogger = null;
     @Nullable private DeviceStateMonitor mDeviceStateMonitor;
     @NonNull private SessionMetricsStats mSessionMetricsStats;
-
     @NonNull private FeatureFlags mFeatureFlags;
+    @NonNull private AlarmManager mAlarmManager;
+    private final AlarmManager.OnAlarmListener mAlarmListener = new AlarmManager.OnAlarmListener() {
+        @Override
+        public void onAlarm() {
+            plogd("onAlarm: screen off timer expired");
+            sendMessage(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
+        }
+    };
 
     /**
      * @return The singleton instance of SatelliteSessionController.
@@ -296,6 +307,7 @@
         }
         mDeviceStateMonitor = satellitePhone.getDeviceStateMonitor();
         mSessionMetricsStats = SessionMetricsStats.getInstance();
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
 
         if (mFeatureFlags.carrierRoamingNbIotNtn()) {
             // Register to received Cellular service state
@@ -594,6 +606,9 @@
         plogd("cleanUpResource");
         mIsDeviceAlignedWithSatellite = false;
         unregisterForScreenStateChanged();
+        if (mAlarmManager != null) {
+            mAlarmManager.cancel(mAlarmListener);
+        }
 
         if (mFeatureFlags.carrierRoamingNbIotNtn()) {
             // Register to received Cellular service state
@@ -621,6 +636,16 @@
         sendMessage(EVENT_SERVICE_STATE_CHANGED, new AsyncResult(null, serviceState, null));
     }
 
+    /**
+     * Uses this function to set AlarmManager object for testing.
+     *
+     * @param alarmManager The instance of AlarmManager.
+     */
+    @VisibleForTesting
+    public void setAlarmManager(AlarmManager alarmManager) {
+        mAlarmManager = alarmManager;
+    }
+
     private boolean isDemoMode() {
         return mIsDemoMode;
     }
@@ -1672,13 +1697,26 @@
         if (!screenOn) {
             // Screen off, start timer
             int timeoutMillis = getScreenOffInactivityTimeoutDurationSec() * 1000;
-            sendMessageDelayed(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT, timeoutMillis);
 
+            if (mAlarmManager == null) {
+                plogd("handleEventScreenStateChanged: can not access AlarmManager to start timer");
+                return;
+            }
+
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() + timeoutMillis,
+                    TAG, new HandlerExecutor(getHandler()), new WorkSource(), mAlarmListener);
             plogd("handleEventScreenStateChanged: start timer " + timeoutMillis);
         } else {
             // Screen on, stop timer
             removeMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
 
+            if (mAlarmManager == null) {
+                plogd("handleEventScreenStateChanged: can not access AlarmManager to stop timer");
+                return;
+            }
+
+            mAlarmManager.cancel(mAlarmListener);
             plogd("handleEventScreenStateChanged: stop timer");
         }
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index da4bf50..8720171 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -438,4 +438,15 @@
         verify(mTelephonyRegistryManager).notifyCarrierRoamingNtnEligibleStateChanged(
                 eq(subId), eq(true));
     }
+
+    @Test
+    @SmallTest
+    public void testCarrierRoamingNtnAvailableServicesChanged() {
+        int subId = mPhone.getSubId();
+        int[] testServices = {3, 6};
+        mDefaultPhoneNotifierUT.notifyCarrierRoamingNtnAvailableServicesChanged(
+                mPhone, testServices);
+        verify(mTelephonyRegistryManager).notifyCarrierRoamingNtnAvailableServicesChanged(
+                eq(subId), eq(testServices));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 9bee1ac..70bdcba 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -3503,7 +3503,7 @@
         doReturn(Arrays.asList("10123")).when(mSatelliteController).getSatellitePlmnsForCarrier(
                 anyInt());
         doReturn(satelliteSupportedServiceList).when(mSatelliteController)
-                .getSupportedSatelliteServices(sst.mSubId, "10123");
+                .getSupportedSatelliteServicesForPlmn(sst.mSubId, "10123");
 
         assertFalse(sst.mSS.isUsingNonTerrestrialNetwork());
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 8f0cbbb..53ecac3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -1391,6 +1391,6 @@
                 SmsDispatchersController.PendingRequest.TYPE_TEXT, null, "test-app",
                 mCallingUserId, "1111", "2222", asArrayList(mSentIntent), asArrayList(null),
                 false, null, 0, asArrayList("text"), null,
-                false, 0, false, 10, 100L, false);
+                false, 0, false, 10, 100L, false, false);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index ed45fac..1f5a26b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -128,6 +128,7 @@
     private long mCallbackModeDurationMillis;
     private boolean mCarrierRoamingNtnMode;
     private boolean mCarrierRoamingNtnEligible;
+    private List<Integer> mCarrierRoamingNtnAvailableServices;
 
     // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
     private static final Set<Integer> READ_PHONE_STATE_EVENTS;
@@ -333,6 +334,12 @@
             invocationCount.incrementAndGet();
             mCarrierRoamingNtnEligible = eligible;
         }
+
+        @Override
+        public void onCarrierRoamingNtnAvailableServicesChanged(List<Integer> services) {
+            invocationCount.incrementAndGet();
+            mCarrierRoamingNtnAvailableServices = services;
+        }
     }
 
     private void addTelephonyRegistryService() {
@@ -1698,4 +1705,23 @@
         processAllMessages();
         assertTrue(mCarrierRoamingNtnEligible);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void testNotifyCarrierRoamingNtnAvailableServicesChanged() {
+        int subId = INVALID_SUBSCRIPTION_ID;
+        doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+        doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+        int[] events = {TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+
+        int[] services = {3, 6};
+        mTelephonyRegistry.notifyCarrierRoamingNtnAvailableServicesChanged(subId, services);
+        processAllMessages();
+        int[] carrierServices = mCarrierRoamingNtnAvailableServices.stream()
+                .mapToInt(Integer::intValue).toArray();
+        assertTrue(Arrays.equals(carrierServices, services));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
index 142e70a..d964d88 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -1204,6 +1204,6 @@
                 SmsDispatchersController.PendingRequest.TYPE_TEXT, null, "test-app",
                 Binder.getCallingUserHandle().getIdentifier(), "1111", "2222", asArrayList(null),
                 asArrayList(null), false, null, 0, asArrayList("text"), null, false, 0, false,
-                10, 100L, false);
+                10, 100L, false, false);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
index 873078e..354b20f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
@@ -80,7 +80,7 @@
         doReturn(Arrays.asList(SATELLITE_PLMN_ARRAY))
                 .when(mMockSatelliteController).getSatellitePlmnsForCarrier(anyInt());
         doReturn(mSatelliteSupportedServiceList).when(mMockSatelliteController)
-                .getSupportedSatelliteServices(SUB_ID, SATELLITE_PLMN);
+                .getSupportedSatelliteServicesForPlmn(SUB_ID, SATELLITE_PLMN);
     }
 
     @After
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 65152d5..c043074 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -49,6 +49,7 @@
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_NEXT_VISIBILITY;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT;
 import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN;
 import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
 import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN;
@@ -115,6 +116,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.hardware.devicestate.DeviceState;
 import android.os.AsyncResult;
@@ -251,6 +254,7 @@
     @Mock private Resources mResources;
     @Mock private SubscriptionManager mSubscriptionManager;
     @Mock private SubscriptionInfo mSubscriptionInfo;
+    @Mock private PackageManager mMockPManager;
 
     private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0);
     private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() {
@@ -2016,7 +2020,7 @@
                 SUB_ID);
         assertEquals(EMPTY_STRING_ARRAY.length, satellitePlmnList.size());
         List<Integer> supportedSatelliteServices =
-                mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101");
+                mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(SUB_ID, "00101");
         assertTrue(supportedSatelliteServices.isEmpty());
 
         String[] satelliteProviderStrArray = {"00101", "00102"};
@@ -2043,7 +2047,7 @@
         satellitePlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID);
         assertTrue(satellitePlmnList.isEmpty());
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00101");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00101");
         assertTrue(supportedSatelliteServices.isEmpty());
 
         // Add entitlement provided PLMNs.
@@ -2059,16 +2063,16 @@
         processAllMessages();
 
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00102");
         assertTrue(supportedSatelliteServices.isEmpty());
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00103");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00103");
         assertTrue(supportedSatelliteServices.isEmpty());
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00104");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00104");
         assertTrue(supportedSatelliteServices.isEmpty());
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00105");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00105");
         assertTrue(supportedSatelliteServices.isEmpty());
 
         // Trigger carrier config changed with carrierEnabledSatelliteFlag enabled
@@ -2087,27 +2091,27 @@
         assertTrue(Arrays.equals(
                 expectedSupportedSatellitePlmns, satellitePlmnList.stream().toArray()));
         supportedSatelliteServices =
-                mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00102");
+                mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(SUB_ID, "00102");
         // "00101" should return carrier config assigned value, though it is in allowed list.
         assertTrue(Arrays.equals(expectedSupportedServices2,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         supportedSatelliteServices =
-                mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00103");
+                mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(SUB_ID, "00103");
         assertTrue(Arrays.equals(expectedSupportedServices3,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         // "00104", and "00105" should return default supported service.
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00104");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00104");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00105");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00105");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
@@ -2128,33 +2132,33 @@
         assertTrue(satellitePlmnList.isEmpty());
         // "00102" and "00103" should return default supported service for SUB_ID.
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00102");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00103");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00103");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         // "00104", and "00105" should return default supported service for SUB_ID.
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00104");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00104");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00105");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00105");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
 
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00102");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00102");
         assertNotNull(supportedSatelliteServices);
         assertTrue(Arrays.equals(expectedSupportedServices2,
                 supportedSatelliteServices.stream()
@@ -2162,20 +2166,20 @@
                         .toArray()));
 
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00103");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00103");
         assertTrue(Arrays.equals(expectedSupportedServices3,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         /* "00104", and "00105" should return default supported service. */
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00104");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00104");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
                         .toArray()));
         supportedSatelliteServices =
-                testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00105");
+                testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00105");
         assertTrue(Arrays.equals(defaultSupportedServices,
                 supportedSatelliteServices.stream()
                         .mapToInt(Integer::intValue)
@@ -3299,7 +3303,8 @@
         setConfigData(new ArrayList<>());
         setCarrierConfigDataPlmnList(new ArrayList<>());
         invokeCarrierConfigChanged();
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "31016");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "31016");
         assertEquals(new ArrayList<>(), servicesPerPlmn);
 
         // Verify whether the carrier config plmn list is returned with conditions below
@@ -3308,13 +3313,16 @@
         setConfigData(new ArrayList<>());
         setCarrierConfigDataPlmnList(Arrays.asList("00101", "00102", "00104"));
         invokeCarrierConfigChanged();
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00101");
         assertEquals(Arrays.asList(2).stream().sorted().toList(),
                 servicesPerPlmn.stream().sorted().toList());
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00102");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00102");
         assertEquals(Arrays.asList(1, 3).stream().sorted().toList(),
                 servicesPerPlmn.stream().sorted().toList());
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00104");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00104");
         assertEquals(Arrays.asList(2).stream().sorted().toList(),
                 servicesPerPlmn.stream().sorted().toList());
 
@@ -3324,15 +3332,19 @@
         setConfigData(Arrays.asList("00101", "00102", "31024"));
         setCarrierConfigDataPlmnList(Arrays.asList("00101", "00102", "00104"));
         invokeCarrierConfigChanged();
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00101");
         assertEquals(Arrays.asList(1).stream().sorted().toList(),
                 servicesPerPlmn.stream().sorted().toList());
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00102");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00102");
         assertEquals(Arrays.asList(3).stream().sorted().toList(),
                 servicesPerPlmn.stream().sorted().toList());
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00104");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "00104");
         assertEquals(new ArrayList<>(), servicesPerPlmn.stream().sorted().toList());
-        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "31024");
+        servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(
+                SUB_ID, "31024");
         assertEquals(Arrays.asList(5).stream().sorted().toList(),
                 servicesPerPlmn.stream().sorted().toList());
     }
@@ -4801,6 +4813,56 @@
                 eq(SUB_ID), eq(true));
     }
 
+    @Test
+    public void testIsCarrierRoamingNtnAvailableServicesForManualConnect() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
+        // CARRIER_ROAMING_NTN_CONNECT_MANUAL: 1
+        mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
+
+        mSatelliteControllerUT.setSatellitePhone(1);
+        processAllMessages();
+        when(mContext.getPackageManager()).thenReturn(mMockPManager);
+        try {
+            when(mMockPManager.getApplicationInfo(anyString(),
+                    anyInt())).thenReturn(getApplicationInfo());
+        } catch (PackageManager.NameNotFoundException e) {
+            logd("NameNotFoundException");
+        }
+        assertTrue(mSatelliteControllerUT
+                .isP2PSmsDisallowedOnCarrierRoamingNtn(mPhone));
+    }
+
+    @Test
+    public void testIsCarrierRoamingNtnAvailableServicesForAutomaticConnect() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
+        // CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC: 0
+        mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
+
+        mSatelliteControllerUT.setSatellitePhone(1);
+        processAllMessages();
+        when(mContext.getPackageManager()).thenReturn(mMockPManager);
+        try {
+            when(mMockPManager.getApplicationInfo(anyString(),
+                    anyInt())).thenReturn(getApplicationInfo());
+        } catch (PackageManager.NameNotFoundException e) {
+            logd("NameNotFoundException");
+        }
+        // If it is automatic connection case, it is not support the callback.
+        assertFalse(mSatelliteControllerUT
+                .isP2PSmsDisallowedOnCarrierRoamingNtn(mPhone));
+    }
+
+    ApplicationInfo getApplicationInfo() {
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.metaData = new Bundle();
+        applicationInfo.metaData.putBoolean(
+                METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT, true);
+        return applicationInfo;
+    }
     private void verifyProvisionStatusPerSubscriberIdGetFromDb(boolean provision) {
         doReturn(provision).when(
                 mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram(anyInt());
@@ -5558,6 +5620,7 @@
         public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE;
         public boolean setSettingsKeyToAllowDeviceRotationCalled = false;
         public OutcomeReceiver<Boolean, SatelliteException> isSatelliteAllowedCallback = null;
+        public String packageName = "com.example.app";
 
         TestSatelliteController(
                 Context context, Looper looper, @NonNull FeatureFlags featureFlags) {
@@ -5637,6 +5700,11 @@
                     "DEFAULT" /* name */).build()));
         }
 
+        @Override
+        protected String getConfigSatelliteGatewayServicePackage() {
+            return packageName;
+        }
+
         void setSatelliteProvisioned(@Nullable Boolean isProvisioned) {
             synchronized (mSatelliteViaOemProvisionLock) {
                 mIsSatelliteViaOemProvisioned = isProvisioned;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index 9518da4..eb9103a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -39,6 +39,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
@@ -50,6 +52,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AlarmManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.AsyncResult;
@@ -80,6 +83,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -117,9 +121,12 @@
     @Mock private DatagramController mMockDatagramController;
     @Mock private ServiceState mMockServiceState;
     @Mock private SessionMetricsStats mMockSessionMetricsStats;
+    @Mock private AlarmManager mAlarmManager;
 
     @Captor ArgumentCaptor<Handler> mHandlerCaptor;
     @Captor ArgumentCaptor<Integer> mMsgCaptor;
+    @Captor ArgumentCaptor<Executor> mExecutorArgumentCaptor;
+    @Captor ArgumentCaptor<AlarmManager.OnAlarmListener> mOnAlarmListenerArgumentCaptor;
 
     @Before
     public void setUp() throws Exception {
@@ -161,6 +168,7 @@
                 mTestSatelliteModemStateCallback);
         assertSuccessfulModemStateChangedCallback(
                 mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        mTestSatelliteSessionController.setAlarmManager(mAlarmManager);
     }
 
     @After
@@ -238,11 +246,18 @@
         processAllMessages();
         clearInvocations(mMockSatelliteController);
 
-        // Verify that the screen off inactivity timer is started.
-        assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
-
-        // Time shift to cause timeout
-        moveTimeForward(SCREEN_OFF_INACTIVITY_TIMEOUT_SEC * 1000);
+        // Verify that the screen off inactivity timer is set.
+        verify(mAlarmManager).setExact(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                anyLong(),
+                anyString(),
+                mExecutorArgumentCaptor.capture(),
+                any(),
+                mOnAlarmListenerArgumentCaptor.capture()
+        );
+        // Notify alarm expired
+        mExecutorArgumentCaptor.getValue().execute(
+                () -> mOnAlarmListenerArgumentCaptor.getValue().onAlarm());
         processAllMessages();
 
         // Verify that SatelliteController#requestSatelliteEnabled() was called.
@@ -288,15 +303,23 @@
         sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false);
         processAllMessages();
 
-        // Verify that the screen off inactivity timer is started.
-        assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
+        // Verify that the screen off inactivity timer is set.
+        verify(mAlarmManager).setExact(
+                eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                anyLong(),
+                anyString(),
+                mExecutorArgumentCaptor.capture(),
+                any(),
+                mOnAlarmListenerArgumentCaptor.capture()
+        );
 
         // Notify Screen on
         sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), true);
+
         processAllMessages();
 
-        // Verify that the screen off inactivity timer is stopped
-        assertFalse(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
+        // Verify that the screen off inactivity timer is clear.
+        verify(mAlarmManager).cancel(eq(mOnAlarmListenerArgumentCaptor.getValue()));
     }
 
     @Test
@@ -2036,10 +2059,6 @@
             return hasDeferredMessages(event);
         }
 
-        boolean isScreenOffInActivityTimerStarted() {
-            return hasMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
-        }
-
         protected boolean isSatelliteEnabledForNtnOnlySubscription() {
             return mSatelliteEnabledForNtnOnlySubscription;
         }