Merge "Add satellite log" into 24D1-dev
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 46a2400..3847fcd 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -682,6 +682,7 @@
     optional int32 count_of_incoming_datagram_success = 10;
     optional int32 count_of_incoming_datagram_failed = 11;
     optional bool is_demo_mode = 12;
+    optional int32 max_ntn_signal_strength_level = 13;
 }
 
 message SatelliteIncomingDatagram {
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 61325316..0031c7e 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -1370,7 +1370,8 @@
                 satelliteSession.countOfOutgoingDatagramFailed,
                 satelliteSession.countOfIncomingDatagramSuccess,
                 satelliteSession.countOfIncomingDatagramFailed,
-                satelliteSession.isDemoMode);
+                satelliteSession.isDemoMode,
+                satelliteSession.maxNtnSignalStrengthLevel);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteIncomingDatagram stats) {
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 979777b..8afdc36 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -2066,7 +2066,7 @@
     }
 
     /**
-     * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+     * Returns SatelliteSession atom that has same values or {@code null}
      * if it does not exist.
      */
     private @Nullable SatelliteSession find(
@@ -2085,7 +2085,8 @@
                     && stats.countOfOutgoingDatagramFailed == key.countOfOutgoingDatagramFailed
                     && stats.countOfIncomingDatagramSuccess == key.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed == key.countOfIncomingDatagramFailed
-                    && stats.isDemoMode == key.isDemoMode) {
+                    && stats.isDemoMode == key.isDemoMode
+                    && stats.maxNtnSignalStrengthLevel == key.maxNtnSignalStrengthLevel) {
                 return stats;
             }
         }
@@ -2093,7 +2094,7 @@
     }
 
     /**
-     * Returns SatelliteOutgoingDatagram atom that has same values or {@code null}
+     * Returns SatelliteSosMessageRecommender atom that has same values or {@code null}
      * if it does not exist.
      */
     private @Nullable SatelliteSosMessageRecommender find(
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
index a9aab58..e097c62 100644
--- a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.metrics;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
 
 import com.android.internal.telephony.PhoneFactory;
@@ -560,6 +563,7 @@
         private final int mCountOfIncomingDatagramSuccess;
         private final int mCountOfIncomingDatagramFailed;
         private final boolean mIsDemoMode;
+        private final @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
 
         private SatelliteSessionParams(Builder builder) {
             this.mSatelliteServiceInitializationResult =
@@ -575,6 +579,7 @@
             this.mCountOfIncomingDatagramSuccess = builder.mCountOfIncomingDatagramSuccess;
             this.mCountOfIncomingDatagramFailed = builder.mCountOfIncomingDatagramFailed;
             this.mIsDemoMode = builder.mIsDemoMode;
+            this.mMaxNtnSignalStrengthLevel = builder.mMaxNtnSignalStrengthLevel;
         }
 
         public int getSatelliteServiceInitializationResult() {
@@ -621,6 +626,10 @@
             return mIsDemoMode;
         }
 
+        public @NtnSignalStrength.NtnSignalStrengthLevel int getMaxNtnSignalStrengthLevel() {
+            return mMaxNtnSignalStrengthLevel;
+        }
+
         /**
          * A builder class to create {@link SatelliteSessionParams} data structure class
          */
@@ -636,6 +645,8 @@
             private int mCountOfIncomingDatagramSuccess = -1;
             private int mCountOfIncomingDatagramFailed = -1;
             private boolean mIsDemoMode = false;
+            private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel =
+                    NTN_SIGNAL_STRENGTH_NONE;
 
             /**
              * Sets satelliteServiceInitializationResult value of {@link SatelliteSession}
@@ -711,6 +722,13 @@
                 return this;
             }
 
+            /** Sets the max ntn signal strength for the satellite session */
+            public Builder setMaxNtnSignalStrengthLevel(
+                    @NtnSignalStrength.NtnSignalStrengthLevel int maxNtnSignalStrengthLevel) {
+                this.mMaxNtnSignalStrengthLevel = maxNtnSignalStrengthLevel;
+                return this;
+            }
+
             /**
              * Returns SessionParams, which contains whole component of
              * {@link SatelliteSession} atom
@@ -735,6 +753,7 @@
                     + ", CountOfIncomingDatagramSuccess=" + mCountOfIncomingDatagramSuccess
                     + ", CountOfIncomingDatagramFailed=" + mCountOfIncomingDatagramFailed
                     + ", IsDemoMode=" + mIsDemoMode
+                    + ", MaxNtnSignalStrengthLevel=" + mMaxNtnSignalStrengthLevel
                     + ")";
         }
     }
@@ -1261,6 +1280,7 @@
         proto.countOfIncomingDatagramSuccess = param.getCountOfIncomingDatagramSuccess();
         proto.countOfIncomingDatagramFailed = param.getCountOfOutgoingDatagramFailed();
         proto.isDemoMode = param.getIsDemoMode();
+        proto.maxNtnSignalStrengthLevel = param.getMaxNtnSignalStrengthLevel();
         mAtomsStorage.addSatelliteSessionStats(proto);
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index dbb586d..2f18796 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -29,6 +29,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.Looper;
 import android.os.SystemProperties;
@@ -69,6 +70,8 @@
     public static final int TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE = 3;
     /** This type is used by CTS to override the time to datagram delay in demo mode */
     public static final int TIMEOUT_TYPE_DATAGRAM_DELAY_IN_DEMO_MODE = 4;
+    /** This type is used by CTS to override wait for device alignment in demo datagram boolean */
+    public static final int BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM = 1;
     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
     private static final boolean DEBUG = !"user".equals(Build.TYPE);
 
@@ -101,6 +104,7 @@
     private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
     private long mDatagramWaitTimeForConnectedState;
     private long mModemImageSwitchingDuration;
+    private boolean mWaitForDeviceAlignmentInDemoDatagram;
     @GuardedBy("mLock")
     @SatelliteManager.SatelliteModemState
     private int mSatelltieModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
@@ -155,6 +159,8 @@
 
         mDatagramWaitTimeForConnectedState = getDatagramWaitForConnectedStateTimeoutMillis();
         mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis();
+        mWaitForDeviceAlignmentInDemoDatagram =
+                getWaitForDeviceAlignmentInDemoDatagramFromResources();
         mDemoModeDatagramList = new ArrayList<>();
     }
 
