Merge "Remove post runnable as TestableLooper is used." into main
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index 95b70d5..5852d76 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -547,7 +547,8 @@
   }
   eccs {
     phone_number: "1414"
-    types: TYPE_UNSPECIFIED
+    types: MOUNTAIN_RESCUE
+    routing: NORMAL
   }
   eccs {
     phone_number: "0800117117"
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index 513d6b9..d16b06f 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/res/values/config.xml b/res/values/config.xml
index cdef37e..61e01b0 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -347,6 +347,8 @@
         <!-- b/317945295 -->
         <item>in</item>
         <item>sg</item>
+        <!-- b/341611911 -->
+        <item>my</item>
     </string-array>
 
     <!-- Array of countries that a CS preferred scan is preferred after CSFB failure
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 308ef40..c648edb 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -8140,20 +8140,31 @@
     @Override
     public void uploadCallComposerPicture(int subscriptionId, String callingPackage,
             String contentType, ParcelFileDescriptor fd, ResultReceiver callback) {
-        try {
-            if (!Objects.equals(mApp.getPackageManager().getPackageUid(callingPackage, 0),
-                    Binder.getCallingUid())) {
+        if (com.android.internal.telephony.flags.Flags.supportPhoneUidCheckForMultiuser()) {
+            enforceCallingPackage(callingPackage, Binder.getCallingUid(),
+                    "Invalid package:" + callingPackage);
+        } else {
+            try {
+                if (!Objects.equals(mApp.getPackageManager().getPackageUid(callingPackage, 0),
+                        Binder.getCallingUid())) {
+                    throw new SecurityException("Invalid package:" + callingPackage);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
                 throw new SecurityException("Invalid package:" + callingPackage);
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new SecurityException("Invalid package:" + callingPackage);
         }
 
         enforceTelephonyFeatureWithException(callingPackage,
                 PackageManager.FEATURE_TELEPHONY_CALLING, "uploadCallComposerPicture");
 
         RoleManager rm = mApp.getSystemService(RoleManager.class);
-        List<String> dialerRoleHolders = rm.getRoleHolders(RoleManager.ROLE_DIALER);
+        List<String> dialerRoleHolders;
+        if (com.android.internal.telephony.flags.Flags.supportPhoneUidCheckForMultiuser()) {
+            dialerRoleHolders = rm.getRoleHoldersAsUser(RoleManager.ROLE_DIALER,
+                    UserHandle.of(ActivityManager.getCurrentUser()));
+        } else {
+            dialerRoleHolders = rm.getRoleHolders(RoleManager.ROLE_DIALER);
+        }
         if (!dialerRoleHolders.contains(callingPackage)) {
             throw new SecurityException("App must be the dialer role holder to"
                     + " upload a call composer pic");
@@ -14326,7 +14337,8 @@
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public void requestIsProvisioned(String satelliteSubscriberId, @NonNull ResultReceiver result) {
+    public void requestIsProvisioned(@NonNull String satelliteSubscriberId,
+            @NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestIsProvisioned");
         mSatelliteController.requestIsProvisioned(satelliteSubscriberId, result);
     }
@@ -14340,7 +14352,7 @@
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public void provisionSatellite(List<ProvisionSubscriberId> list,
+    public void provisionSatellite(@NonNull List<ProvisionSubscriberId> list,
             @NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("provisionSatellite");
         mSatelliteController.provisionSatellite(list, result);
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 77bc32a..da9cfdf 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,12 +32,14 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Telephony;
@@ -1324,8 +1326,12 @@
      */
     public static synchronized TelecomAccountRegistry getInstance(Context context) {
         if (sInstance == null && context != null) {
-            if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
-                PackageManager pm = context.getPackageManager();
+            int vendorApiLevel = SystemProperties.getInt("ro.vendor.api_level",
+                    Build.VERSION.DEVICE_INITIAL_SDK_INT);
+            PackageManager pm = context.getPackageManager();
+
+            if (Flags.enforceTelephonyFeatureMappingForPublicApis()
+                    && vendorApiLevel >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
                 if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
                         && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
                     sInstance = new TelecomAccountRegistry(context);
@@ -1334,7 +1340,14 @@
                             + "missing telephony/calling feature(s)");
                 }
             } else {
-                sInstance = new TelecomAccountRegistry(context);
+                // One of features is defined, create instance
+                if (pm != null && (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+                        || pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING))) {
+                    sInstance = new TelecomAccountRegistry(context);
+                } else {
+                    Log.d(LOG_TAG, "Not initializing TelecomAccountRegistry: "
+                            + "missing telephony or calling feature(s)");
+                }
             }
         }
         return sInstance;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 4421f0c..5c4f367 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -90,6 +90,8 @@
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.emergency.RadioOnHelper;
 import com.android.internal.telephony.emergency.RadioOnStateListener;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -214,6 +216,9 @@
             new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
     private final CdmaConferenceController mCdmaConferenceController =
             new CdmaConferenceController(this);
+
+    private FeatureFlags mFeatureFlags = new FeatureFlagsImpl();
+
     private ImsConferenceController mImsConferenceController;
 
     private ComponentName mExpectedComponentName = null;
@@ -765,6 +770,15 @@
                 if (cause == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
                         || cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE) {
                     if (mEmergencyConnection != null) {
+                        if (Flags.hangupEmergencyCallForCrossSimRedialing()) {
+                            if (mEmergencyConnection.getOriginalConnection() != null) {
+                                if (mEmergencyConnection.getOriginalConnection()
+                                        .getState().isAlive()) {
+                                    mEmergencyConnection.hangup(cause);
+                                }
+                                return;
+                            }
+                        }
                         boolean isPermanentFailure =
                                 cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
                         Log.i(this, "onSelectionTerminated permanent=" + isPermanentFailure);
@@ -1156,14 +1170,15 @@
 
         final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
 
-        boolean needToTurnOffSatellite = isSatelliteBlockingCall(isEmergencyNumber);
-
         // Get the right phone object from the account data passed in.
         final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
                 /* Note: when not an emergency, handle can be null for unknown callers */
                 handle == null ? null : handle.getSchemeSpecificPart());
         ImsPhone imsPhone = phone != null ? (ImsPhone) phone.getImsPhone() : null;
 
+        boolean needToTurnOffSatellite = shouldExitSatelliteModeForEmergencyCall(
+                isEmergencyNumber, phone);
+
         boolean isPhoneWifiCallingEnabled = phone != null && phone.isWifiCallingEnabled();
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
                 || (isRadioPowerDownOnBluetooth() && !isPhoneWifiCallingEnabled);
@@ -1279,7 +1294,7 @@
                                 // Do not wait for voice in service on opportunistic SIMs.
                                 || subInfo != null && subInfo.isOpportunistic()
                                 || (serviceState == ServiceState.STATE_IN_SERVICE
-                                && !isSatelliteBlockingCall(isEmergencyNumber));
+                                && !needToTurnOffSatellite);
                     }
                 }
             }, isEmergencyNumber && !isTestEmergencyNumber, phone, isTestEmergencyNumber,
@@ -1478,7 +1493,7 @@
                 });
             }
         } else {
-            if (isSatelliteBlockingCall(isEmergencyNumber)) {
+            if (shouldExitSatelliteModeForEmergencyCall(isEmergencyNumber, phone)) {
                 Log.w(LOG_TAG, "handleOnComplete, failed to turn off satellite modem");
                 closeOrDestroyConnection(originalConnection,
                         mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -2129,7 +2144,8 @@
         return result;
     }
 
-    private boolean isSatelliteBlockingCall(boolean isEmergencyNumber) {
+    private boolean shouldExitSatelliteModeForEmergencyCall(boolean isEmergencyNumber,
+            Phone phone) {
         if (!mSatelliteController.isSatelliteEnabled()) {
             return false;
         }
@@ -2138,6 +2154,12 @@
             if (mSatelliteController.isDemoModeEnabled()) {
                 // If user makes emergency call in demo mode, end the satellite session
                 return true;
+            } else if (mFeatureFlags.carrierRoamingNbIotNtn()
+                    && mSatelliteController.isInSatelliteModeForCarrierRoaming(phone)
+                    && !mSatelliteController.getRequestIsEmergency()) {
+                // If CarrierRoaming mode enabled and OEM Satellite request is not for emergency
+                // end to satellite session
+                return true;
             } else {
                 return getTurnOffOemEnabledSatelliteDuringEmergencyCall();
             }
@@ -2866,9 +2888,19 @@
                 + "csCause=" +  callFailCause + ", psCause=" + reasonInfo
                 + ", showPreciseCause=" + showPreciseCause + ", overrideCause=" + overrideCause);
 
-        if (c.getOriginalConnection() != null
+        boolean isLocalHangup = c.getOriginalConnection() != null
                 && c.getOriginalConnection().getDisconnectCause()
-                        != android.telephony.DisconnectCause.LOCAL
+                        == android.telephony.DisconnectCause.LOCAL;
+
+        // Do not treat it as local hangup if it is a cross-sim redial.
+        if (Flags.hangupEmergencyCallForCrossSimRedialing()) {
+            isLocalHangup = isLocalHangup
+                    && overrideCause != android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
+                    && overrideCause != android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+        }
+
+        // If it is neither a local hangup nor a power off hangup, then reselect domain.
+        if (c.getOriginalConnection() != null && (!isLocalHangup)
                 && c.getOriginalConnection().getDisconnectCause()
                         != android.telephony.DisconnectCause.POWER_OFF) {
 
@@ -4797,4 +4829,10 @@
         }
         return turnOffSatellite;
     }
+
+    /* Only for testing */
+    @VisibleForTesting
+    public void setFeatureFlags(FeatureFlags featureFlags) {
+        mFeatureFlags = featureFlags;
+    }
 }
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index 9f2e0a9..b44d476 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -101,6 +101,7 @@
 import android.util.LocalLog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
 import com.android.phone.R;
 
 import java.util.ArrayList;
@@ -270,6 +271,7 @@
     private final CrossSimRedialingController mCrossSimRedialingController;
     private final DataConnectionStateHelper mEpdnHelper;
     private final List<Network> mWiFiNetworksAvailable = new ArrayList<>();
+    private final ImsEmergencyRegistrationStateHelper mImsEmergencyRegistrationHelper;
 
     /** Constructor. */
     public EmergencyCallDomainSelector(Context context, int slotId, int subId,
@@ -288,6 +290,8 @@
         mCrossSimRedialingController = csrController;
         mEpdnHelper = epdnHelper;
         epdnHelper.setEmergencyCallDomainSelector(this);
+        mImsEmergencyRegistrationHelper = new ImsEmergencyRegistrationStateHelper(
+                mContext, getSlotId(), getSubId(), getLooper());
         acquireWakeLock();
     }
 
@@ -623,6 +627,9 @@
         mDomainSelectionRequested = true;
         startCrossStackTimer();
         if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+            if (mCallSetupTimerOnCurrentRat > 0) {
+                mImsEmergencyRegistrationHelper.start();
+            }
             sendEmptyMessageDelayed(MSG_WAIT_FOR_IMS_STATE_TIMEOUT,
                     DEFAULT_WAIT_FOR_IMS_STATE_TIMEOUT_MS);
             selectDomain();
@@ -1818,7 +1825,7 @@
         logi("notifyCrossStackTimerExpired");
 
         mCrossStackTimerExpired = true;
-        if (mDomainSelected) {
+        if (mDomainSelected && !hangupOngoingDialing()) {
             // When reselecting domain, terminateSelection will be called.
             return;
         }
@@ -1827,6 +1834,12 @@
         terminateSelectionForCrossSimRedialing(false);
     }
 
+    private boolean hangupOngoingDialing() {
+        return Flags.hangupEmergencyCallForCrossSimRedialing()
+                && (mCallSetupTimerOnCurrentRat > 0)
+                && (!mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+    }
+
     /** Notifies the ePDN connection state changes. */
     public void notifyDataConnectionStateChange(int slotId, int state) {
         if (slotId == getSlotId() && mIsWaitingForDataDisconnection) {
@@ -1921,6 +1934,7 @@
         if (DBG) logd("destroy");
 
         mEpdnHelper.setEmergencyCallDomainSelector(null);
+        mImsEmergencyRegistrationHelper.destroy();
         mCrossSimRedialingController.stopTimer();
         releaseWakeLock();
 
diff --git a/src/com/android/services/telephony/domainselection/ImsEmergencyRegistrationStateHelper.java b/src/com/android/services/telephony/domainselection/ImsEmergencyRegistrationStateHelper.java
new file mode 100644
index 0000000..a6ac9c4
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/ImsEmergencyRegistrationStateHelper.java
@@ -0,0 +1,212 @@
+/*
+ * 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.ImsStateCallback;
+import android.telephony.ims.RegistrationManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A class to listen to the IMS emergency registration state.
+ */
+public class ImsEmergencyRegistrationStateHelper {
+    private static final String TAG = ImsEmergencyRegistrationStateHelper.class.getSimpleName();
+
+    protected static final long MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS = 2 * 1000; // 2 seconds
+
+    private final Context mContext;
+    private final int mSlotId;
+    private final int mSubId;
+    private final Handler mHandler;
+
+    private ImsMmTelManager mMmTelManager;
+    private ImsStateCallback mImsStateCallback;
+    private RegistrationManager.RegistrationCallback mRegistrationCallback;
+    private boolean mImsEmergencyRegistered;
+
+    public ImsEmergencyRegistrationStateHelper(@NonNull Context context,
+            int slotId, int subId, @NonNull Looper looper) {
+        mContext = context;
+        mSlotId = slotId;
+        mSubId = subId;
+        mHandler = new Handler(looper);
+    }
+
+    /**
+     * Destroys this instance.
+     */
+    public void destroy() {
+        stopListeningForImsEmergencyRegistrationState();
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    /**
+     * Returns the Handler instance.
+     */
+    @VisibleForTesting
+    public @NonNull Handler getHandler() {
+        return mHandler;
+    }
+
+    /**
+     * Returns {@code true} if IMS is registered, {@code false} otherwise.
+     */
+    public boolean isImsEmergencyRegistered() {
+        return mImsEmergencyRegistered;
+    }
+
+    /**
+     * Starts listening for IMS emergency registration state.
+     */
+    public void start() {
+        startListeningForImsEmergencyRegistrationState();
+    }
+
+    /**
+     * Starts listening to monitor the IMS states -
+     * connection state, IMS emergency registration state.
+     */
+    private void startListeningForImsEmergencyRegistrationState() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return;
+        }
+
+        ImsManager imsMngr = mContext.getSystemService(ImsManager.class);
+        mMmTelManager = imsMngr.getImsMmTelManager(mSubId);
+        mImsEmergencyRegistered = false;
+        registerImsStateCallback();
+    }
+
+    /**
+     * Stops listening to monitor the IMS states -
+     * connection state, IMS emergency registration state.
+     */
+    private void stopListeningForImsEmergencyRegistrationState() {
+        if (mMmTelManager != null) {
+            unregisterImsEmergencyRegistrationCallback();
+            unregisterImsStateCallback();
+            mMmTelManager = null;
+        }
+    }
+
+    private void registerImsStateCallback() {
+        if (mImsStateCallback != null) {
+            loge("ImsStateCallback is already registered for sub-" + mSubId);
+            return;
+        }
+
+        // Listens to the IMS connection state change.
+        mImsStateCallback = new ImsStateCallback() {
+            @Override
+            public void onUnavailable(@DisconnectedReason int reason) {
+                unregisterImsEmergencyRegistrationCallback();
+            }
+
+            @Override
+            public void onAvailable() {
+                registerImsEmergencyRegistrationCallback();
+            }
+
+            @Override
+            public void onError() {
+                mImsStateCallback = null;
+                mHandler.postDelayed(
+                        ImsEmergencyRegistrationStateHelper.this::registerImsStateCallback,
+                        MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS);
+            }
+        };
+
+        try {
+            mMmTelManager.registerImsStateCallback(mHandler::post, mImsStateCallback);
+        } catch (ImsException e) {
+            loge("Exception when registering ImsStateCallback: " + e);
+            mImsStateCallback = null;
+        }
+    }
+
+    private void unregisterImsStateCallback() {
+        if (mImsStateCallback != null) {
+            try {
+                mMmTelManager.unregisterImsStateCallback(mImsStateCallback);
+            }  catch (Exception ignored) {
+                // Ignore the runtime exception while unregistering callback.
+                logd("Exception when unregistering ImsStateCallback: " + ignored);
+            }
+            mImsStateCallback = null;
+        }
+    }
+
+    private void registerImsEmergencyRegistrationCallback() {
+        if (mRegistrationCallback != null) {
+            logd("RegistrationCallback is already registered for sub-" + mSubId);
+            return;
+        }
+
+        // Listens to the IMS emergency registration state change.
+        mRegistrationCallback = new RegistrationManager.RegistrationCallback() {
+            @Override
+            public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+                mImsEmergencyRegistered = true;
+            }
+
+            @Override
+            public void onUnregistered(@NonNull ImsReasonInfo info) {
+                mImsEmergencyRegistered = false;
+            }
+        };
+
+        try {
+            mMmTelManager.registerImsEmergencyRegistrationCallback(mHandler::post,
+                    mRegistrationCallback);
+        } catch (ImsException e) {
+            loge("Exception when registering RegistrationCallback: " + e);
+            mRegistrationCallback = null;
+        }
+    }
+
+    private void unregisterImsEmergencyRegistrationCallback() {
+        if (mRegistrationCallback != null) {
+            try {
+                mMmTelManager.unregisterImsEmergencyRegistrationCallback(mRegistrationCallback);
+            }  catch (Exception ignored) {
+                // Ignore the runtime exception while unregistering callback.
+                logd("Exception when unregistering RegistrationCallback: " + ignored);
+            }
+            mRegistrationCallback = null;
+        }
+    }
+
+    private void logd(String s) {
+        Log.d(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+    }
+
+    private void loge(String s) {
+        Log.e(TAG, "[" + mSlotId + "|" + mSubId + "] " + s);
+    }
+}
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
index 6aec1da..b1b2ccf 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
@@ -125,6 +125,24 @@
             android:layout_height="wrap_content"
             android:paddingRight="4dp"
             android:text="@string/getIsEmergency"/>
+        <Button
+            android:id="@+id/requestProvisionSubscriberIds"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestProvisionSubscriberIds"/>
+        <Button
+            android:id="@+id/requestIsProvisioned"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsProvisioned"/>
+        <Button
+            android:id="@+id/provisionSatellite"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/provisionSatellite"/>
          <Button
             android:id="@+id/Back"
             android:onClick="Back"
diff --git a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
index e7fbb97..1f2a3ca 100644
--- a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
@@ -91,6 +91,10 @@
     <string name="reportSatelliteNotSupportedFromModem">reportSatelliteNotSupportedFromModem</string>
     <string name="showCurrentSatelliteSupportedStated">showCurrentSatelliteSupportedStated</string>
 
+    <string name="requestProvisionSubscriberIds">requestProvisionSubscriberIds</string>
+    <string name="requestIsProvisioned">requestIsProvisioned</string>
+    <string name="provisionSatellite">provisionSatellite</string>
+
     <string name="Back">Back</string>
     <string name="ClearLog">Clear Log</string>
 
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
index a03f04e..4b09a56 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
@@ -23,6 +23,7 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.EnableRequestAttributes;
+import android.telephony.satellite.ProvisionSubscriberId;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.stub.SatelliteResult;
@@ -31,6 +32,7 @@
 import android.widget.TextView;
 
 import java.time.Duration;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -45,6 +47,7 @@
 
     private SatelliteManager mSatelliteManager;
     private SubscriptionManager mSubscriptionManager;
+    private List<ProvisionSubscriberId> mProvisionSubscriberIdList = new ArrayList<>();
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -83,6 +86,12 @@
                 .setOnClickListener(this::isRequestIsSatelliteEnabledForCarrierApp);
         findViewById(R.id.getIsEmergency)
                 .setOnClickListener(this::getIsEmergencyApp);
+        findViewById(R.id.requestProvisionSubscriberIds)
+                .setOnClickListener(this::requestProvisionSubscriberIdsApp);
+        findViewById(R.id.requestIsProvisioned)
+                .setOnClickListener(this::requestIsProvisionedApp);
+        findViewById(R.id.provisionSatellite)
+                .setOnClickListener(this::provisionSatelliteApp);
         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -383,4 +392,94 @@
                 + SatelliteTestApp.getTestSatelliteService()
                 .getIsEmergency());
     }
+
+    private void requestProvisionSubscriberIdsApp(View view) {
+        final AtomicReference<List<ProvisionSubscriberId>> list = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<List<ProvisionSubscriberId>, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(List<ProvisionSubscriberId> result) {
+                        mProvisionSubscriberIdList = result;
+                        list.set(result);
+                        TextView textView = findViewById(R.id.text_id);
+                        String text = "";
+                        for (ProvisionSubscriberId psi : result) {
+                            text += "" + psi + " , ";
+                        }
+                        textView.setText("requestProvisionSubscriberIds: result=" + text);
+                    }
+
+                    @Override
+                    public void onError(SatelliteManager.SatelliteException exception) {
+                        errorCode.set(exception.getErrorCode());
+                        TextView textView = findViewById(R.id.text_id);
+                        textView.setText("Status for requestProvisionSubscriberIds error : "
+                                + SatelliteErrorUtils.mapError(errorCode.get()));
+                    }
+                };
+        mSatelliteManager.requestProvisionSubscriberIds(Runnable::run, receiver);
+    }
+
+    private void requestIsProvisionedApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        enabled.set(result);
+                        TextView textView = findViewById(R.id.text_id);
+                        if (enabled.get()) {
+                            textView.setText("requestIsProvisioned is true");
+                        } else {
+                            textView.setText("Status for requestIsProvisioned result : "
+                                    + enabled.get());
+                        }
+                    }
+
+                    @Override
+                    public void onError(SatelliteManager.SatelliteException exception) {
+                        errorCode.set(exception.getErrorCode());
+                        TextView textView = findViewById(R.id.text_id);
+                        textView.setText("Status for requestIsProvisioned error : "
+                                + SatelliteErrorUtils.mapError(errorCode.get()));
+                    }
+                };
+        if (mProvisionSubscriberIdList == null || mProvisionSubscriberIdList.get(0) == null) {
+            TextView textView = findViewById(R.id.text_id);
+            textView.setText("No ProvisionSubscriberIdList");
+            return;
+        }
+        mSatelliteManager.requestIsProvisioned(mProvisionSubscriberIdList.get(0).getSubscriberId(),
+                Runnable::run, receiver);
+    }
+
+    private void provisionSatelliteApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        enabled.set(result);
+                        TextView textView = findViewById(R.id.text_id);
+                        if (enabled.get()) {
+                            textView.setText("provisionSatellite is true");
+                        } else {
+                            textView.setText("Status for provisionSatellite result : "
+                                    + enabled.get());
+                        }
+                    }
+
+                    @Override
+                    public void onError(SatelliteManager.SatelliteException exception) {
+                        errorCode.set(exception.getErrorCode());
+                        TextView textView = findViewById(R.id.text_id);
+                        textView.setText("Status for provisionSatellite error : "
+                                + SatelliteErrorUtils.mapError(errorCode.get()));
+                    }
+                };
+        mSatelliteManager.provisionSatellite(mProvisionSubscriberIdList, Runnable::run, receiver);
+    }
 }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 304cf2a..97c3d44 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -106,6 +106,7 @@
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.emergency.RadioOnHelper;
 import com.android.internal.telephony.emergency.RadioOnStateListener;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
