Loading src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java +86 −5 Original line number Diff line number Diff line Loading @@ -163,6 +163,7 @@ public class EmergencyStateTracker { private Runnable mOnEcmExitCompleteRunnable; private int mOngoingCallProperties; private boolean mSentEmergencyCallState; private android.telecom.Connection mNormalRoutingEmergencyConnection; /** For emergency SMS */ private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>(); Loading Loading @@ -633,8 +634,14 @@ public class EmergencyStateTracker { mOngoingConnection = c; mIsTestEmergencyNumber = isTestEmergencyNumber; sendEmergencyCallStateChange(mPhone, true); final android.telecom.Connection expectedConnection = mOngoingConnection; maybeRejectIncomingCall(result -> { Rlog.i(TAG, "maybeRejectIncomingCall : result = " + result); if (!Objects.equals(mOngoingConnection, expectedConnection)) { Rlog.i(TAG, "maybeRejectIncomingCall " + expectedConnection.getTelecomCallId() + " canceled."); return; } turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber); }); return mCallEmergencyModeFuture; Loading Loading @@ -1933,6 +1940,27 @@ public class EmergencyStateTracker { } } private Call getRingingCall(Phone phone) { if (phone == null) return null; Call ringingCall = phone.getRingingCall(); if (ringingCall != null && ringingCall.getState() != Call.State.IDLE && ringingCall.getState() != Call.State.DISCONNECTED) { return ringingCall; } // 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) { return ringingCall; } return null; } /** * Ensures that there is no incoming call. * Loading @@ -1951,14 +1979,14 @@ public class EmergencyStateTracker { Call ringingCall = null; for (Phone phone : phones) { ringingCall = phone.getRingingCall(); if (ringingCall != null && ringingCall.isRinging()) { ringingCall = getRingingCall(phone); if (ringingCall != null) { Rlog.i(TAG, "maybeRejectIncomingCall found a ringing call"); break; } } if (ringingCall == null || !ringingCall.isRinging()) { if (ringingCall == null) { if (completeConsumer != null) { completeConsumer.accept(true); } Loading Loading @@ -2007,9 +2035,13 @@ public class EmergencyStateTracker { */ private void handleNewRingingConnection(Message msg) { Connection c = (Connection) ((AsyncResult) msg.obj).result; if (c == null || mOngoingConnection == null if (c == null) return; if ((mNormalRoutingEmergencyConnection == null || mNormalRoutingEmergencyConnection.getState() == STATE_ACTIVE || mNormalRoutingEmergencyConnection.getState() == STATE_DISCONNECTED) && (mOngoingConnection == null || mOngoingConnection.getState() == STATE_ACTIVE || mOngoingConnection.getState() == STATE_DISCONNECTED) { || mOngoingConnection.getState() == STATE_DISCONNECTED)) { return; } if ((c.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) Loading @@ -2024,4 +2056,53 @@ public class EmergencyStateTracker { } } } /** * Indicates the start of a normal routing emergency call. * * <p> * Handles turning on radio and switching DDS. * * @param phone the {@code Phone} on which to process the emergency call. * @param c the {@code Connection} on which to process the emergency call. * @param completeConsumer The consumer to call once rejecting incoming call completes, * provides {@code true} result if operation completes successfully * or {@code false} if the operation timed out/failed. */ public void startNormalRoutingEmergencyCall(@NonNull Phone phone, @NonNull android.telecom.Connection c, @NonNull Consumer<Boolean> completeConsumer) { Rlog.i(TAG, "startNormalRoutingEmergencyCall: phoneId=" + phone.getPhoneId() + ", callId=" + c.getTelecomCallId()); mNormalRoutingEmergencyConnection = c; maybeRejectIncomingCall(completeConsumer); } /** * Indicates the termination of a normal routing emergency call. * * @param c the normal routing emergency call disconnected. */ public void endNormalRoutingEmergencyCall(@NonNull android.telecom.Connection c) { if (c != mNormalRoutingEmergencyConnection) return; Rlog.i(TAG, "endNormalRoutingEmergencyCall: callId=" + c.getTelecomCallId()); mNormalRoutingEmergencyConnection = null; } /** * Handles the normal routing emergency call state change. * * @param c the call whose state has changed * @param state the new call state */ public void onNormalRoutingEmergencyCallStateChanged(android.telecom.Connection c, @android.telecom.Connection.ConnectionState int state) { if (c != mNormalRoutingEmergencyConnection) return; // If the call is connected, we don't need to monitor incoming call any more. if (state == android.telecom.Connection.STATE_ACTIVE || state == android.telecom.Connection.STATE_DISCONNECTED) { endNormalRoutingEmergencyCall(c); } } } tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java +115 −1 Original line number Diff line number Diff line Loading @@ -2687,7 +2687,7 @@ public class EmergencyStateTrackerTest extends TelephonyTest { Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(c).when(call).getLatestConnection(); doReturn(true).when(call).isRinging(); doReturn(Call.State.INCOMING).when(call).getState(); doReturn(call).when(phone).getRingingCall(); setEcmSupportedConfig(phone, true); Loading Loading @@ -2789,6 +2789,120 @@ public class EmergencyStateTrackerTest extends TelephonyTest { } } /** * Test that the EmergencyStateTracker rejects incoming call when starting * a normal routing emergency call. */ @Test @SmallTest public void testNormalRoutingEmergencyCallRejectRingingCall() { Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */, false /* isRadioOn */); when(phone.getSubId()).thenReturn(1); Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(c).when(call).getLatestConnection(); doReturn(Call.State.DISCONNECTING).when(call).getState(); doReturn(call).when(phone).getRingingCall(); setEcmSupportedConfig(phone, true); EmergencyStateTracker testEst = setupEmergencyStateTracker( false /* isSuplDdsSwitchRequiredForEmergencyCall */); // There is an ongoing normal routing emergency call. testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {}); // Verify rejecting ringing call. try { verify(call).hangup(); } catch (CallStateException e) { } } /** * Test that the EmergencyStateTracker rejects incoming call if there is * a normal routing emergency call in dialing state. */ @Test @SmallTest public void testNormalRoutingRejectNewIncomingCall() { Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */, false /* isRadioOn */); when(phone.getSubId()).thenReturn(1); setEcmSupportedConfig(phone, true); EmergencyStateTracker testEst = setupEmergencyStateTracker( false /* isSuplDdsSwitchRequiredForEmergencyCall */); ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); verify(phone).registerForNewRingingConnection(handlerCaptor.capture(), intCaptor.capture(), any()); assertNotNull(handlerCaptor.getValue()); assertNotNull(intCaptor.getValue()); // There is an ongoing normal routing emergency call. testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {}); Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(call).when(c).getCall(); Message msg = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue()); AsyncResult.forMessage(msg, c, null); msg.sendToTarget(); processAllMessages(); // Verify rejecting incoming call. try { verify(call).hangup(); } catch (CallStateException e) { } } @Test @SmallTest public void testNormalRoutingDiscardedNotRejectNewIncomingCall() { Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */, false /* isRadioOn */); when(phone.getSubId()).thenReturn(1); setEcmSupportedConfig(phone, true); EmergencyStateTracker testEst = setupEmergencyStateTracker( false /* isSuplDdsSwitchRequiredForEmergencyCall */); ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); verify(phone).registerForNewRingingConnection(handlerCaptor.capture(), intCaptor.capture(), any()); assertNotNull(handlerCaptor.getValue()); assertNotNull(intCaptor.getValue()); // Start normal routing emergency call. testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {}); // Discard normal routing emergency call. testEst.endNormalRoutingEmergencyCall(mTestConnection1); Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(call).when(c).getCall(); Message msg = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue()); AsyncResult.forMessage(msg, c, null); msg.sendToTarget(); processAllMessages(); // Verify not rejecting incoming call. try { verify(call, never()).hangup(); } catch (CallStateException e) { } } /** * Test that emergency call state changes are sent. */ Loading Loading
src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java +86 −5 Original line number Diff line number Diff line Loading @@ -163,6 +163,7 @@ public class EmergencyStateTracker { private Runnable mOnEcmExitCompleteRunnable; private int mOngoingCallProperties; private boolean mSentEmergencyCallState; private android.telecom.Connection mNormalRoutingEmergencyConnection; /** For emergency SMS */ private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>(); Loading Loading @@ -633,8 +634,14 @@ public class EmergencyStateTracker { mOngoingConnection = c; mIsTestEmergencyNumber = isTestEmergencyNumber; sendEmergencyCallStateChange(mPhone, true); final android.telecom.Connection expectedConnection = mOngoingConnection; maybeRejectIncomingCall(result -> { Rlog.i(TAG, "maybeRejectIncomingCall : result = " + result); if (!Objects.equals(mOngoingConnection, expectedConnection)) { Rlog.i(TAG, "maybeRejectIncomingCall " + expectedConnection.getTelecomCallId() + " canceled."); return; } turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber); }); return mCallEmergencyModeFuture; Loading Loading @@ -1933,6 +1940,27 @@ public class EmergencyStateTracker { } } private Call getRingingCall(Phone phone) { if (phone == null) return null; Call ringingCall = phone.getRingingCall(); if (ringingCall != null && ringingCall.getState() != Call.State.IDLE && ringingCall.getState() != Call.State.DISCONNECTED) { return ringingCall; } // 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) { return ringingCall; } return null; } /** * Ensures that there is no incoming call. * Loading @@ -1951,14 +1979,14 @@ public class EmergencyStateTracker { Call ringingCall = null; for (Phone phone : phones) { ringingCall = phone.getRingingCall(); if (ringingCall != null && ringingCall.isRinging()) { ringingCall = getRingingCall(phone); if (ringingCall != null) { Rlog.i(TAG, "maybeRejectIncomingCall found a ringing call"); break; } } if (ringingCall == null || !ringingCall.isRinging()) { if (ringingCall == null) { if (completeConsumer != null) { completeConsumer.accept(true); } Loading Loading @@ -2007,9 +2035,13 @@ public class EmergencyStateTracker { */ private void handleNewRingingConnection(Message msg) { Connection c = (Connection) ((AsyncResult) msg.obj).result; if (c == null || mOngoingConnection == null if (c == null) return; if ((mNormalRoutingEmergencyConnection == null || mNormalRoutingEmergencyConnection.getState() == STATE_ACTIVE || mNormalRoutingEmergencyConnection.getState() == STATE_DISCONNECTED) && (mOngoingConnection == null || mOngoingConnection.getState() == STATE_ACTIVE || mOngoingConnection.getState() == STATE_DISCONNECTED) { || mOngoingConnection.getState() == STATE_DISCONNECTED)) { return; } if ((c.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) Loading @@ -2024,4 +2056,53 @@ public class EmergencyStateTracker { } } } /** * Indicates the start of a normal routing emergency call. * * <p> * Handles turning on radio and switching DDS. * * @param phone the {@code Phone} on which to process the emergency call. * @param c the {@code Connection} on which to process the emergency call. * @param completeConsumer The consumer to call once rejecting incoming call completes, * provides {@code true} result if operation completes successfully * or {@code false} if the operation timed out/failed. */ public void startNormalRoutingEmergencyCall(@NonNull Phone phone, @NonNull android.telecom.Connection c, @NonNull Consumer<Boolean> completeConsumer) { Rlog.i(TAG, "startNormalRoutingEmergencyCall: phoneId=" + phone.getPhoneId() + ", callId=" + c.getTelecomCallId()); mNormalRoutingEmergencyConnection = c; maybeRejectIncomingCall(completeConsumer); } /** * Indicates the termination of a normal routing emergency call. * * @param c the normal routing emergency call disconnected. */ public void endNormalRoutingEmergencyCall(@NonNull android.telecom.Connection c) { if (c != mNormalRoutingEmergencyConnection) return; Rlog.i(TAG, "endNormalRoutingEmergencyCall: callId=" + c.getTelecomCallId()); mNormalRoutingEmergencyConnection = null; } /** * Handles the normal routing emergency call state change. * * @param c the call whose state has changed * @param state the new call state */ public void onNormalRoutingEmergencyCallStateChanged(android.telecom.Connection c, @android.telecom.Connection.ConnectionState int state) { if (c != mNormalRoutingEmergencyConnection) return; // If the call is connected, we don't need to monitor incoming call any more. if (state == android.telecom.Connection.STATE_ACTIVE || state == android.telecom.Connection.STATE_DISCONNECTED) { endNormalRoutingEmergencyCall(c); } } }
tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java +115 −1 Original line number Diff line number Diff line Loading @@ -2687,7 +2687,7 @@ public class EmergencyStateTrackerTest extends TelephonyTest { Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(c).when(call).getLatestConnection(); doReturn(true).when(call).isRinging(); doReturn(Call.State.INCOMING).when(call).getState(); doReturn(call).when(phone).getRingingCall(); setEcmSupportedConfig(phone, true); Loading Loading @@ -2789,6 +2789,120 @@ public class EmergencyStateTrackerTest extends TelephonyTest { } } /** * Test that the EmergencyStateTracker rejects incoming call when starting * a normal routing emergency call. */ @Test @SmallTest public void testNormalRoutingEmergencyCallRejectRingingCall() { Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */, false /* isRadioOn */); when(phone.getSubId()).thenReturn(1); Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(c).when(call).getLatestConnection(); doReturn(Call.State.DISCONNECTING).when(call).getState(); doReturn(call).when(phone).getRingingCall(); setEcmSupportedConfig(phone, true); EmergencyStateTracker testEst = setupEmergencyStateTracker( false /* isSuplDdsSwitchRequiredForEmergencyCall */); // There is an ongoing normal routing emergency call. testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {}); // Verify rejecting ringing call. try { verify(call).hangup(); } catch (CallStateException e) { } } /** * Test that the EmergencyStateTracker rejects incoming call if there is * a normal routing emergency call in dialing state. */ @Test @SmallTest public void testNormalRoutingRejectNewIncomingCall() { Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */, false /* isRadioOn */); when(phone.getSubId()).thenReturn(1); setEcmSupportedConfig(phone, true); EmergencyStateTracker testEst = setupEmergencyStateTracker( false /* isSuplDdsSwitchRequiredForEmergencyCall */); ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); verify(phone).registerForNewRingingConnection(handlerCaptor.capture(), intCaptor.capture(), any()); assertNotNull(handlerCaptor.getValue()); assertNotNull(intCaptor.getValue()); // There is an ongoing normal routing emergency call. testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {}); Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(call).when(c).getCall(); Message msg = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue()); AsyncResult.forMessage(msg, c, null); msg.sendToTarget(); processAllMessages(); // Verify rejecting incoming call. try { verify(call).hangup(); } catch (CallStateException e) { } } @Test @SmallTest public void testNormalRoutingDiscardedNotRejectNewIncomingCall() { Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */, false /* isRadioOn */); when(phone.getSubId()).thenReturn(1); setEcmSupportedConfig(phone, true); EmergencyStateTracker testEst = setupEmergencyStateTracker( false /* isSuplDdsSwitchRequiredForEmergencyCall */); ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class); verify(phone).registerForNewRingingConnection(handlerCaptor.capture(), intCaptor.capture(), any()); assertNotNull(handlerCaptor.getValue()); assertNotNull(intCaptor.getValue()); // Start normal routing emergency call. testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {}); // Discard normal routing emergency call. testEst.endNormalRoutingEmergencyCall(mTestConnection1); Connection c = mock(Connection.class); Call call = mock(Call.class); doReturn(call).when(c).getCall(); Message msg = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue()); AsyncResult.forMessage(msg, c, null); msg.sendToTarget(); processAllMessages(); // Verify not rejecting incoming call. try { verify(call, never()).hangup(); } catch (CallStateException e) { } } /** * Test that emergency call state changes are sent. */ Loading