@@ -492,6 +498,36 @@
         return true;
     }
 
+    /**
+     * This API can be used by only CTS to override the boolean configs used by the
+     * DatagramController module.
+     *
+     * @param enable Whether to enable or disable boolean config.
+     * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+     */
+    boolean setDatagramControllerBooleanConfig(
+            boolean reset, int booleanType, boolean enable) {
+        if (!isMockModemAllowed()) {
+            loge("Updating boolean config is not allowed");
+            return false;
+        }
+
+        logd("setDatagramControllerTimeoutDuration: booleanType=" + booleanType
+                + ", reset=" + reset + ", enable=" + enable);
+        if (booleanType == BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM) {
+            if (reset) {
+                mWaitForDeviceAlignmentInDemoDatagram =
+                        getWaitForDeviceAlignmentInDemoDatagramFromResources();
+            } else {
+                mWaitForDeviceAlignmentInDemoDatagram = enable;
+            }
+        } else {
+            loge("Invalid boolean type " + booleanType);
+            return false;
+        }
+        return true;
+    }
+
     private boolean isMockModemAllowed() {
         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
     }
@@ -546,6 +582,38 @@
         }
     }
 
+    /**
+     * Get whether to wait for device alignment with satellite before sending datagrams.
+     *
+     * @param isAligned if the device is aligned with satellite or not
+     * @return {@code true} if device is not aligned to satellite,
+     * and it is required to wait for alignment else {@code false}
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean waitForAligningToSatellite(boolean isAligned) {
+        if (isAligned) {
+            return false;
+        }
+
+        return getWaitForDeviceAlignmentInDemoDatagram();
+    }
+
+    private boolean getWaitForDeviceAlignmentInDemoDatagram() {
+        return mWaitForDeviceAlignmentInDemoDatagram;
+    }
+
+    private boolean getWaitForDeviceAlignmentInDemoDatagramFromResources() {
+        boolean waitForDeviceAlignmentInDemoDatagram = false;
+        try {
+            waitForDeviceAlignmentInDemoDatagram = mContext.getResources().getBoolean(
+                    R.bool.config_wait_for_device_alignment_in_demo_datagram);
+        } catch (Resources.NotFoundException ex) {
+            loge("getWaitForDeviceAlignmentInDemoDatagram: ex=" + ex);
+        }
+
+        return waitForDeviceAlignmentInDemoDatagram;
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 4fc8d55..f764b2b 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -239,7 +239,7 @@
                     if (mIsDemoMode && (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)) {
                         if (argument.skipCheckingSatelliteAligned) {
                             logd("Satellite was already aligned. No need to check alignment again");
-                        } else if (!mIsAligned) {
+                        } else if (mDatagramController.waitForAligningToSatellite(mIsAligned)) {
                             logd("Satellite is not aligned in demo mode, wait for the alignment.");
                             startSatelliteAlignedTimer(request);
                             break;
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index 145b017..f1f0fde 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -620,7 +620,7 @@
             DatagramReceiverHandlerRequest request = new DatagramReceiverHandlerRequest(
                     callback, phone, subId);
             synchronized (mLock) {
-                if (mIsAligned) {
+                if (!mDatagramController.waitForAligningToSatellite(mIsAligned)) {
                     Message msg = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
                             request);
                     AsyncResult.forMessage(msg, null, null);
diff --git a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
new file mode 100644
index 0000000..3c31ae8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
@@ -0,0 +1,329 @@
+/*
+ * 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.satellite;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.IIntegerConsumer;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NtnSignalStrength;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.telephony.satellite.stub.SatelliteResult;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+public class DemoSimulator extends StateMachine {
+    private static final String TAG = "DemoSimulator";
+    private static final boolean DBG = true;
+
+    private static final int EVENT_SATELLITE_MODE_ON = 1;
+    private static final int EVENT_SATELLITE_MODE_OFF = 2;
+    private static final int EVENT_DEVICE_ALIGNED_WITH_SATELLITE = 3;
+    protected static final int EVENT_DEVICE_ALIGNED = 4;
+    protected static final int EVENT_DEVICE_NOT_ALIGNED = 5;
+
+    @NonNull private static DemoSimulator sInstance;
+
+    @NonNull private final Context mContext;
+    @NonNull private final SatelliteController mSatelliteController;
+    @NonNull private final PowerOffState mPowerOffState = new PowerOffState();
+    @NonNull private final NotConnectedState mNotConnectedState = new NotConnectedState();
+    @NonNull private final ConnectedState mConnectedState = new ConnectedState();
+    @NonNull private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private boolean mIsAligned = false;
+    private ISatelliteListener mISatelliteListener;
+
+    /**
+     * @return The singleton instance of DemoSimulator.
+     */
+    public static DemoSimulator getInstance() {
+        if (sInstance == null) {
+            Log.e(TAG, "DemoSimulator was not yet initialized.");
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create the DemoSimulator singleton instance.
+     *
+     * @param context The Context for the DemoSimulator.
+     * @return The singleton instance of DemoSimulator.
+     */
+    public static DemoSimulator make(@NonNull Context context,
+            @NonNull SatelliteController satelliteController) {
+        if (sInstance == null) {
+            sInstance = new DemoSimulator(context, Looper.getMainLooper(), satelliteController);
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create a DemoSimulator.
+     *
+     * @param context The Context for the DemoSimulator.
+     * @param looper The looper associated with the handler of this class.
+     */
+    protected DemoSimulator(@NonNull Context context, @NonNull Looper looper,
+            @NonNull SatelliteController satelliteController) {
+        super(TAG, looper);
+
+        mContext = context;
+        mSatelliteController = satelliteController;
+        addState(mPowerOffState);
+        addState(mNotConnectedState);
+        addState(mConnectedState);
+        setInitialState(mPowerOffState);
+        start();
+    }
+
+    private class PowerOffState extends State {
+        @Override
+        public void enter() {
+            logd("Entering PowerOffState");
+        }
+
+        @Override
+        public void exit() {
+            logd("Exiting PowerOffState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("PowerOffState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODE_ON:
+                    transitionTo(mNotConnectedState);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+    }
+
+    private class NotConnectedState extends State {
+        @Override
+        public void enter() {
+            logd("Entering NotConnectedState");
+
+            try {
+                NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
+                ntnSignalStrength.signalStrengthLevel = 0;
+                mISatelliteListener.onSatelliteModemStateChanged(
+                        SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+                mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
+
+                synchronized (mLock) {
+                    if (mIsAligned) {
+                        handleEventDeviceAlignedWithSatellite(true);
+                    }
+                }
+            } catch (RemoteException e) {
+                loge("NotConnectedState: RemoteException " + e);
+            }
+        }
+
+        @Override
+        public void exit() {
+            logd("Exiting NotConnectedState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("NotConnectedState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODE_OFF:
+                    transitionTo(mPowerOffState);
+                    break;
+                case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+                    handleEventDeviceAlignedWithSatellite((boolean) msg.obj);
+                    break;
+                case EVENT_DEVICE_ALIGNED:
+                    transitionTo(mConnectedState);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+
+        private void handleEventDeviceAlignedWithSatellite(boolean isAligned) {
+            if (isAligned && !hasMessages(EVENT_DEVICE_ALIGNED)) {
+                long durationMillis = mSatelliteController.getDemoPointingAlignedDurationMillis();
+                logd("NotConnectedState: handleEventAlignedWithSatellite isAligned=true."
+                        + " Send delayed EVENT_DEVICE_ALIGNED message in"
+                        + " durationMillis=" + durationMillis);
+                sendMessageDelayed(EVENT_DEVICE_ALIGNED, durationMillis);
+            } else if (!isAligned && hasMessages(EVENT_DEVICE_ALIGNED)) {
+                logd("NotConnectedState: handleEventAlignedWithSatellite isAligned=false."
+                        + " Remove EVENT_DEVICE_ALIGNED message.");
+                removeMessages(EVENT_DEVICE_ALIGNED);
+            }
+        }
+    }
+
+    private class ConnectedState extends State {
+        @Override
+        public void enter() {
+            logd("Entering ConnectedState");
+
+            try {
+                NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
+                ntnSignalStrength.signalStrengthLevel = 2;
+                mISatelliteListener.onSatelliteModemStateChanged(
+                        SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+                mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
+
+                synchronized (mLock) {
+                    if (!mIsAligned) {
+                        handleEventDeviceAlignedWithSatellite(false);
+                    }
+                }
+            } catch (RemoteException e) {
+                loge("ConnectedState: RemoteException " + e);
+            }
+        }
+
+        @Override
+        public void exit() {
+            logd("Exiting ConnectedState");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("ConnectedState: processing " + getWhatToString(msg.what));
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODE_OFF:
+                    transitionTo(mPowerOffState);
+                    break;
+                case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+                    handleEventDeviceAlignedWithSatellite((boolean) msg.obj);
+                    break;
+                case EVENT_DEVICE_NOT_ALIGNED:
+                    transitionTo(mNotConnectedState);
+                    break;
+            }
+            // Ignore all unexpected events.
+            return HANDLED;
+        }
+
+        private void handleEventDeviceAlignedWithSatellite(boolean isAligned) {
+            if (!isAligned && !hasMessages(EVENT_DEVICE_NOT_ALIGNED)) {
+                long durationMillis =
+                        mSatelliteController.getDemoPointingNotAlignedDurationMillis();
+                logd("ConnectedState: handleEventAlignedWithSatellite isAligned=false."
+                        + " Send delayed EVENT_DEVICE_NOT_ALIGNED message"
+                        + " in durationMillis=" + durationMillis);
+                sendMessageDelayed(EVENT_DEVICE_NOT_ALIGNED, durationMillis);
+            } else if (isAligned && hasMessages(EVENT_DEVICE_NOT_ALIGNED)) {
+                logd("ConnectedState: handleEventAlignedWithSatellite isAligned=true."
+                        + " Remove EVENT_DEVICE_NOT_ALIGNED message.");
+                removeMessages(EVENT_DEVICE_NOT_ALIGNED);
+            }
+        }
+    }
+
+    /**
+     * @return the string for msg.what
+     */
+    @Override
+    protected String getWhatToString(int what) {
+        String whatString;
+        switch (what) {
+            case EVENT_SATELLITE_MODE_ON:
+                whatString = "EVENT_SATELLITE_MODE_ON";
+                break;
+            case EVENT_SATELLITE_MODE_OFF:
+                whatString = "EVENT_SATELLITE_MODE_OFF";
+                break;
+            case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+                whatString = "EVENT_DEVICE_ALIGNED_WITH_SATELLITE";
+                break;
+            case EVENT_DEVICE_ALIGNED:
+                whatString = "EVENT_DEVICE_ALIGNED";
+                break;
+            case EVENT_DEVICE_NOT_ALIGNED:
+                whatString = "EVENT_DEVICE_NOT_ALIGNED";
+                break;
+            default:
+                whatString = "UNKNOWN EVENT " + what;
+        }
+        return whatString;
+    }
+
+    /**
+     * Register the callback interface with satellite service.
+     *
+     * @param listener The callback interface to handle satellite service indications.
+     */
+    public void setSatelliteListener(@NonNull ISatelliteListener listener) {
+        mISatelliteListener = listener;
+    }
+
+    /**
+     * Allow cellular modem scanning while satellite mode is on.
+     *
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     *                             and {@code false} to disable
+     * @param errorCallback The callback to receive the error code result of the operation.
+     */
+    public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+            @NonNull IIntegerConsumer errorCallback) {
+        try {
+            errorCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS);
+        } catch (RemoteException e) {
+            loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
+        }
+    }
+
+    /**
+     * This function is used by {@link SatelliteSessionController} to notify {@link DemoSimulator}
+     * that satellite mode is ON.
+     */
+    public void onSatelliteModeOn() {
+        if (mSatelliteController.isDemoModeEnabled()) {
+            sendMessage(EVENT_SATELLITE_MODE_ON);
+        }
+    }
+
+    /**
+     * This function is used by {@link SatelliteSessionController} to notify {@link DemoSimulator}
+     * that satellite mode is OFF.
+     */
+    public void onSatelliteModeOff() {
+        sendMessage(EVENT_SATELLITE_MODE_OFF);
+    }
+
+    /**
+     * Set whether the device is aligned with the satellite.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void setDeviceAlignedWithSatellite(boolean isAligned) {
+        synchronized (mLock) {
+            if (mSatelliteController.isDemoModeEnabled()) {
+                mIsAligned = isAligned;
+                sendMessage(EVENT_DEVICE_ALIGNED_WITH_SATELLITE, isAligned);
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 1cc2977..35603ab 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -162,6 +162,10 @@
      * to enable satellite.
      */
     public static final int TIMEOUT_TYPE_WAIT_FOR_SATELLITE_ENABLING_RESPONSE = 1;
+    /** This is used by CTS to override demo pointing aligned duration. */
+    public static final int TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS = 2;
+    /** This is used by CTS to override demo pointing not aligned duration. */
+    public static final int TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS = 3;
 
     /** Key used to read/write OEM-enabled satellite provision status in shared preferences. */
     private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY =
@@ -214,7 +218,8 @@
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
     @NonNull private final SatelliteModemInterface mSatelliteModemInterface;
-    @NonNull private SatelliteSessionController mSatelliteSessionController;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @NonNull protected SatelliteSessionController mSatelliteSessionController;
     @NonNull private final PointingAppController mPointingAppController;
     @NonNull private final DatagramController mDatagramController;
     @NonNull private final ControllerMetricsStats mControllerMetricsStats;
@@ -401,6 +406,8 @@
     private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
     private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0);
     private long mWaitTimeForSatelliteEnablingResponse;
+    private long mDemoPointingAlignedDurationMillis;
+    private long mDemoPointingNotAlignedDurationMillis;
 
     /** Key used to read/write satellite system notification done in shared preferences. */
     private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
@@ -528,6 +535,9 @@
                 null);
         loadSatelliteSharedPreferences();
         mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis();
+        mDemoPointingAlignedDurationMillis = getDemoPointingAlignedDurationMillisFromResources();
+        mDemoPointingNotAlignedDurationMillis =
+                getDemoPointingNotAlignedDurationMillisFromResources();
     }
 
     /**
@@ -2046,6 +2056,8 @@
             logd("setDeviceAlignedWithSatellite: oemEnabledSatelliteFlag is disabled");
             return;
         }
+
+        DemoSimulator.getInstance().setDeviceAlignedWithSatellite(isAligned);
         mDatagramController.setDeviceAlignedWithSatellite(isAligned);
     }
 
@@ -2376,6 +2388,25 @@
     }
 
     /**
+     * This API can be used by only CTS to override the boolean configs used by the
+     * DatagramController module.
+     *
+     * @param enable Whether to enable or disable boolean config.
+     * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+     */
+    public boolean setDatagramControllerBooleanConfig(
+            boolean reset, int booleanType, boolean enable) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("setDatagramControllerBooleanConfig: oemEnabledSatelliteFlag is disabled");
+            return false;
+        }
+        logd("setDatagramControllerBooleanConfig: reset=" + reset + ", booleanType="
+                + booleanType + ", enable=" + enable);
+        return mDatagramController.setDatagramControllerBooleanConfig(
+                reset, booleanType, enable);
+    }
+
+    /**
      * This API can be used by only CTS to override timeout durations used by SatelliteController
      * module.
      *
@@ -2402,6 +2433,20 @@
                 mWaitTimeForSatelliteEnablingResponse = timeoutMillis;
             }
             logd("mWaitTimeForSatelliteEnablingResponse=" + mWaitTimeForSatelliteEnablingResponse);
+        } else if (timeoutType == TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS) {
+            if (reset) {
+                mDemoPointingAlignedDurationMillis =
+                        getDemoPointingAlignedDurationMillisFromResources();
+            } else {
+                mDemoPointingAlignedDurationMillis = timeoutMillis;
+            }
+        } else if (timeoutType == TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS) {
+            if (reset) {
+                mDemoPointingNotAlignedDurationMillis =
+                        getDemoPointingNotAlignedDurationMillisFromResources();
+            } else {
+                mDemoPointingNotAlignedDurationMillis = timeoutMillis;
+            }
         } else {
             logw("Invalid timeoutType=" + timeoutType);
             return false;
@@ -3198,6 +3243,9 @@
         }
         mSatelliteSessionController = SatelliteSessionController.make(
                 mContext, getLooper(), supported);
+        logd("create a new SatelliteSessionController due to isSatelliteSupported state has "
+                + "changed to " + supported);
+
         if (supported) {
             registerForSatelliteProvisionStateChanged();
             registerForPendingDatagramCount();
@@ -3386,6 +3434,7 @@
         synchronized (mNtnSignalsStrengthLock) {
             mNtnSignalStrength = ntnSignalStrength;
         }
+        mSessionMetricsStats.updateMaxNtnSignalStrengthLevel(ntnSignalStrength.getLevel());
 
         List<INtnSignalStrengthCallback> deadCallersList = new ArrayList<>();
         mNtnSignalStrengthChangedListeners.values().forEach(listener -> {
@@ -3458,6 +3507,10 @@
                 }
             }
             mIsSatelliteSupported = supported;
+            mSatelliteSessionController = SatelliteSessionController.make(
+                    mContext, getLooper(), supported);
+            logd("create a new SatelliteSessionController due to isSatelliteSupported state has "
+                    + "changed to " + supported);
         }
 
         List<ISatelliteSupportedStateCallback> deadCallersList = new ArrayList<>();
@@ -4568,6 +4621,40 @@
         }
     }
 
+    private long getDemoPointingAlignedDurationMillisFromResources() {
+        long durationMillis = 15000L;
+        try {
+            durationMillis = mContext.getResources().getInteger(
+                    R.integer.config_demo_pointing_aligned_duration_millis);
+        } catch (Resources.NotFoundException ex) {
+            loge("getPointingAlignedDurationMillis: ex=" + ex);
+        }
+
+        return durationMillis;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public long getDemoPointingAlignedDurationMillis() {
+        return mDemoPointingAlignedDurationMillis;
+    }
+
+    private long getDemoPointingNotAlignedDurationMillisFromResources() {
+        long durationMillis = 30000L;
+        try {
+            durationMillis = mContext.getResources().getInteger(
+                    R.integer.config_demo_pointing_not_aligned_duration_millis);
+        } catch (Resources.NotFoundException ex) {
+            loge("getPointingNotAlignedDurationMillis: ex=" + ex);
+        }
+
+        return durationMillis;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public long getDemoPointingNotAlignedDurationMillis() {
+        return mDemoPointingNotAlignedDurationMillis;
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 2e99ae6..8a26fd2 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -42,6 +42,7 @@
 import android.telephony.satellite.stub.ISatellite;
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
 import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.SatelliteModemState;
 import android.telephony.satellite.stub.SatelliteService;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -64,6 +65,9 @@
 
     @NonNull private static SatelliteModemInterface sInstance;
     @NonNull private final Context mContext;
+    @NonNull private final DemoSimulator mDemoSimulator;
+    @NonNull private final SatelliteListener mVendorListener;
+    @NonNull private final SatelliteListener mDemoListener;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     @NonNull protected final ExponentialBackoff mExponentialBackoff;
     @NonNull private final Object mLock = new Object();
@@ -96,7 +100,14 @@
     @NonNull private final RegistrantList mSatelliteSupportedStateChangedRegistrants =
             new RegistrantList();
 
-    @NonNull private final ISatelliteListener mListener = new ISatelliteListener.Stub() {
+    private class SatelliteListener extends ISatelliteListener.Stub {
+
+        private final boolean mIsDemoListener;
+
+        SatelliteListener(boolean isDemoListener) {
+            mIsDemoListener = isDemoListener;
+        }
+
         @Override
         public void onSatelliteProvisionStateChanged(boolean provisioned) {
             mSatelliteProvisionStateChangedRegistrants.notifyResult(provisioned);
@@ -105,15 +116,19 @@
         @Override
         public void onSatelliteDatagramReceived(
                 android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
-            logd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
-            mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
-                    SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+            if (notifyResultIfExpectedListener()) {
+                logd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
+                mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
+                        SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+            }
         }
 
         @Override
         public void onPendingDatagrams() {
-            logd("onPendingDatagrams");
-            mPendingDatagramsRegistrants.notifyResult(null);
+            if (notifyResultIfExpectedListener()) {
+                logd("onPendingDatagrams");
+                mPendingDatagramsRegistrants.notifyResult(null);
+            }
         }
 
         @Override
@@ -125,33 +140,39 @@
 
         @Override
         public void onSatelliteModemStateChanged(int state) {
-            mSatelliteModemStateChangedRegistrants.notifyResult(
-                    SatelliteServiceUtils.fromSatelliteModemState(state));
-            int datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
-            switch (state) {
-                case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
-                    datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
-                    break;
-                case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
-                    datagramTransferState =
-                            SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
-                    break;
-                case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
-                    datagramTransferState =
-                            SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
-                    break;
-                case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
-                    // keep previous state as this could be retrying sending or receiving
-                    break;
+            if (notifyModemStateChanged(state)) {
+                mSatelliteModemStateChangedRegistrants.notifyResult(
+                        SatelliteServiceUtils.fromSatelliteModemState(state));
+                int datagramTransferState =
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
+                switch (state) {
+                    case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
+                        datagramTransferState =
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+                        break;
+                    case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
+                        datagramTransferState =
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
+                        break;
+                    case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
+                        datagramTransferState =
+                                SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
+                        break;
+                    case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
+                        // keep previous state as this could be retrying sending or receiving
+                        break;
+                }
+                mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
             }
-            mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
         }
 
         @Override
         public void onNtnSignalStrengthChanged(
                 android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) {
-            mNtnSignalStrengthChangedRegistrants.notifyResult(
-                    SatelliteServiceUtils.fromNtnSignalStrength(ntnSignalStrength));
+            if (notifyResultIfExpectedListener()) {
+                mNtnSignalStrengthChangedRegistrants.notifyResult(
+                        SatelliteServiceUtils.fromNtnSignalStrength(ntnSignalStrength));
+            }
         }
 
         @Override
@@ -165,7 +186,22 @@
         public void onSatelliteSupportedStateChanged(boolean supported) {
             mSatelliteSupportedStateChangedRegistrants.notifyResult(supported);
         }
-    };
+
+        private boolean notifyResultIfExpectedListener() {
+            // Demo listener should notify results only during demo mode
+            // Vendor listener should notify result only during real mode
+            return mIsDemoListener == mSatelliteController.isDemoModeEnabled();
+        }
+
+        private boolean notifyModemStateChanged(int state) {
+            if (notifyResultIfExpectedListener()) {
+                return true;
+            }
+
+            return state == SatelliteModemState.SATELLITE_MODEM_STATE_OFF
+                    || state == SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE;
+        }
+    }
 
     /**
      * @return The singleton instance of SatelliteModemInterface.
@@ -202,6 +238,9 @@
     protected SatelliteModemInterface(@NonNull Context context,
             SatelliteController satelliteController, @NonNull Looper looper) {
         mContext = context;
+        mDemoSimulator = DemoSimulator.make(context, satelliteController);
+        mVendorListener = new SatelliteListener(false);
+        mDemoListener = new SatelliteListener(true);
         mIsSatelliteServiceSupported = getSatelliteServiceSupport();
         mSatelliteController = satelliteController;
         mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
@@ -314,7 +353,8 @@
             mSatelliteService = ISatellite.Stub.asInterface(service);
             mExponentialBackoff.stop();
             try {
-                mSatelliteService.setSatelliteListener(mListener);
+                mSatelliteService.setSatelliteListener(mVendorListener);
+                mDemoSimulator.setSatelliteListener(mDemoListener);
             } catch (RemoteException e) {
                 // TODO: Retry setSatelliteListener
                 logd("setSatelliteListener: RemoteException " + e);
@@ -584,19 +624,26 @@
             @Nullable Message message) {
         if (mSatelliteService != null) {
             try {
-                mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(enabled,
-                        new IIntegerConsumer.Stub() {
-                            @Override
-                            public void accept(int result) {
-                                int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                logd("enableCellularModemWhileSatelliteModeIsOn: " + error);
-                                Binder.withCleanCallingIdentity(() -> {
-                                        if (message != null) {
-                                            sendMessageWithResult(message, null, error);
-                                        }
-                                });
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        int error = SatelliteServiceUtils.fromSatelliteError(result);
+                        logd("enableCellularModemWhileSatelliteModeIsOn: " + error);
+                        Binder.withCleanCallingIdentity(() -> {
+                            if (message != null) {
+                                sendMessageWithResult(message, null, error);
                             }
                         });
+                    }
+                };
+
+                if (mSatelliteController.isDemoModeEnabled()) {
+                    mDemoSimulator.enableCellularModemWhileSatelliteModeIsOn(
+                            enabled, errorCallback);
+                } else {
+                    mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(
+                            enabled, errorCallback);
+                }
             } catch (RemoteException e) {
                 loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
                 if (message != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index cd3c05b..dcf9bb0 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -39,7 +39,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.telephony.Rlog;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.stub.ISatelliteGateway;
@@ -163,15 +162,9 @@
      */
     public static SatelliteSessionController make(
             @NonNull Context context, @NonNull Looper looper, boolean isSatelliteSupported) {
-        if (sInstance == null) {
+        if (sInstance == null || isSatelliteSupported != sInstance.mIsSatelliteSupported) {
             sInstance = new SatelliteSessionController(context, looper, isSatelliteSupported,
                     SatelliteModemInterface.getInstance());
-        } else {
-            if (isSatelliteSupported != sInstance.mIsSatelliteSupported) {
-                Rlog.e(TAG, "New satellite support state " + isSatelliteSupported
-                        + " is different from existing state " + sInstance.mIsSatelliteSupported
-                        + ". Ignore the new state.");
-            }
         }
         return sInstance;
     }
@@ -450,6 +443,7 @@
             }
             unbindService();
             stopNbIotInactivityTimer();
+            DemoSimulator.getInstance().onSatelliteModeOff();
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
         }
 
@@ -520,6 +514,7 @@
                 } else {
                     transitionTo(mIdleState);
                 }