@@ -254,6 +255,7 @@
     @Mock private SatelliteSOSMessageRecommender mSatelliteSOSMessageRecommender;
     @Mock private EmergencyStateTracker mEmergencyStateTracker;
     @Mock private Resources mMockResources;
+    @Mock private FeatureFlags mFeatureFlags;
     private Phone mPhone0;
     private Phone mPhone1;
 
@@ -281,6 +283,7 @@
         super.setUp();
         doReturn(Looper.getMainLooper()).when(mContext).getMainLooper();
         mTestConnectionService = new TestTelephonyConnectionService(mContext);
+        mTestConnectionService.setFeatureFlags(mFeatureFlags);
         mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
         mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
         // Set configurations statically
@@ -1467,6 +1470,53 @@
     }
 
     /**
+     * Test that the TelephonyConnectionService successfully placing the emergency call based on
+     * CarrierRoaming mode of Satellite.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_exitingSatellite_CarrierRoaming() {
+        when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+
+        // Set config_turn_off_oem_enabled_satellite_during_emergency_call as false
+        doReturn(false).when(mMockResources).getBoolean(anyInt());
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(false).when(mSatelliteController).isDemoModeEnabled();
+
+        doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn();
+        // Disable CarrierRoaming mode
+        doReturn(false).when(mSatelliteController).isInSatelliteModeForCarrierRoaming(any());
+        doReturn(false).when(mSatelliteController).getRequestIsEmergency();
+        // Setup outgoing emergency call
+        setupConnectionServiceInApm();
+
+        // Verify DisconnectCause which not allows emergency call
+        assertNotNull(mConnection.getDisconnectCause());
+        assertEquals(android.telephony.DisconnectCause.SATELLITE_ENABLED,
+                mConnection.getDisconnectCause().getTelephonyDisconnectCause());
+
+        // Enable CarrierRoaming but satellite request was not for an emergency
+        doReturn(true).when(mSatelliteController).isInSatelliteModeForCarrierRoaming(any());
+        doReturn(true).when(mSatelliteController).getRequestIsEmergency();
+        // Setup outgoing emergency call
+        setupConnectionServiceInApm();
+
+        // Verify DisconnectCause which not allows emergency call
+        assertNotNull(mConnection.getDisconnectCause());
+        assertEquals(android.telephony.DisconnectCause.SATELLITE_ENABLED,
+                mConnection.getDisconnectCause().getTelephonyDisconnectCause());
+
+        // Enable CarrierRoaming and satellite request was for an emergency
+        doReturn(true).when(mSatelliteController).isInSatelliteModeForCarrierRoaming(any());
+        doReturn(false).when(mSatelliteController).getRequestIsEmergency();
+        // Setup outgoing emergency call
+        setupConnectionServiceInApm();
+
+        // Verify there is no DisconnectCause which allows emergency call
+        assertNull(mConnection.getDisconnectCause());
+    }
+
+    /**
      * Test that the TelephonyConnectionService successfully turns radio on before placing the
      * call when radio off because bluetooth on and wifi calling is not enabled
      */
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index fb3fef1..457cc67 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -103,6 +103,7 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
@@ -130,10 +131,12 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.TestContext;
+import com.android.internal.telephony.flags.Flags;
 import com.android.phone.R;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -147,10 +150,9 @@
 
 /**
  * Unit tests for EmergencyCallDomainSelector
- */
+*/
 public class EmergencyCallDomainSelectorTest {
     private static final String TAG = "EmergencyCallDomainSelectorTest";
-
     private static final int SLOT_0 = 0;
     private static final int SLOT_0_SUB_ID = 1;
     private static final Uri TEST_URI = Uri.fromParts(PhoneAccount.SCHEME_TEL, "911", null);
@@ -167,6 +169,7 @@
     @Mock private CrossSimRedialingController mCsrdCtrl;
     @Mock private DataConnectionStateHelper mEpdnHelper;
     @Mock private Resources mResources;
+    @Mock private ImsEmergencyRegistrationStateHelper mImsEmergencyRegistrationHelper;
 
     private TelecomManager mTelecomManager;
 
@@ -181,6 +184,8 @@
     private ConnectivityManager.NetworkCallback mNetworkCallback;
     private Consumer<EmergencyRegistrationResult> mResultConsumer;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -2397,6 +2402,8 @@
         bindImsServiceUnregistered();
 
         processAllMessages();
+
+        verify(mImsEmergencyRegistrationHelper, never()).start();
         verify(mCsrdCtrl).startTimer(any(), eq(mDomainSelector), any(),
                 any(), anyBoolean(), anyBoolean(), anyInt());
     }
