Merge "Add additional field into sms atoms for satellite service" into 24D1-dev
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index e2a23f2..1c8b696 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -19,7 +19,9 @@
 import static android.telecom.Connection.STATE_ACTIVE;
 import static android.telecom.Connection.STATE_DISCONNECTED;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL;
 
+import static com.android.internal.telephony.TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_NONE;
 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
@@ -159,6 +161,7 @@
     private boolean mIsTestEmergencyNumber;
     private Runnable mOnEcmExitCompleteRunnable;
     private int mOngoingCallProperties;
+    private boolean mSentEmergencyCallState;
 
     /** For emergency SMS */
     private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>();
@@ -174,6 +177,8 @@
 
     private final android.util.ArrayMap<Integer, Boolean> mNoSimEcbmSupported =
             new android.util.ArrayMap<>();
+    private final android.util.ArrayMap<Integer, Boolean> mBroadcastEmergencyCallStateChanges =
+            new android.util.ArrayMap<>();
     private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
             (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
                     slotIndex, subId);
@@ -466,6 +471,9 @@
         mTelephonyManagerProxy = new TelephonyManagerProxyImpl(context);
 
         registerForNewRingingConnection();
+
+        // To recover the abnormal state after crash of com.android.phone process
+        maybeResetEmergencyCallStateChangedIntent();
     }
 
     /**
@@ -581,6 +589,7 @@
                 mPhone = phone;
                 mOngoingConnection = c;
                 mIsTestEmergencyNumber = isTestEmergencyNumber;
+                sendEmergencyCallStateChange(mPhone, true);
                 maybeRejectIncomingCall(null);
                 return mCallEmergencyModeFuture;
             }
@@ -589,6 +598,7 @@
         mPhone = phone;
         mOngoingConnection = c;
         mIsTestEmergencyNumber = isTestEmergencyNumber;
+        sendEmergencyCallStateChange(mPhone, true);
         maybeRejectIncomingCall(result -> {
             Rlog.i(TAG, "maybeRejectIncomingCall : result = " + result);
             turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber);
@@ -611,6 +621,7 @@
         if (Objects.equals(mOngoingConnection, c)) {
             mOngoingConnection = null;
             mOngoingCallProperties = 0;
+            sendEmergencyCallStateChange(mPhone, false);
         }
 
         if (wasActive && mActiveEmergencyCalls.isEmpty()
@@ -1199,6 +1210,18 @@
                 && mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_CS && isInEcm();
     }
 
+    private void sendEmergencyCallStateChange(Phone phone, boolean isAlive) {
+        if ((isAlive && !mSentEmergencyCallState && getBroadcastEmergencyCallStateChanges(phone))
+                || (!isAlive && mSentEmergencyCallState)) {
+            mSentEmergencyCallState = isAlive;
+            Rlog.i(TAG, "sendEmergencyCallStateChange: " + isAlive);
+            Intent intent = new Intent(ACTION_EMERGENCY_CALL_STATE_CHANGED);
+            intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, isAlive);
+            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phone.getPhoneId());
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        }
+    }
+
     /**
      * Starts the process of an emergency SMS.
      *
@@ -1654,9 +1677,9 @@
     private boolean getConfig(int subId, String key, boolean defVal) {
         return getConfigBundle(subId, key).getBoolean(key, defVal);
     }
-    private PersistableBundle getConfigBundle(int subId, String key) {
+    private PersistableBundle getConfigBundle(int subId, String... keys) {
         if (mConfigManager == null) return new PersistableBundle();
-        return mConfigManager.getConfigForSubId(subId, key);
+        return mConfigManager.getConfigForSubId(subId, keys);
     }
 
     /**
@@ -1710,10 +1733,6 @@
             return;
         }
 
-        updateNoSimEcbmSupported(slotIndex, subId);
-    }
-
-    private void updateNoSimEcbmSupported(int slotIndex, int subId) {
         SharedPreferences sp = null;
         Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(slotIndex));
         if (savedConfig == null) {
@@ -1721,8 +1740,8 @@
             savedConfig = Boolean.valueOf(
                     sp.getBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, false));
             mNoSimEcbmSupported.put(Integer.valueOf(slotIndex), savedConfig);
-            Rlog.i(TAG, "updateNoSimEcbmSupported load from preference slotIndex=" + slotIndex
-                    + ", supported=" + savedConfig);
+            Rlog.i(TAG, "onCarrierConfigChanged load from preference slotIndex=" + slotIndex
+                    + ", ecbmSupported=" + savedConfig);
         }
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -1730,17 +1749,28 @@
             return;
         }
 
-        PersistableBundle b = getConfigBundle(subId, KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
+        PersistableBundle b = getConfigBundle(subId,
+                KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
+                KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL);
         if (b.isEmpty()) {
-            Rlog.e(TAG, "updateNoSimEcbmSupported empty result");
+            Rlog.e(TAG, "onCarrierConfigChanged empty result");
             return;
         }
 
         if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) {
-            Rlog.i(TAG, "updateNoSimEcbmSupported not carrier specific configuration");
+            Rlog.i(TAG, "onCarrierConfigChanged not carrier specific configuration");
             return;
         }
 
+        // KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL
+        boolean broadcast = b.getBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL);
+        mBroadcastEmergencyCallStateChanges.put(
+                Integer.valueOf(slotIndex), Boolean.valueOf(broadcast));
+
+        Rlog.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex
+                + ", broadcastEmergencyCallStateChanges=" + broadcast);
+
+        // KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL
         boolean carrierConfig = b.getBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
         if (carrierConfig == savedConfig) {
             return;
@@ -1755,8 +1785,34 @@
         editor.putBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, carrierConfig);
         editor.apply();
 
-        Rlog.i(TAG, "updateNoSimEcbmSupported preference updated slotIndex=" + slotIndex
-                + ", supported=" + carrierConfig);
+        Rlog.i(TAG, "onCarrierConfigChanged preference updated slotIndex=" + slotIndex
+                + ", ecbmSupported=" + carrierConfig);
+    }
+
+    private boolean getBroadcastEmergencyCallStateChanges(Phone phone) {
+        Boolean broadcast = mBroadcastEmergencyCallStateChanges.get(
+                Integer.valueOf(phone.getPhoneId()));
+        return (broadcast == null) ? false : broadcast;
+    }
+
+    /**
+     * Resets the emergency call state if it's in alive state.
+     */
+    @VisibleForTesting
+    public void maybeResetEmergencyCallStateChangedIntent() {
+        Intent intent = mContext.registerReceiver(null,
+            new IntentFilter(ACTION_EMERGENCY_CALL_STATE_CHANGED), Context.RECEIVER_NOT_EXPORTED);
+        if (intent != null
+                && ACTION_EMERGENCY_CALL_STATE_CHANGED.equals(intent.getAction())) {
+            boolean isAlive = intent.getBooleanExtra(
+                    TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
+            Rlog.i(TAG, "maybeResetEmergencyCallStateChangedIntent isAlive=" + isAlive);
+            if (isAlive) {
+                intent = new Intent(ACTION_EMERGENCY_CALL_STATE_CHANGED);
+                intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
+                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+            }
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteConfig.java b/src/java/com/android/internal/telephony/satellite/SatelliteConfig.java
index 8d7e723..60950f2 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteConfig.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteConfig.java
@@ -228,6 +228,10 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public boolean isFileExist(Path filePath) {
+        if (filePath == null) {
+            Log.d(TAG, "isFileExist : filePath is null");
+            return false;
+        }
         return Files.exists(filePath);
     }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 5c79c28..458e8e7 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -431,6 +431,9 @@
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged((boolean) msg.obj);
                     break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    deferMessage(msg);
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -443,6 +446,14 @@
                 } else {
                     transitionTo(mIdleState);
                 }
+            } else {
+                /*
+                 * During the state transition from POWER_OFF to NOT_CONNECTED, modem might be
+                 * reset. In such cases, we need to remove all deferred
+                 * EVENT_SATELLITE_MODEM_STATE_CHANGED events so that they will not mess up our
+                 * state machine later.
+                 */
+                removeDeferredMessages(EVENT_SATELLITE_MODEM_STATE_CHANGED);
             }
         }
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
index 2acc667..699ee7b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -36,6 +36,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyVararg;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -49,10 +50,12 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
@@ -74,6 +77,7 @@
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.data.PhoneSwitcher;
 