+                DemoSimulator.getInstance().onSatelliteModeOn();
             } else {
                 /*
                  * During the state transition from ENABLING to NOT_CONNECTED, modem might be
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
index c50db07..73ede60 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -16,7 +16,10 @@
 
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+
 import android.annotation.NonNull;
+import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
 import android.util.Log;
 
@@ -41,7 +44,7 @@
     private int mCountOfSuccessfulIncomingDatagram;
     private int mCountOfIncomingDatagramFailed;
     private boolean mIsDemoMode;
-
+    private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
 
     private SessionMetricsStats() {
         initializeSessionMetricsParam();
@@ -144,6 +147,17 @@
         return this;
     }
 
+    /** Updates the max Ntn signal strength level for the session. */
+    public SessionMetricsStats updateMaxNtnSignalStrengthLevel(
+            @NtnSignalStrength.NtnSignalStrengthLevel int latestNtnSignalStrengthLevel) {
+        if (latestNtnSignalStrengthLevel > mMaxNtnSignalStrengthLevel) {
+            mMaxNtnSignalStrengthLevel = latestNtnSignalStrengthLevel;
+        }
+        logd("updateMaxNtnSignalsStrength: latest signal strength=" + latestNtnSignalStrengthLevel
+                + ", max signal strength=" + mMaxNtnSignalStrengthLevel);
+        return this;
+    }
+
     /** Report the session metrics atoms to PersistAtomsStorage in telephony. */
     public void reportSessionMetrics() {
         SatelliteStats.SatelliteSessionParams sessionParams =
@@ -159,6 +173,7 @@
                         .setCountOfIncomingDatagramSuccess(mCountOfSuccessfulIncomingDatagram)
                         .setCountOfIncomingDatagramFailed(mCountOfIncomingDatagramFailed)
                         .setIsDemoMode(mIsDemoMode)
+                        .setMaxNtnSignalStrengthLevel(mMaxNtnSignalStrengthLevel)
                         .build();
         logd("reportSessionMetrics: " + sessionParams.toString());
         SatelliteStats.getInstance().onSatelliteSessionMetrics(sessionParams);
@@ -187,6 +202,7 @@
         mCountOfSuccessfulIncomingDatagram = 0;
         mCountOfIncomingDatagramFailed = 0;
         mIsDemoMode = false;
+        mMaxNtnSignalStrengthLevel = NTN_SIGNAL_STRENGTH_NONE;
     }
 
     private static void logd(@NonNull String log) {
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
index 3d07d47..7596754 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -1044,7 +1044,7 @@
                 throw new IllegalArgumentException("updateSubscription: subscription does not "
                         + "exist. subId=" + subId);
             }
-            if (oldSubInfo.equals(newSubInfo)) return;
+            if (oldSubInfo.equalsDbItemsOnly(newSubInfo)) return;
 
             if (updateDatabase(subId, createDeltaContentValues(oldSubInfo, newSubInfo)) > 0) {
                 mAllSubscriptionInfoInternalCache.put(subId, newSubInfo);
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index bb77d5c..c6dee7c 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -454,12 +454,13 @@
      */
     private final int mIsOnlyNonTerrestrialNetwork;
 
-    // Below are the fields that do not exist in the SimInfo table.
+    // This field does not exist in the SimInfo table.
     /**
      * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
      */
     private final int mCardId;
 
+    // This field does not exist in the SimInfo table.
     /**
      * Whether group of the subscription is disabled. This is only useful if it's a grouped
      * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
@@ -1370,11 +1371,14 @@
                 + "]";
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        SubscriptionInfoInternal that = (SubscriptionInfoInternal) o;
+    /**
+     * Campare only the columns existing in the SimInfo table and the mapped variables to see if
+     * they are equal.
+     *
+     * @param that SubscriptionInfoInternal to be compared
+     * @return {@code true} if equals.
+     */
+    public boolean equalsDbItemsOnly(@NonNull SubscriptionInfoInternal that) {
         return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
                 && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
                 && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
@@ -1407,7 +1411,6 @@
                 && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
                 && mLastUsedTPMessageReference == that.mLastUsedTPMessageReference
                 && mUserId == that.mUserId && mIsSatelliteEnabled == that.mIsSatelliteEnabled
-                && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled
                 && mIccId.equals(that.mIccId) && mDisplayName.equals(that.mDisplayName)
                 && mCarrierName.equals(that.mCarrierName) && mNumber.equals(that.mNumber)
                 && mMcc.equals(that.mMcc) && mMnc.equals(that.mMnc) && mEhplmns.equals(
@@ -1431,6 +1434,15 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SubscriptionInfoInternal that = (SubscriptionInfoInternal) o;
+        return equalsDbItemsOnly(that)
+                && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled;
+    }
+
+    @Override
     public int hashCode() {
         int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
                 mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mEhplmns, mHplmns,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index 3aad333..5e5cb9a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -1149,6 +1149,7 @@
         mSatelliteSession1.countOfIncomingDatagramSuccess = 1;
         mSatelliteSession1.countOfIncomingDatagramFailed = 0;
         mSatelliteSession1.isDemoMode = false;
+        mSatelliteSession1.maxNtnSignalStrengthLevel = 2;
 
         mSatelliteSession2 = new SatelliteSession();
         mSatelliteSession2.satelliteServiceInitializationResult =
@@ -1156,16 +1157,17 @@
         mSatelliteSession2.satelliteTechnology =
                 SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
         mSatelliteSession2.count = 1;
-        mSatelliteSession1.satelliteServiceTerminationResult =
+        mSatelliteSession2.satelliteServiceTerminationResult =
                 SatelliteProtoEnums.SATELLITE_ERROR_NONE;
-        mSatelliteSession1.initializationProcessingTimeMillis = 300;
-        mSatelliteSession1.terminationProcessingTimeMillis = 100;
-        mSatelliteSession1.sessionDurationSeconds = 10;
-        mSatelliteSession1.countOfOutgoingDatagramSuccess = 0;
-        mSatelliteSession1.countOfOutgoingDatagramFailed = 2;
-        mSatelliteSession1.countOfIncomingDatagramSuccess = 0;
-        mSatelliteSession1.countOfIncomingDatagramFailed = 1;
+        mSatelliteSession2.initializationProcessingTimeMillis = 300;
+        mSatelliteSession2.terminationProcessingTimeMillis = 100;
+        mSatelliteSession2.sessionDurationSeconds = 10;
+        mSatelliteSession2.countOfOutgoingDatagramSuccess = 0;
+        mSatelliteSession2.countOfOutgoingDatagramFailed = 2;
+        mSatelliteSession2.countOfIncomingDatagramSuccess = 0;
+        mSatelliteSession2.countOfIncomingDatagramFailed = 1;
         mSatelliteSession2.isDemoMode = true;
+        mSatelliteSession2.maxNtnSignalStrengthLevel = 4;
 
         mSatelliteSessions =
                 new SatelliteSession[] {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
index 9a84224..bc665f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.metrics;
 
+import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -167,6 +169,7 @@
                         .setCountOfIncomingDatagramSuccess(1)
                         .setCountOfIncomingDatagramFailed(0)
                         .setIsDemoMode(false)
+                        .setMaxNtnSignalStrengthLevel(NTN_SIGNAL_STRENGTH_GOOD)
                         .build();
 
         mSatelliteStats.onSatelliteSessionMetrics(param);
@@ -189,6 +192,7 @@
                 stats.countOfIncomingDatagramSuccess);
         assertEquals(param.getCountOfIncomingDatagramFailed(), stats.countOfIncomingDatagramFailed);
         assertEquals(param.getIsDemoMode(), stats.isDemoMode);
+        assertEquals(param.getMaxNtnSignalStrengthLevel(), stats.maxNtnSignalStrengthLevel);
 
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
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 ecec4cd..4896671 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -487,6 +487,7 @@
         mDatagramDispatcherUT.setDemoMode(true);
         mDatagramDispatcherUT.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
+        when(mMockDatagramController.waitForAligningToSatellite(false)).thenReturn(true);
 
         int[] sosDatagramTypes = {DATAGRAM_TYPE1, DATAGRAM_TYPE4, DATAGRAM_TYPE5};
         for (int datagramType : sosDatagramTypes) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
index 361c638..79d3657 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -372,6 +372,7 @@
         mTestDemoModeDatagramReceiver.setDemoMode(true);
         mTestDemoModeDatagramReceiver.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
         mTestDemoModeDatagramReceiver.setDeviceAlignedWithSatellite(false);
+        when(mMockDatagramController.waitForAligningToSatellite(false)).thenReturn(true);
         when(mMockDatagramController.popDemoModeDatagram()).thenReturn(mDatagram);
         mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
new file mode 100644
index 0000000..319e39f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.satellite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NtnSignalStrength;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for DemoSimulator
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DemoSimulatorTest extends TelephonyTest {
+    private static final String TAG = "DemoSimulatorTest";
+    private static final long TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS = 200L;
+    private static final long TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS = 300L;
+    private static final String STATE_POWER_OFF = "PowerOffState";
+    private static final String STATE_NOT_CONNECTED = "NotConnectedState";
+    private static final String STATE_CONNECTED = "ConnectedState";
+
+    private TestDemoSimulator mTestDemoSimulator;
+    @Mock private ISatelliteListener mISatelliteListener;
+
+    @Mock private SatelliteController mMockSatelliteController;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockSatelliteController.isDemoModeEnabled()).thenReturn(true);
+        when(mMockSatelliteController.getDemoPointingAlignedDurationMillis()).thenReturn(
+                TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS);
+        when(mMockSatelliteController.getDemoPointingNotAlignedDurationMillis()).thenReturn(
+                TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS);
+
+        mTestDemoSimulator = new TestDemoSimulator(mContext, Looper.myLooper(),
+                mMockSatelliteController);
+        mTestDemoSimulator.setSatelliteListener(mISatelliteListener);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testInitialState() {
+        assertNotNull(mTestDemoSimulator);
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    @Test
+    public void testStateTransition() {
+        // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+        moveToConnectedState();
+
+        // Device is not aligned with satellite. EVENT_DEVICE_NOT_ALIGNED timer should start
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+
+        // After timeout, DemoSimulator should move to NOT_CONNECTED state.
+        moveTimeForward(TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS);
+        processAllMessages();
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is OFF. DemoSimulator should move to POWER_OFF state.
+        mTestDemoSimulator.onSatelliteModeOff();
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    @Test
+    public void testNotConnectedState_enter() throws Exception {
+        clearInvocations(mISatelliteListener);
+
+        // State transitions: POWER_OFF -> NOT_CONNECTED
+        moveToNotConnectedState();
+
+        verify(mISatelliteListener).onSatelliteModemStateChanged(
+                SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
+                NtnSignalStrength.class);
+        verify(mISatelliteListener).onNtnSignalStrengthChanged(ntnSignalStrength.capture());
+        assertEquals(0, ntnSignalStrength.getValue().signalStrengthLevel);
+    }
+
+    @Test
+    public void testNotConnectedState() {
+        // State transitions: POWER_OFF -> NOT_CONNECTED
+        moveToNotConnectedState();
+
+        // Device is aligned with satellite. EVENT_DEVICE_ALIGNED timer should start.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+
+        // Device is not aligned with satellite. EVENT_DEVICE_ALIGNED messages should be removed.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+        processAllMessages();
+        assertFalse(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is OFF. DemoSimulator should move to POWER_OFF state.
+        mTestDemoSimulator.onSatelliteModeOff();
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    @Test
+    public void testConnectedState_enter() throws Exception {
+        clearInvocations(mISatelliteListener);
+
+        // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+        moveToConnectedState();
+
+        verify(mISatelliteListener).onSatelliteModemStateChanged(
+                SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        verify(mISatelliteListener).onSatelliteModemStateChanged(
+                SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+        ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
+                NtnSignalStrength.class);
+        verify(mISatelliteListener, times(2))
+                .onNtnSignalStrengthChanged(ntnSignalStrength.capture());
+        NtnSignalStrength ntnSignalStrengthOnConnected = ntnSignalStrength.getAllValues().get(1);
+        assertEquals(2, ntnSignalStrengthOnConnected.signalStrengthLevel);
+    }
+
+    @Test
+    public void testConnectedState() {
+        // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+        moveToConnectedState();
+
+        // Device is not aligned with satellite. EVENT_DEVICE_NOT_ALIGNED timer should start
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+
+        // Device is aligned with satellite before timeout.
+        // EVENT_DEVICE_NOT_ALIGNED messages should be removed.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+        processAllMessages();
+        assertFalse(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+        assertEquals(STATE_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is off. DemoSimulator should move to POWER_OFF state
+        mTestDemoSimulator.onSatelliteModeOff();
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    private void moveToNotConnectedState() {
+        // DemoSimulator will initially be in POWER_OFF state.
+        assertNotNull(mTestDemoSimulator);
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is ON. DemoSimulator should move to NOT_CONNECTED state.
+        mTestDemoSimulator.onSatelliteModeOn();
+        processAllMessages();
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    private void moveToConnectedState() {
+        // DemoSimulator will initially be in POWER_OFF state.
+        assertNotNull(mTestDemoSimulator);
+        processAllMessages();
+        assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+
+        // Satellite mode is ON. DemoSimulator should move to NOT_CONNECTED state.
+        mTestDemoSimulator.onSatelliteModeOn();
+        processAllMessages();
+        assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+        // Device is aligned with satellite. EVENT_DEVICE_ALIGNED timer should start.
+        mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+        processAllMessages();
+        assertTrue(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+
+        // After timeout, DemoSimulator should move to CONNECTED state.
+        moveTimeForward(TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS);
+        processAllMessages();
+        assertEquals(STATE_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+    }
+
+    private static class TestDemoSimulator extends DemoSimulator {
+
+        TestDemoSimulator(@NonNull Context context, @NonNull Looper looper,
+                @NonNull SatelliteController satelliteController) {
+            super(context, looper, satelliteController);
+        }
+
+        String getCurrentStateName() {
+            return getCurrentState().getName();
+        }
+
+        boolean isDeviceAlignedTimerStarted() {
+            return hasMessages(EVENT_DEVICE_ALIGNED);
+        }
+
+        boolean isDeviceNotAlignedTimerStarted() {
+            return hasMessages(EVENT_DEVICE_NOT_ALIGNED);
+        }
+    }
+}
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 1484b47..f8656bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -202,6 +202,7 @@
     @Mock private TelephonyConfigUpdateInstallReceiver mMockTelephonyConfigUpdateInstallReceiver;
     @Mock private SatelliteConfigParser mMockConfigParser;
     @Mock private SatelliteConfig mMockConfig;
+    @Mock private DemoSimulator mMockDemoSimulator;
 
     private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0);
     private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() {
@@ -475,6 +476,7 @@
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mPhone2});
         replaceInstance(TelephonyConfigUpdateInstallReceiver.class, "sReceiverAdaptorInstance",
                 null, mMockTelephonyConfigUpdateInstallReceiver);
+        replaceInstance(DemoSimulator.class, "sInstance", null, mMockDemoSimulator);
 
         mServiceState2 = Mockito.mock(ServiceState.class);
         when(mPhone.getServiceState()).thenReturn(mServiceState);
@@ -785,6 +787,7 @@
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
         mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
                 mIIntegerConsumer);
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -1304,6 +1307,7 @@
                 .registerForSatelliteModemStateChanged(callback);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
 
         errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
                 SUB_ID, callback);
@@ -1324,7 +1328,7 @@
                 .unregisterForSatelliteModemStateChanged(callback);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
-
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         mSatelliteControllerUT.unregisterForModemStateChanged(SUB_ID, callback);
         verify(mMockSatelliteSessionController).unregisterForSatelliteModemStateChanged(callback);
     }
@@ -2250,6 +2254,7 @@
                 SATELLITE_MODEM_STATE_CONNECTED);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         clearInvocations(mMockSatelliteSessionController);
         clearInvocations(mMockDatagramController);
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
@@ -4502,5 +4507,9 @@
         protected long getElapsedRealtime() {
             return elapsedRealtime;
         }
+
+        void setSatelliteSessionController(SatelliteSessionController satelliteSessionController) {
+            mSatelliteSessionController = satelliteSessionController;
+        }
     }
 }