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

Commit e2174101 authored by Hwangoo Park's avatar Hwangoo Park
Browse files

Add cross stack redialing during emergency call

If an emergency call is initiated during a non-emergency NTN session,
the subscription currently in progress with the emergency call becomes
inactive and the subscription in other stack becomes active. Due to this
situation, an emergency call is connected through other stack about 3
minutes later.
This change is to ensure that when the above situation occurs, the
emergency call can be connected quickly by determining whether an
emergency call is possible to other stack at an early stage of the
domain selection procedure.

Bug: 389190613
Flag: com.android.internal.telephony.flags.perform_cross_stack_redial_check_for_emergency_call
Test: atest EmergencyStateTrackerTest
Test: manual (verified quick cross stack redialing during emergency call
while non-emergency NTN session is in progress)
Test: manual (verified emergency call using test emergency number)

Change-Id: I86f59b4718e93f041b22621e45e22b60152b7636
parent 041542a6
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -71,3 +71,14 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

# OWNER=hwangoo TARGET=25Q2
flag {
    name: "perform_cross_stack_redial_check_for_emergency_call"
    namespace: "telephony"
    description: "This flag performs a quick cross stack redial if the subscription is being invalid and an exception occurs, while an emergency call is in progress."
    bug:"389190613"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+26 −8
Original line number Diff line number Diff line
@@ -312,8 +312,24 @@ public class EmergencyStateTracker {
                    maybeNotifyTransportChangeCompleted(emergencyType, false);

                    if (emergencyType == EMERGENCY_TYPE_CALL) {
                        // If the emergency registration result(mLastEmergencyRegistrationResult) is
                        // null, it means that the emergency mode is not set properly on the modem.
                        // Therefore, based on the emergency registration result and current
                        // subscription status, the current phone is not available for an emergency
                        // call, so we check if an emergency call is possible through cross stack.
                        if (mFeatureFlags.performCrossStackRedialCheckForEmergencyCall()
                                && mLastEmergencyRegistrationResult == null
                                && mPhone != null
                                && !SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())
                                && needToSwitchPhone(mPhone)) {
                            Rlog.i(TAG, "setEmergencyMode failed: need to switch stacks.");
                            mEmergencyMode = MODE_EMERGENCY_NONE;
                            completeEmergencyMode(emergencyType,
                                    DisconnectCause.EMERGENCY_PERM_FAILURE);
                        } else {
                            setIsInEmergencyCall(true);
                            completeEmergencyMode(emergencyType);
                        }

                        // Case 1) When the emergency call is setting the emergency mode and
                        // the emergency SMS is being sent, completes the SMS future also.
@@ -861,23 +877,25 @@ public class EmergencyStateTracker {

    private void completeEmergencyMode(@EmergencyType int emergencyType,
            @DisconnectCauses int result) {
        CompletableFuture<Integer> emergencyModeFuture = null;

        if (emergencyType == EMERGENCY_TYPE_CALL) {
            if (mCallEmergencyModeFuture != null && !mCallEmergencyModeFuture.isDone()) {
                mCallEmergencyModeFuture.complete(result);
            }
            emergencyModeFuture = mCallEmergencyModeFuture;

            if (result != DisconnectCause.NOT_DISCONNECTED) {
                clearEmergencyCallInfo();
            }
        } else if (emergencyType == EMERGENCY_TYPE_SMS) {
            if (mSmsEmergencyModeFuture != null && !mSmsEmergencyModeFuture.isDone()) {
                mSmsEmergencyModeFuture.complete(result);
            }
            emergencyModeFuture = mSmsEmergencyModeFuture;

            if (result != DisconnectCause.NOT_DISCONNECTED) {
                clearEmergencySmsInfo();
            }
        }

        if (emergencyModeFuture != null && !emergencyModeFuture.isDone()) {
            emergencyModeFuture.complete(result);
        }
    }

    /**
+89 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL;
import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS;

@@ -74,10 +75,12 @@ import androidx.test.filters.SmallTest;

import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.GsmCdmaPhone;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.data.PhoneSwitcher;
@@ -107,6 +110,12 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
    private static final int TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS = 3000;
    private static final EmergencyRegistrationResult E_REG_RESULT = new EmergencyRegistrationResult(
            EUTRAN, REGISTRATION_STATE_HOME, DOMAIN_CS_PS, true, true, 0, 1, "001", "01", "US");
    private static final EmergencyRegistrationResult UNKNOWN_E_REG_RESULT =
            new EmergencyRegistrationResult(
                    AccessNetworkConstants.AccessNetworkType.UNKNOWN,
                    REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
                    NetworkRegistrationInfo.DOMAIN_UNKNOWN,
                    false, false, 0, 0, "", "", "");

    @Mock EmergencyStateTracker.PhoneFactoryProxy mPhoneFactoryProxy;
    @Mock EmergencyStateTracker.PhoneSwitcherProxy mPhoneSwitcherProxy;
@@ -125,6 +134,7 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
                .when(mTelephonyManagerProxy).getSimState(anyInt());
        doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification();
        doReturn(true).when(mFeatureFlags).disableEcbmBasedOnRat();
        doReturn(true).when(mFeatureFlags).performCrossStackRedialCheckForEmergencyCall();
    }

    @After
@@ -3491,6 +3501,74 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
        verify(phone1, never()).setEmergencyMode(anyInt(), any(Message.class));
    }

    @Test
    @SmallTest
    public void testSwitchPhoneWhenNonEmergencyNtnSessionInProgress() {
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
                /* isRadioOn= */ true);
        setUpAsyncResultForSetEmergencyMode(
                phone0, UNKNOWN_E_REG_RESULT, RILConstants.INTERNAL_ERR);
        // Start an emergency call over Phone0
        CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
                mTestConnection1, false);

        Phone phone1 = getPhone(1);
        // Phone0: Disable NTN
        doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                .when(phone0).getSubId();
        doReturn(TelephonyManager.SIM_STATE_ABSENT)
                .when(mTelephonyManagerProxy).getSimState(eq(0));
        // Phone1: Enable TN
        doReturn(2).when(phone1).getSubId();
        doReturn(TelephonyManager.SIM_STATE_READY)
                .when(mTelephonyManagerProxy).getSimState(eq(1));

        processAllMessages();

        verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        assertFalse(emergencyStateTracker.isInEmergencyMode());
        assertTrue(future.isDone());
        // Expect: DisconnectCause#EMERGENCY_PERM_FAILURE
        assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
                Integer.valueOf(DisconnectCause.EMERGENCY_PERM_FAILURE));
    }

    @Test
    @SmallTest
    public void testSwitchPhoneWhenNonEmergencyNtnSessionInProgressAndFlagDisabled() {
        doReturn(false).when(mFeatureFlags).performCrossStackRedialCheckForEmergencyCall();
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
                /* isRadioOn= */ true);
        setUpAsyncResultForSetEmergencyMode(
                phone0, UNKNOWN_E_REG_RESULT, RILConstants.INTERNAL_ERR);
        // Start an emergency call over Phone0
        CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
                mTestConnection1, false);

        Phone phone1 = getPhone(1);
        // Phone0: Disable NTN
        doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                .when(phone0).getSubId();
        doReturn(TelephonyManager.SIM_STATE_ABSENT)
                .when(mTelephonyManagerProxy).getSimState(eq(0));
        // Phone1: Enable TN
        doReturn(2).when(phone1).getSubId();
        doReturn(TelephonyManager.SIM_STATE_READY)
                .when(mTelephonyManagerProxy).getSimState(eq(1));

        processAllMessages();

        verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
        assertTrue(future.isDone());
        // Expect: DisconnectCause#NOT_DISCONNECTED
        assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
                Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
    }

    private EmergencyStateTracker setupEmergencyStateTracker(
            boolean isSuplDdsSwitchRequiredForEmergencyCall) {
        doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
@@ -3571,6 +3649,17 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
        }).when(phone).setEmergencyMode(anyInt(), any(Message.class));
    }

    private void setUpAsyncResultForSetEmergencyMode(Phone phone,
            EmergencyRegistrationResult regResult, int rilError) {
        doAnswer((invocation) -> {
            Object[] args = invocation.getArguments();
            final Message msg = (Message) args[1];
            AsyncResult.forMessage(msg, regResult, CommandException.fromRilErrno(rilError));
            msg.sendToTarget();
            return null;
        }).when(phone).setEmergencyMode(anyInt(), any(Message.class));
    }

    private void setUpAsyncResultForExitEmergencyMode(Phone phone) {
        doAnswer((invocation) -> {
            Object[] args = invocation.getArguments();