@@ -2326,6 +2330,8 @@
                 false /* isRadioOn */);
         when(phone.getSubId()).thenReturn(1);
         setEcmSupportedConfig(phone, true);
+        PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(phone.getSubId());
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());
 
         EmergencyStateTracker testEst = setupEmergencyStateTracker(
                 false /* isSuplDdsSwitchRequiredForEmergencyCall */);
@@ -2675,6 +2681,162 @@
         }
     }
 
+    /**
+     * Test that emergency call state changes are sent.
+     */
+    @Test
+    @SmallTest
+    public void testSendEmergencyCallStateChanges() {
+        mContextFixture.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        // Create test Phone
+        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+                /* isRadioOn= */ true);
+        when(testPhone.getSubId()).thenReturn(1);
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+        CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+        verify(cfgManager).registerCarrierConfigChangeListener(any(),
+                listenerArgumentCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener carrierConfigChangeListener =
+                listenerArgumentCaptor.getAllValues().get(0);
+
+        assertNotNull(carrierConfigChangeListener);
+
+        PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(testPhone.getSubId());
+        bundle.putBoolean(CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL,
+                true);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());
+        // onCarrierConfigChanged with valid subscription
+        carrierConfigChangeListener.onCarrierConfigChanged(
+                testPhone.getPhoneId(), testPhone.getSubId(),
+                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+
+        // Start emergency call
+        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+                mTestConnection1, false);
+
+        // Verify intent is sent that emergency call state is changed
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendStickyBroadcastAsUser(
+                intentCaptor.capture(), eq(UserHandle.ALL));
+        Intent intent = intentCaptor.getValue();
+        assertNotNull(intent);
+        assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED, intent.getAction());
+        assertTrue(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, true));
+
+        // End emergency call
+        emergencyStateTracker.endCall(mTestConnection1);
+
+        // Verify intent is sent that emergency call state is changed
+        verify(mContext, times(2)).sendStickyBroadcastAsUser(
+                intentCaptor.capture(), eq(UserHandle.ALL));
+        intent = intentCaptor.getValue();
+        assertNotNull(intent);
+        assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED, intent.getAction());
+        assertFalse(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false));
+    }
+
+    /**
+     * Test that emergency call state change is reset after crash.
+     */
+    @Test
+    @SmallTest
+    public void testResetEmergencyCallStateChanges() {
+        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
+        intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, true);
+        doReturn(intent).when(mContext).registerReceiver(eq(null), any(),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        emergencyStateTracker.maybeResetEmergencyCallStateChangedIntent();
+
+        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);
+
+        verify(mContext).registerReceiver(eq(null), filterCaptor.capture(),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+
+        IntentFilter filter = filterCaptor.getValue();
+
+        assertNotNull(filter);
+        assertTrue(filter.hasAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED));
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+        // Verify intent is sent that emergency call state is changed
+        verify(mContext).sendStickyBroadcastAsUser(
+                intentCaptor.capture(), eq(UserHandle.ALL));
+        intent = intentCaptor.getValue();
+        assertNotNull(intent);
+        assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED, intent.getAction());
+        assertFalse(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false));
+    }
+
+    /**
+     * Test that emergency call state is not reset after crash
+     * if it's already reset.
+     */
+    @Test
+    @SmallTest
+    public void testResetEmergencyCallStateChangesAlreadyReset() {
+        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
+        intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
+        doReturn(intent).when(mContext).registerReceiver(eq(null), any(),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        emergencyStateTracker.maybeResetEmergencyCallStateChangedIntent();
+
+        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);
+
+        verify(mContext).registerReceiver(eq(null), filterCaptor.capture(),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+
+        IntentFilter filter = filterCaptor.getValue();
+
+        assertNotNull(filter);
+        assertTrue(filter.hasAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED));
+
+        // Verify intent is not sent.
+        verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
+    }
+
+    /**
+     * Test that emergency call state is not reset after crash
+     * if it has never been sent.
+     */
+    @Test
+    @SmallTest
+    public void testResetEmergencyCallStateChangesNotSent() {
+        doReturn(null).when(mContext).registerReceiver(eq(null), any(),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        emergencyStateTracker.maybeResetEmergencyCallStateChangedIntent();
+
+        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);
+
+        verify(mContext).registerReceiver(eq(null), filterCaptor.capture(),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+
+        IntentFilter filter = filterCaptor.getValue();
+
+        assertNotNull(filter);
+        assertTrue(filter.hasAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED));
+
+        // Verify intent is not sent.
+        verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
+    }
+
     private EmergencyStateTracker setupEmergencyStateTracker(
             boolean isSuplDdsSwitchRequiredForEmergencyCall) {
         doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteConfigParserTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteConfigParserTest.java
index e4f0255..5e1dd05 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteConfigParserTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteConfigParserTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.satellite;
 
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertFalse;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -185,27 +186,26 @@
         final String filePath = "/data/user_de/0/com.android.phone/app_satellite/s2_cell_file";
         Path targetSatS2FilePath = Paths.get(filePath);
 
-        SatelliteConfigParser mockedSatelliteConfigParserNull = spy(
+        SatelliteConfigParser spySatelliteConfigParserNull = spy(
                 new SatelliteConfigParser((byte[]) null));
-        assertNotNull(mockedSatelliteConfigParserNull);
-        assertNull(mockedSatelliteConfigParserNull.getConfig());
+        assertNotNull(spySatelliteConfigParserNull);
+        assertNull(spySatelliteConfigParserNull.getConfig());
 
-        SatelliteConfigParser mockedSatelliteConfigParserPlaceholder = spy(
+        SatelliteConfigParser spySatelliteConfigParserPlaceholder = spy(
                 new SatelliteConfigParser("test".getBytes()));
-        assertNotNull(mockedSatelliteConfigParserPlaceholder);
-        assertNull(mockedSatelliteConfigParserPlaceholder.getConfig());
+        assertNotNull(spySatelliteConfigParserPlaceholder);
+        assertNull(spySatelliteConfigParserPlaceholder.getConfig());
 
-        SatelliteConfigParser mockedSatelliteConfigParser =
+        SatelliteConfigParser spySatelliteConfigParser =
                 spy(new SatelliteConfigParser(mBytesProtoBuffer));
+        assertNotNull(spySatelliteConfigParser.getConfig());
+        assertFalse(spySatelliteConfigParser.getConfig().isFileExist(null));
+
         SatelliteConfig mockedSatelliteConfig = Mockito.mock(SatelliteConfig.class);
         doReturn(targetSatS2FilePath).when(mockedSatelliteConfig).getSatelliteS2CellFile(any());
-        doReturn(mockedSatelliteConfig).when(mockedSatelliteConfigParser).getConfig();
-//        assertNotNull(mockedSatelliteConfigParser.getConfig());
-//        doReturn(false).when(mockedSatelliteConfigParser).getConfig().isFileExist(any());
-//        doReturn(targetSatS2FilePath).when(mockedSatelliteConfigParser).getConfig()
-//                .copySatS2FileToPhoneDirectory(any(), any());
+        doReturn(mockedSatelliteConfig).when(spySatelliteConfigParser).getConfig();
         assertEquals(targetSatS2FilePath,
-                mockedSatelliteConfigParser.getConfig().getSatelliteS2CellFile(mContext));
+                spySatelliteConfigParser.getConfig().getSatelliteS2CellFile(mContext));
     }
 
     @Test
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 a1c2cfc..f0a18e8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -53,6 +53,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -804,6 +806,93 @@
         assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
                 SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
         assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power off the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+
+        // SatelliteSessionController should move to POWER_OFF
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        processAllMessages();
+
+        // The modem state changed events should be deferred
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isEventDeferred(
+                4 /* EVENT_SATELLITE_MODEM_STATE_CHANGED */));
+
+        // Power on the modem.
+        mTestSatelliteModemStateCallback.clearModemStates();
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on. Then, it should move to CONNECTED and then back to NOT_CONNECTED state
+        // because of the above deferred events.
+        assertEquals(3, mTestSatelliteModemStateCallback.getNumberOfModemStates());
+        assertEquals(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED,
+                mTestSatelliteModemStateCallback.getModemState(0));
+        assertEquals(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED,
+                mTestSatelliteModemStateCallback.getModemState(1));
+        assertEquals(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED,
+                mTestSatelliteModemStateCallback.getModemState(2));
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Power off the modem.
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+
+        // SatelliteSessionController should move to POWER_OFF
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        mTestSatelliteModemStateCallback.clearSemaphorePermits();
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        processAllMessages();
+
+        // The modem state changed events should be deferred
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        assertTrue(mTestSatelliteSessionController.isEventDeferred(
+                4 /* EVENT_SATELLITE_MODEM_STATE_CHANGED */));
+
+        // Modem got reset. The deferred messages should be removed.
+        mTestSatelliteModemStateCallback.clearSemaphorePermits();
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
+        processAllMessages();
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isEventDeferred(
+                4 /* EVENT_SATELLITE_MODEM_STATE_CHANGED */));
+
+        // Power on the modem.
+        mTestSatelliteModemStateCallback.clearModemStates();
+        mTestSatelliteSessionController.onSatelliteEnabledStateChanged(true);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(1, mTestSatelliteModemStateCallback.getNumberOfModemStates());
+        assertEquals(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED,
+                mTestSatelliteModemStateCallback.getModemState(0));
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
     }
 
     private void setupDatagramTransferringState(boolean isTransferring) {
@@ -878,17 +967,26 @@
         boolean isNbIotInactivityTimerStarted() {
             return hasMessages(EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
         }
+
+        boolean isEventDeferred(int event) {
+            return hasDeferredMessages(event);
+        }
     }
 
     private static class TestSatelliteModemStateCallback extends ISatelliteModemStateCallback.Stub {
         private final AtomicInteger mModemState = new AtomicInteger(
                 SatelliteManager.SATELLITE_MODEM_STATE_OFF);
         private final Semaphore mSemaphore = new Semaphore(0);
+        private final Object mLock = new Object();
+        private final List<Integer> mModemStates = new ArrayList<>();
 
         @Override
         public void onSatelliteModemStateChanged(int state) {
             logd("onSatelliteModemStateChanged: state=" + state);
             mModemState.set(state);
+            synchronized (mLock) {
+                mModemStates.add(state);
+            }
             try {
                 mSemaphore.release();
             } catch (Exception ex) {
@@ -912,6 +1010,28 @@
         public int getModemState() {
             return mModemState.get();
         }
+
+        public int getModemState(int index) {
+            synchronized (mLock) {
+                return mModemStates.get(index);
+            }
+        }
+
+        public void clearModemStates() {
+            synchronized (mLock) {
+                mModemStates.clear();
+            }
+        }
+
+        public int getNumberOfModemStates() {
+            synchronized (mLock) {
+                return mModemStates.size();
+            }
+        }
+
+        public void clearSemaphorePermits() {
+            mSemaphore.drainPermits();
+        }
     }
 
     private static void assertSuccessfulModemStateChangedCallback(