Reject incoming call for a normal routing emergency call
An emergency call fails when receiving a call at the same time.
Reject incoming call before dialing a normal routing emergency call
in ringing state.
Bug: 339159523
Test: atest TelephonyConnectionServiceTest
Manual test:
1. Make a call from A to DUT.
2. Check whether the call is in alerting state in A.
3. At that moment, dial a normal routing emergency number in DUT
before the incoming call is notified.
Change-Id: Ice2bf79d55cee2e8dc00c5ed73f7006726078a2a
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 1386dce..565393d 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -235,6 +235,7 @@
private DomainSelectionResolver mDomainSelectionResolver;
private EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
private TelephonyConnection mEmergencyConnection;
+ private TelephonyConnection mNormalRoutingEmergencyConnection;
private Executor mDomainSelectionMainExecutor;
private ImsManager mImsManager = null;
private DomainSelectionConnection mDomainSelectionConnection;
@@ -565,6 +566,22 @@
}
/**
+ * A listener for normal routing emergency calls.
+ */
+ private final TelephonyConnection.TelephonyConnectionListener
+ mNormalRoutingEmergencyConnectionListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
+ @Override
+ public void onStateChanged(Connection connection,
+ @Connection.ConnectionState int state) {
+ TelephonyConnection c = (TelephonyConnection) connection;
+ Log.i(this, "onStateChanged normal routing callId=" + c.getTelecomCallId()
+ + ", state=" + state);
+ mEmergencyStateTracker.onNormalRoutingEmergencyCallStateChanged(c, state);
+ }
+ };
+
+ /**
* A listener for emergency calls.
*/
private final TelephonyConnection.TelephonyConnectionListener mEmergencyConnectionListener =
@@ -2262,12 +2279,48 @@
}
}
if (mDomainSelectionResolver.isDomainSelectionSupported()) {
- if (isNormalRouting(phone, number)
- && handleOutgoingCallConnection(number, connection,
- phone, videoState)) {
+ if (isNormalRouting(phone, number)) {
/** Normal routing emergency number shall be handled
* by normal call domain selctor.*/
Log.i(this, "placeOutgoingConnection normal routing number");
+ mNormalRoutingEmergencyConnection = connection;
+ mEmergencyStateTracker.startNormalRoutingEmergencyCall(
+ phone, connection, result -> {
+ Log.i(this, "placeOutgoingConnection normal routing number:"
+ + " result = " + result);
+ if (connection.getState()
+ == Connection.STATE_DISCONNECTED) {
+ Log.i(this, "placeOutgoingConnection "
+ + "reject incoming, dialing canceled");
+ return;
+ }
+ if (!handleOutgoingCallConnection(number, connection,
+ phone, videoState)) {
+ Log.w(this, "placeOriginalConnection - Unexpected, "
+ + "domain selector not available.");
+ // Notify EmergencyStateTracker to reset the state.
+ onLocalHangup(connection);
+ // Try dialing without domain selection
+ // as a best-effort.
+ try {
+ // EmergencyStateTracker ensures this is
+ // on the main thread.
+ connection.setOriginalConnection(phone.dial(number,
+ new ImsPhone.ImsDialArgs.Builder()
+ .setVideoState(videoState)
+ .setIntentExtras(extras)
+ .setRttTextStream(
+ connection.getRttTextStream())
+ .build(),
+ connection::registerForCallEvents));
+ } catch (CallStateException e) {
+ connection.unregisterForCallEvents();
+ handleCallStateException(e, connection, phone);
+ }
+ }
+ });
+ connection.addTelephonyConnectionListener(
+ mNormalRoutingEmergencyConnectionListener);
return;
}
}
@@ -2341,6 +2394,31 @@
}
private void handleOutgoingCallConnectionByCallDomainSelection(
+ int domain, Phone phone, String number, int videoState,
+ TelephonyConnection connection) {
+ if (mNormalRoutingEmergencyConnection == connection) {
+ CompletableFuture<Void> rejectFuture = checkAndRejectIncomingCall(phone, (ret) -> {
+ if (!ret) {
+ Log.i(this, "handleOutgoingCallConnectionByCallDomainSelection "
+ + "reject incoming call failed");
+ }
+ });
+ CompletableFuture<Void> unused = rejectFuture.thenRun(() -> {
+ if (connection.getState() == Connection.STATE_DISCONNECTED) {
+ Log.i(this, "handleOutgoingCallConnectionByCallDomainSelection "
+ + "reject incoming, dialing canceled");
+ return;
+ }
+ handleOutgoingCallConnectionByCallDomainSelection(
+ domain, phone, number, videoState);
+ });
+ return;
+ }
+
+ handleOutgoingCallConnectionByCallDomainSelection(domain, phone, number, videoState);
+ }
+
+ private void handleOutgoingCallConnectionByCallDomainSelection(
int domain, Phone phone, String number, int videoState) {
Log.d(this, "Call Domain Selected : " + domain);
try {
@@ -2442,7 +2520,7 @@
Log.d(LOG_TAG, "Selecting same domain as ongoing call on same subId");
mNormalCallConnection = connection;
handleOutgoingCallConnectionByCallDomainSelection(
- activeCallDomain, phone, number, videoState);
+ activeCallDomain, phone, number, videoState, connection);
return true;
}
@@ -2469,7 +2547,7 @@
mNormalCallConnection = connection;
future.thenAcceptAsync((domain) -> handleOutgoingCallConnectionByCallDomainSelection(
- domain, phone, number, videoState), mDomainSelectionMainExecutor);
+ domain, phone, number, videoState, connection), mDomainSelectionMainExecutor);
if (isPotentialUssdCode) {
Log.v(LOG_TAG, "PotentialUssdCode. Closing connection with DisconnectCause.DIALED_MMI");
@@ -2608,8 +2686,14 @@
Log.i(this, "createEmergencyConnection reject incoming call failed");
}
});
- rejectFuture.thenRun(() -> placeEmergencyConnectionOnSelectedDomain(request,
- resultConnection, phone));
+ rejectFuture.thenRun(() -> {
+ if (resultConnection.getState() == Connection.STATE_DISCONNECTED) {
+ Log.i(this, "createEmergencyConnection "
+ + "reject incoming, dialing canceled");
+ return;
+ }
+ placeEmergencyConnectionOnSelectedDomain(request, resultConnection, phone);
+ });
}, mDomainSelectionMainExecutor);
}
@@ -2629,8 +2713,14 @@
Log.i(this, "dialCsEmergencyCall reject incoming call failed");
}
});
- future.thenRun(() -> placeEmergencyConnectionOnSelectedDomain(request,
- resultConnection, phone));
+ CompletableFuture<Void> unused = future.thenRun(() -> {
+ if (resultConnection.getState() == Connection.STATE_DISCONNECTED) {
+ Log.i(this, "dialCsEmergencyCall "
+ + "reject incoming, dialing canceled");
+ return;
+ }
+ placeEmergencyConnectionOnSelectedDomain(request, resultConnection, phone);
+ });
});
}
@@ -2908,7 +2998,7 @@
mNormalCallConnection = c;
future.thenAcceptAsync((result) -> {
- onNormalCallRedial(c, phone, result, videoState);
+ onNormalCallRedial(phone, result, videoState, c);
}, mDomainSelectionMainExecutor);
return true;
}
@@ -2934,7 +3024,14 @@
Log.i(this, "onEmergencyRedialOnDomain reject incoming call failed");
}
});
- future.thenRun(() -> onEmergencyRedialOnDomainInternal(connection, phone, extras));
+ CompletableFuture<Void> unused = future.thenRun(() -> {
+ if (connection.getState() == Connection.STATE_DISCONNECTED) {
+ Log.i(this, "onEmergencyRedialOnDomain "
+ + "reject incoming, dialing canceled");
+ return;
+ }
+ onEmergencyRedialOnDomainInternal(connection, phone, extras);
+ });
}
private void onEmergencyRedialOnDomainInternal(TelephonyConnection connection,
@@ -3100,6 +3197,28 @@
onEmergencyRedialOnDomain(connection, phone, result);
}
+ private void onNormalCallRedial(Phone phone, @NetworkRegistrationInfo.Domain int domain,
+ int videoState, TelephonyConnection connection) {
+ if (mNormalRoutingEmergencyConnection == connection) {
+ CompletableFuture<Void> rejectFuture = checkAndRejectIncomingCall(phone, (ret) -> {
+ if (!ret) {
+ Log.i(this, "onNormalCallRedial reject incoming call failed");
+ }
+ });
+ CompletableFuture<Void> unused = rejectFuture.thenRun(() -> {
+ if (connection.getState() == Connection.STATE_DISCONNECTED) {
+ Log.i(this, "onNormalCallRedial "
+ + "reject incoming, dialing canceled");
+ return;
+ }
+ onNormalCallRedial(connection, phone, domain, videoState);
+ });
+ return;
+ }
+
+ onNormalCallRedial(connection, phone, domain, videoState);
+ }
+
private void onNormalCallRedial(TelephonyConnection connection, Phone phone,
@NetworkRegistrationInfo.Domain int domain, int videocallState) {
@@ -3150,6 +3269,12 @@
releaseEmergencyCallDomainSelection(true, false);
mEmergencyStateTracker.endCall(c);
}
+ if (mNormalRoutingEmergencyConnection == c) {
+ Log.i(this, "onLocalHangup normal routing " + c.getTelecomCallId());
+ mNormalRoutingEmergencyConnection = null;
+ mEmergencyStateTracker.endNormalRoutingEmergencyCall(c);
+ mIsEmergencyCallPending = false;
+ }
}
@VisibleForTesting
@@ -3168,6 +3293,22 @@
}
@VisibleForTesting
+ public TelephonyConnection getNormalRoutingEmergencyConnection() {
+ return mNormalRoutingEmergencyConnection;
+ }
+
+ @VisibleForTesting
+ public void setNormalRoutingEmergencyConnection(TelephonyConnection c) {
+ mNormalRoutingEmergencyConnection = c;
+ }
+
+ @VisibleForTesting
+ public TelephonyConnection.TelephonyConnectionListener
+ getNormalRoutingEmergencyConnectionListener() {
+ return mNormalRoutingEmergencyConnectionListener;
+ }
+
+ @VisibleForTesting
public TelephonyConnection.TelephonyConnectionListener
getEmergencyConnectionSatelliteListener() {
return mEmergencyConnectionSatelliteListener;
@@ -3556,9 +3697,20 @@
}
Call ringingCall = phone.getRingingCall();
- if (ringingCall == null || !ringingCall.isRinging()) {
- completeConsumer.accept(true);
- return CompletableFuture.completedFuture(null);
+ if (ringingCall == null
+ || ringingCall.getState() == Call.State.IDLE
+ || ringingCall.getState() == Call.State.DISCONNECTED) {
+ // Check the ImsPhoneCall in DISCONNECTING state.
+ Phone imsPhone = phone.getImsPhone();
+ if (imsPhone != null) {
+ ringingCall = imsPhone.getRingingCall();
+ }
+ if (imsPhone == null || ringingCall == null
+ || ringingCall.getState() == Call.State.IDLE
+ || ringingCall.getState() == Call.State.DISCONNECTED) {
+ completeConsumer.accept(true);
+ return CompletableFuture.completedFuture(null);
+ }
}
Log.i(this, "checkAndRejectIncomingCall found a ringing call");
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index db47d67..e76c11c 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -2373,7 +2373,7 @@
setupForDialForDomainSelection(mPhone0, selectedDomain, true);
doReturn(mInternalConnection2).when(mCall).getLatestConnection();
- doReturn(true).when(mCall).isRinging();
+ doReturn(Call.State.INCOMING).when(mCall).getState();
doReturn(mCall).when(mPhone0).getRingingCall();
mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
@@ -2435,7 +2435,7 @@
mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
doReturn(mInternalConnection2).when(mCall).getLatestConnection();
- doReturn(true).when(mCall).isRinging();
+ doReturn(Call.State.DISCONNECTING).when(mCall).getState();
doReturn(mCall).when(mPhone0).getRingingCall();
assertTrue(mTestConnectionService.maybeReselectDomain(c, null, true,
@@ -2487,6 +2487,30 @@
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+ ArgumentCaptor<TelephonyConnection> connectionCaptor =
+ ArgumentCaptor.forClass(TelephonyConnection.class);
+ ArgumentCaptor<Consumer<Boolean>> consumerCaptor = ArgumentCaptor
+ .forClass(Consumer.class);
+
+ verify(mEmergencyStateTracker).startNormalRoutingEmergencyCall(eq(mPhone0),
+ connectionCaptor.capture(), consumerCaptor.capture());
+
+ TelephonyConnection tc = connectionCaptor.getValue();
+
+ assertNotNull(tc);
+ assertNotNull(mTestConnectionService.getNormalRoutingEmergencyConnection());
+ assertEquals(mTestConnectionService.getNormalRoutingEmergencyConnection(), tc);
+
+ verify(mDomainSelectionResolver, never())
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection, never()).createNormalConnection(any(), any());
+
+ Consumer<Boolean> consumer = consumerCaptor.getValue();
+
+ assertNotNull(consumer);
+
+ consumer.accept(true);
+
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
@@ -2504,6 +2528,59 @@
}
@Test
+ public void testDomainSelectionNormalRoutingEmergencyNumberAndDiscarded() throws Exception {
+ setupForCallTest();
+ int selectedDomain = DOMAIN_PS;
+
+ EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+ setupForDialForDomainSelection(mPhone0, selectedDomain, false);
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+ doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+ doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+ anyString());
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<TelephonyConnection> connectionCaptor =
+ ArgumentCaptor.forClass(TelephonyConnection.class);
+ ArgumentCaptor<Consumer<Boolean>> consumerCaptor = ArgumentCaptor
+ .forClass(Consumer.class);
+
+ verify(mEmergencyStateTracker).startNormalRoutingEmergencyCall(eq(mPhone0),
+ connectionCaptor.capture(), consumerCaptor.capture());
+
+ TelephonyConnection tc = connectionCaptor.getValue();
+
+ assertNotNull(tc);
+ assertNotNull(mTestConnectionService.getNormalRoutingEmergencyConnection());
+ assertEquals(mTestConnectionService.getNormalRoutingEmergencyConnection(), tc);
+
+ verify(mDomainSelectionResolver, never())
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection, never()).createNormalConnection(any(), any());
+
+ Consumer<Boolean> consumer = consumerCaptor.getValue();
+
+ assertNotNull(consumer);
+
+ // Discard dialing
+ tc.hangup(android.telephony.DisconnectCause.LOCAL);
+
+ consumer.accept(true);
+
+ verify(mDomainSelectionResolver, never())
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection, never()).createNormalConnection(any(), any());
+ }
+
+ @Test
public void testDomainSelectionDialedSimEmergencyNumberOnlyFalse() throws Exception {
setupForCallTest();
@@ -2578,6 +2655,30 @@
createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+ ArgumentCaptor<TelephonyConnection> connectionCaptor =
+ ArgumentCaptor.forClass(TelephonyConnection.class);
+ ArgumentCaptor<Consumer<Boolean>> consumerCaptor = ArgumentCaptor
+ .forClass(Consumer.class);
+
+ verify(mEmergencyStateTracker).startNormalRoutingEmergencyCall(eq(mPhone0),
+ connectionCaptor.capture(), consumerCaptor.capture());
+
+ TelephonyConnection tc = connectionCaptor.getValue();
+
+ assertNotNull(tc);
+ assertNotNull(mTestConnectionService.getNormalRoutingEmergencyConnection());
+ assertEquals(mTestConnectionService.getNormalRoutingEmergencyConnection(), tc);
+
+ verify(mDomainSelectionResolver, never())
+ .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
+ verify(mNormalCallDomainSelectionConnection, never()).createNormalConnection(any(), any());
+
+ Consumer<Boolean> consumer = consumerCaptor.getValue();
+
+ assertNotNull(consumer);
+
+ consumer.accept(true);
+
verify(mDomainSelectionResolver)
.getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());