@@ -2652,6 +2659,67 @@
     }
 
     @Test
+    public void testCrossStackTimerExpiredHangupOngoingDialing() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT, 1);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_HANGUP_EMERGENCY_CALL_FOR_CROSS_SIM_REDIALING);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verify(mImsEmergencyRegistrationHelper).start();
+        verifyCsDialed();
+
+        mDomainSelector.notifyCrossStackTimerExpired();
+
+        verify(mTransportSelectorCallback)
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
+    @Test
+    public void testCrossStackTimerExpiredNotHangupOngoingDialing() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT, 1);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(true).when(mImsEmergencyRegistrationHelper).isImsEmergencyRegistered();
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_HANGUP_EMERGENCY_CALL_FOR_CROSS_SIM_REDIALING);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verify(mImsEmergencyRegistrationHelper).start();
+        verifyCsDialed();
+
+        mDomainSelector.notifyCrossStackTimerExpired();
+
+        verify(mTransportSelectorCallback, never())
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
+    @Test
     public void testMaxCellularTimeout() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
@@ -4380,6 +4448,8 @@
         mDomainSelector.clearResourceConfiguration();
         replaceInstance(DomainSelectorBase.class,
                 "mWwanSelectorCallback", mDomainSelector, mWwanSelectorCallback);
