Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba60e610 authored by Hunsuk Choi's avatar Hunsuk Choi Committed by Android (Google) Code Review
Browse files

Merge "Wait for the disconnection of ePDN to exit ECBM" into main

parents 56641a6b 7f2160cc
Loading
Loading
Loading
Loading
+167 −14
Original line number Diff line number Diff line
@@ -39,14 +39,18 @@ import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.sysprop.TelephonyProperties;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
import android.telephony.EmergencyRegResult;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;
@@ -65,6 +69,7 @@ import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
@@ -83,6 +88,7 @@ public class EmergencyStateTracker {
    private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true;
    /** Default Emergency Callback Mode exit timeout value. */
    private static final long DEFAULT_ECM_EXIT_TIMEOUT_MS = 300000;
    private static final int DEFAULT_EPDN_DISCONNECTION_TIMEOUT_MS = 500;

    /** The emergency types used when setting the emergency mode on modem. */
    @Retention(RetentionPolicy.SOURCE)
@@ -120,6 +126,9 @@ public class EmergencyStateTracker {
    private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode;
    // Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}.
    private final Set<String> mActiveEmergencyCalls = new ArraySet<>();
    private Phone mPhoneToExit;
    private int mPdnDisconnectionTimeoutMs = DEFAULT_EPDN_DISCONNECTION_TIMEOUT_MS;
    private final Object mLock = new Object();
    private Phone mPhone;
    // Tracks ongoing emergency callId to handle a second emergency call
    private String mOngoingCallId;
@@ -165,6 +174,29 @@ public class EmergencyStateTracker {
        }
    };

    /**
     * TelephonyCallback used to monitor whether ePDN on cellular network is disconnected or not.
     */
    private final class PreciseDataConnectionStateListener extends TelephonyCallback implements
            TelephonyCallback.PreciseDataConnectionStateListener {
        @Override
        public void onPreciseDataConnectionStateChanged(
                @NonNull PreciseDataConnectionState dataConnectionState) {
            ApnSetting apnSetting = dataConnectionState.getApnSetting();
            if ((apnSetting == null)
                    || ((apnSetting.getApnTypeBitmask() | ApnSetting.TYPE_EMERGENCY) == 0)
                    || (dataConnectionState.getTransportType()
                            != AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
                return;
            }
            int state = dataConnectionState.getState();
            Rlog.d(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + state);
            if (state == TelephonyManager.DATA_DISCONNECTED) exitEmergencyModeIfDelayed();
        }
    }

    private PreciseDataConnectionStateListener mDataConnectionStateListener;

    /** PhoneFactory Dependencies for testing. */
    @VisibleForTesting
    public interface PhoneFactoryProxy {
@@ -188,6 +220,8 @@ public class EmergencyStateTracker {
    @VisibleForTesting
    public interface TelephonyManagerProxy {
        int getPhoneCount();
        void registerTelephonyCallback(int subId, Executor executor, TelephonyCallback callback);
        void unregisterTelephonyCallback(TelephonyCallback callback);
    }

    private final TelephonyManagerProxy mTelephonyManagerProxy;
@@ -195,7 +229,6 @@ public class EmergencyStateTracker {
    private static class TelephonyManagerProxyImpl implements TelephonyManagerProxy {
        private final TelephonyManager mTelephonyManager;


        TelephonyManagerProxyImpl(Context context) {
            mTelephonyManager = new TelephonyManager(context);
        }
@@ -204,6 +237,18 @@ public class EmergencyStateTracker {
        public int getPhoneCount() {
            return mTelephonyManager.getActiveModemCount();
        }

        @Override
        public void registerTelephonyCallback(int subId,
                Executor executor, TelephonyCallback callback) {
            TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
            tm.registerTelephonyCallback(executor, callback);
        }

        @Override
        public void unregisterTelephonyCallback(TelephonyCallback callback) {
            mTelephonyManager.unregisterTelephonyCallback(callback);
        }
    }

    /**
@@ -215,11 +260,15 @@ public class EmergencyStateTracker {
    }

    @VisibleForTesting
    public static final int MSG_SET_EMERGENCY_MODE_DONE = 1;
    public static final int MSG_SET_EMERGENCY_MODE = 1;
    @VisibleForTesting
    public static final int MSG_EXIT_EMERGENCY_MODE = 2;
    @VisibleForTesting
    public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2;
    public static final int MSG_SET_EMERGENCY_MODE_DONE = 3;
    @VisibleForTesting
    public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 3;
    public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 4;
    @VisibleForTesting
    public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 5;

    private class MyHandler extends Handler {

@@ -263,7 +312,7 @@ public class EmergencyStateTracker {
                            if (mIsEmergencyCallStartedDuringEmergencySms) {
                                Phone phone = mPhone;
                                mPhone = null;
                                exitEmergencyMode(mSmsPhone, emergencyType);
                                exitEmergencyMode(mSmsPhone, emergencyType, false);
                                // Restore call phone for further use.
                                mPhone = phone;

@@ -322,6 +371,27 @@ public class EmergencyStateTracker {
                    }
                    break;
                }
                case MSG_EXIT_EMERGENCY_MODE: {
                    Rlog.v(TAG, "MSG_EXIT_EMERGENCY_MODE");
                    exitEmergencyModeIfDelayed();
                    break;
                }
                case MSG_SET_EMERGENCY_MODE: {
                    AsyncResult ar = (AsyncResult) msg.obj;
                    Integer emergencyType = (Integer) ar.userObj;
                    Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE for "
                            + emergencyTypeToString(emergencyType) + ", " + mEmergencyMode);
                    // Should be reached here only when starting a new emergency service
                    // while exiting emergency callback mode on the other slot.
                    if (mEmergencyMode != MODE_EMERGENCY_WWAN) return;
                    final Phone phone = (mPhone != null) ? mPhone : mSmsPhone;
                    if (phone != null) {
                        mWasEmergencyModeSetOnModem = true;
                        phone.setEmergencyMode(MODE_EMERGENCY_WWAN,
                                mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE_DONE, emergencyType));
                    }
                    break;
                }
                default:
                    break;
            }
@@ -459,7 +529,7 @@ public class EmergencyStateTracker {
            // exit the emergency mode when receiving the result of setting the emergency mode and
            // the emergency mode for this call will be restarted after the exit complete.
            if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
            }

            mPhone = phone;
@@ -511,7 +581,7 @@ public class EmergencyStateTracker {
                            MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
                }
            } else {
                exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL);
                exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, false);
                clearEmergencyCallInfo();
            }
        }
@@ -576,9 +646,28 @@ public class EmergencyStateTracker {
            return;
        }

        synchronized (mLock) {
            unregisterForDataConnectionStateChanges();
            if (mPhoneToExit != null) {
                if (emergencyType != EMERGENCY_TYPE_CALL) {
                    setIsInEmergencyCall(false);
                }
                mOnEcmExitCompleteRunnable = null;
                if (mPhoneToExit != phone) {
                    // Exit emergency mode on the other phone first,
                    // then set emergency mode on the given phone.
                    mPhoneToExit.exitEmergencyMode(
                            mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE,
                            Integer.valueOf(emergencyType)));
                    mPhoneToExit = null;
                    return;
                }
                mPhoneToExit = null;
            }
            mWasEmergencyModeSetOnModem = true;
            phone.setEmergencyMode(mode, m);
        }
    }

    private void completeEmergencyMode(@EmergencyType int emergencyType) {
        completeEmergencyMode(emergencyType, DisconnectCause.NOT_DISCONNECTED);
@@ -650,8 +739,10 @@ public class EmergencyStateTracker {
     *
     * @param phone the {@code Phone} to exit the emergency mode.
     * @param emergencyType the emergency type to identify an emergency call or SMS.
     * @param waitForPdnDisconnect indicates whether it shall wait for the disconnection of ePDN.
     */
    private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType) {
    private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType,
            boolean waitForPdnDisconnect) {
        Rlog.i(TAG, "exitEmergencyMode for " + emergencyTypeToString(emergencyType));

        if (emergencyType == EMERGENCY_TYPE_CALL) {
@@ -687,9 +778,24 @@ public class EmergencyStateTracker {
            return;
        }

        synchronized (mLock) {
            mWasEmergencyModeSetOnModem = false;
            if (waitForPdnDisconnect) {
                registerForDataConnectionStateChanges(phone);
                mPhoneToExit = phone;
                if (mPdnDisconnectionTimeoutMs > 0) {
                    // To avoid waiting for the disconnection indefinitely.
                    mHandler.sendEmptyMessageDelayed(MSG_EXIT_EMERGENCY_MODE,
                            mPdnDisconnectionTimeoutMs);
                }
                return;
            } else {
                unregisterForDataConnectionStateChanges();
                mPhoneToExit = null;
            }
            phone.exitEmergencyMode(m);
        }
    }

    /** Returns last {@link EmergencyRegResult} as set by {@code setEmergencyMode()}. */
    public EmergencyRegResult getEmergencyRegResult() {
@@ -887,7 +993,9 @@ public class EmergencyStateTracker {
            gsmCdmaPhone.notifyEmergencyCallRegistrants(false);

            // Exit emergency mode on modem.
            exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL);
            // b/299866883: Wait for the disconnection of ePDN before calling exitEmergencyMode.
            exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL,
                    mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_PS);
        }

        mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
@@ -1026,7 +1134,7 @@ public class EmergencyStateTracker {
                            MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
                }
            } else {
                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
            }

            clearEmergencySmsInfo();
@@ -1334,4 +1442,49 @@ public class EmergencyStateTracker {
        Rlog.i(TAG, "updateNoSimEcbmSupported preference updated slotIndex=" + slotIndex
                + ", supported=" + carrierConfig);
    }

    /** For test purpose only */
    @VisibleForTesting
    public void setPdnDisconnectionTimeoutMs(int timeout) {
        mPdnDisconnectionTimeoutMs = timeout;
    }

    private void exitEmergencyModeIfDelayed() {
        synchronized (mLock) {
            if (mPhoneToExit != null) {
                unregisterForDataConnectionStateChanges();
                mPhoneToExit.exitEmergencyMode(
                        mHandler.obtainMessage(MSG_EXIT_EMERGENCY_MODE_DONE,
                                Integer.valueOf(EMERGENCY_TYPE_CALL)));
                mPhoneToExit = null;
            }
        }
    }

    /**
     * Registers for changes to data connection state.
     */
    private void registerForDataConnectionStateChanges(Phone phone) {
        if ((mDataConnectionStateListener != null) || (phone == null)) {
            return;
        }
        Rlog.i(TAG, "registerForDataConnectionStateChanges");

        mDataConnectionStateListener = new PreciseDataConnectionStateListener();
        mTelephonyManagerProxy.registerTelephonyCallback(phone.getSubId(),
                mHandler::post, mDataConnectionStateListener);
    }

    /**
     * Unregisters for changes to data connection state.
     */
    private void unregisterForDataConnectionStateChanges() {
        if (mDataConnectionStateListener == null) {
            return;
        }
        Rlog.i(TAG, "unregisterForDataConnectionStateChanges");

        mTelephonyManagerProxy.unregisterTelephonyCallback(mDataConnectionStateListener);
        mDataConnectionStateListener = null;
    }
}
+211 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import android.telephony.DisconnectCause;
import android.telephony.EmergencyRegResult;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -597,6 +598,216 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
        assertFalse(emergencyStateTracker.isInEmergencyMode());
    }

    /**
     * Test that once endCall() for IMS call is called and we enter ECM, then we exit ECM
     * after the specified timeout.
     */
    @Test
    @SmallTest
    public void endCall_entersEcm_thenExitsEcmAfterTimeoutImsCall() throws Exception {
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        // Create test Phone
        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
                /* isRadioOn= */ true);
        setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
        setUpAsyncResultForExitEmergencyMode(testPhone);
        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
                TEST_CALL_ID, false);
        // Set call to ACTIVE
        emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
        emergencyStateTracker.onEmergencyCallDomainUpdated(
                PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
        // Set ecm as supported
        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);

        processAllMessages();

        emergencyStateTracker.endCall(TEST_CALL_ID);

        assertTrue(emergencyStateTracker.isInEcm());

        Context mockContext = mock(Context.class);
        replaceInstance(EmergencyStateTracker.class, "mContext",
                emergencyStateTracker, mockContext);
        processAllFutureMessages();

        ArgumentCaptor<TelephonyCallback> callbackCaptor =
                ArgumentCaptor.forClass(TelephonyCallback.class);

        verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
                  any(), callbackCaptor.capture());

        TelephonyCallback callback = callbackCaptor.getValue();

        assertNotNull(callback);

        // Verify exitEmergencyMode() is called after timeout
        verify(testPhone).exitEmergencyMode(any(Message.class));
        assertFalse(emergencyStateTracker.isInEmergencyMode());
        assertFalse(emergencyStateTracker.isInEcm());
        verify(mTelephonyManagerProxy).unregisterTelephonyCallback(eq(callback));
    }

    /**
     * Test that startEmergencyCall() is called right after exiting ECM on the same slot.
     */
    @Test
    @SmallTest
    public void exitEcm_thenDialEmergencyCallOnTheSameSlotRightAfter() throws Exception {
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        emergencyStateTracker.setPdnDisconnectionTimeoutMs(0);
        // Create test Phone
        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
                /* isRadioOn= */ true);
        setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
        setUpAsyncResultForExitEmergencyMode(testPhone);

        verify(testPhone, times(0)).setEmergencyMode(anyInt(), any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));

        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
                TEST_CALL_ID, false);
        processAllMessages();

        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));

        // Set call to ACTIVE
        emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
        emergencyStateTracker.onEmergencyCallDomainUpdated(
                PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
        // Set ecm as supported
        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);

        processAllMessages();

        emergencyStateTracker.endCall(TEST_CALL_ID);

        assertTrue(emergencyStateTracker.isInEcm());

        Context mockContext = mock(Context.class);
        replaceInstance(EmergencyStateTracker.class, "mContext",
                emergencyStateTracker, mockContext);
        processAllFutureMessages();

        ArgumentCaptor<TelephonyCallback> callbackCaptor =
                ArgumentCaptor.forClass(TelephonyCallback.class);

        verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
                  any(), callbackCaptor.capture());

        TelephonyCallback callback = callbackCaptor.getValue();

        assertNotNull(callback);
        assertFalse(emergencyStateTracker.isInEmergencyMode());
        assertFalse(emergencyStateTracker.isInEcm());
        verify(mTelephonyManagerProxy, times(0)).unregisterTelephonyCallback(eq(callback));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
                any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));

        replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);

        // dial on the same slot
        unused = emergencyStateTracker.startEmergencyCall(testPhone, TEST_CALL_ID, false);
        processAllMessages();

        assertTrue(emergencyStateTracker.isInEmergencyMode());
        assertFalse(emergencyStateTracker.isInEcm());
        verify(mTelephonyManagerProxy, times(1)).unregisterTelephonyCallback(eq(callback));
        verify(testPhone, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
                any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
    }

    /**
     * Test that startEmergencyCall() is called right after exiting ECM on the other slot.
     */
    @Test
    @SmallTest
    public void exitEcm_thenDialEmergencyCallOnTheOtherSlotRightAfter() throws Exception {
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        emergencyStateTracker.setPdnDisconnectionTimeoutMs(0);
        // Create test Phone
        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
                /* isRadioOn= */ true);
        setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
        setUpAsyncResultForExitEmergencyMode(testPhone);

        verify(testPhone, times(0)).setEmergencyMode(anyInt(), any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));

        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
                TEST_CALL_ID, false);
        processAllMessages();

        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));

        // Set call to ACTIVE
        emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
        emergencyStateTracker.onEmergencyCallDomainUpdated(
                PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
        // Set ecm as supported
        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);

        processAllMessages();

        emergencyStateTracker.endCall(TEST_CALL_ID);

        assertTrue(emergencyStateTracker.isInEcm());

        Context mockContext = mock(Context.class);
        replaceInstance(EmergencyStateTracker.class, "mContext",
                emergencyStateTracker, mockContext);
        processAllFutureMessages();

        ArgumentCaptor<TelephonyCallback> callbackCaptor =
                ArgumentCaptor.forClass(TelephonyCallback.class);

        verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
                  any(), callbackCaptor.capture());

        TelephonyCallback callback = callbackCaptor.getValue();

        assertNotNull(callback);
        assertFalse(emergencyStateTracker.isInEmergencyMode());
        assertFalse(emergencyStateTracker.isInEcm());
        verify(mTelephonyManagerProxy, times(0)).unregisterTelephonyCallback(eq(callback));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
                any(Message.class));
        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));

        Phone phone1 = getPhone(1);
        verify(phone1, times(0)).setEmergencyMode(anyInt(), any(Message.class));
        verify(phone1, times(0)).exitEmergencyMode(any(Message.class));

        replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);

        // dial on the other slot
        unused = emergencyStateTracker.startEmergencyCall(phone1, TEST_CALL_ID, false);
        processAllMessages();

        assertTrue(emergencyStateTracker.isInEmergencyMode());
        assertFalse(emergencyStateTracker.isInEcm());
        verify(mTelephonyManagerProxy, times(1)).unregisterTelephonyCallback(eq(callback));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
                any(Message.class));
        verify(testPhone, times(1)).exitEmergencyMode(any(Message.class));
        verify(phone1, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        verify(phone1, times(0)).exitEmergencyMode(any(Message.class));
    }

    /**
     * Test that after exitEmergencyCallbackMode() is called, the correct intents are sent and
     * emergency mode is exited on the modem.