Loading flags/domainselection.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -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 } } src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java +26 −8 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); } } /** Loading tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java +89 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading
flags/domainselection.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -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 } }
src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java +26 −8 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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); } } /** Loading
tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java +89 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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(); Loading