+        replaceInstance(EmergencyCallDomainSelector.class, "mImsEmergencyRegistrationHelper",
+                mDomainSelector, mImsEmergencyRegistrationHelper);
     }
 
     private void verifyCsDialed() {
diff --git a/tests/src/com/android/services/telephony/domainselection/ImsEmergencyRegistrationStateHelperTest.java b/tests/src/com/android/services/telephony/domainselection/ImsEmergencyRegistrationStateHelperTest.java
new file mode 100644
index 0000000..41f1747
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/ImsEmergencyRegistrationStateHelperTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.services.telephony.domainselection;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.ImsStateCallback;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+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;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for ImsEmergencyRegistrationStateHelper.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsEmergencyRegistrationStateHelperTest {
+    private static final String TAG = "ImsEmergencyRegistrationStateHelperTest";
+
+    private static final int SLOT_0 = 0;
+    private static final int SUB_1 = 1;
+
+    @Mock private ImsMmTelManager mMmTelManager;
+
+    private Context mContext;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+    private ImsEmergencyRegistrationStateHelper mImsEmergencyRegistrationHelper;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = new TestContext() {
+            @Override
+            public String getSystemServiceName(Class<?> serviceClass) {
+                if (serviceClass == ImsManager.class) {
+                    return Context.TELEPHONY_IMS_SERVICE;
+                }
+                return super.getSystemServiceName(serviceClass);
+            }
+        };
+
+        mHandlerThread = new HandlerThread(
+                ImsEmergencyRegistrationStateHelperTest.class.getSimpleName());
+        mHandlerThread.start();
+        try {
+            mLooper = new TestableLooper(mHandlerThread.getLooper());
+        } catch (Exception e) {
+            loge("Unable to create looper from handler.");
+        }
+        mImsEmergencyRegistrationHelper = new ImsEmergencyRegistrationStateHelper(
+                mContext, SLOT_0, SUB_1, mHandlerThread.getLooper());
+
+        ImsManager imsManager = mContext.getSystemService(ImsManager.class);
+        when(imsManager.getImsMmTelManager(eq(SUB_1))).thenReturn(mMmTelManager);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mImsEmergencyRegistrationHelper != null) {
+            mImsEmergencyRegistrationHelper.destroy();
+            mImsEmergencyRegistrationHelper = null;
+        }
+        mMmTelManager = null;
+
+        if (mLooper != null) {
+            mLooper.destroy();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testStart() throws ImsException {
+        mImsEmergencyRegistrationHelper.start();
+
+        verify(mMmTelManager).registerImsStateCallback(
+                any(Executor.class), any(ImsStateCallback.class));
+        assertFalse(mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyImsStateCallbackOnAvailable() throws ImsException {
+        ImsStateCallback callback = setUpImsStateCallback();
+        callback.onAvailable();
+        processAllMessages();
+
+        verify(mMmTelManager).registerImsEmergencyRegistrationCallback(
+                any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+        assertFalse(mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyImsRegistrationCallbackOnRegistered() throws ImsException {
+        RegistrationManager.RegistrationCallback callback = setUpImsEmergencyRegistrationCallback();
+
+        assertFalse(mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+
+        callback.onRegistered(getImsEmergencyRegistrationAttributes());
+        processAllMessages();
+
+        assertTrue(mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyImsRegistrationCallbackOnRegisteredUnregistered() throws ImsException {
+        RegistrationManager.RegistrationCallback callback = setUpImsEmergencyRegistrationCallback();
+
+        assertFalse(mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+
+        callback.onRegistered(getImsEmergencyRegistrationAttributes());
+        processAllMessages();
+
+        callback.onUnregistered(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED, 0, null), 0, 0);
+        processAllMessages();
+
+        assertFalse(mImsEmergencyRegistrationHelper.isImsEmergencyRegistered());
+    }
+
+    private ImsStateCallback setUpImsStateCallback() throws ImsException {
+        mImsEmergencyRegistrationHelper.start();
+
+        ArgumentCaptor<ImsStateCallback> callbackCaptor =
+                ArgumentCaptor.forClass(ImsStateCallback.class);
+
+        verify(mMmTelManager).registerImsStateCallback(
+                any(Executor.class), callbackCaptor.capture());
+
+        ImsStateCallback imsStateCallback = callbackCaptor.getValue();
+        assertNotNull(imsStateCallback);
+        return imsStateCallback;
+    }
+
+    private RegistrationManager.RegistrationCallback setUpImsEmergencyRegistrationCallback()
+            throws ImsException {
+        ImsStateCallback imsStateCallback = setUpImsStateCallback();
+        imsStateCallback.onAvailable();
+        processAllMessages();
+
+        ArgumentCaptor<RegistrationManager.RegistrationCallback> callbackCaptor =
+                ArgumentCaptor.forClass(RegistrationManager.RegistrationCallback.class);
+
+        verify(mMmTelManager).registerImsEmergencyRegistrationCallback(
+                any(Executor.class), callbackCaptor.capture());
+
+        RegistrationManager.RegistrationCallback registrationCallback = callbackCaptor.getValue();
+        assertNotNull(registrationCallback);
+        return registrationCallback;
+    }
+
+    private static ImsRegistrationAttributes getImsEmergencyRegistrationAttributes() {
+        return new ImsRegistrationAttributes.Builder(ImsRegistrationImplBase.REGISTRATION_TECH_LTE)
+                .setFlagRegistrationTypeEmergency()
+                .build();
+    }
+
+    private void processAllMessages() {
+        while (!mLooper.getLooper().getQueue().isIdle()) {
+            mLooper.processAllMessages();
+        }
+    }
+
+    private static void loge(String str) {
+        Log.e(TAG, str);
+    }
+}