Merge "Delay dialing normal routing emergency number in airplane mode" into udc-dev
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 91d345f..18b95e1 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -43,8 +43,10 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.SelectionAttributes;
 import android.telephony.EmergencyRegResult;
@@ -123,6 +125,10 @@
     // from the modem.
     private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
 
+    // Timeout to start dynamic routing of normal routing emergency numbers.
+    @VisibleForTesting
+    public static final int TIMEOUT_TO_DYNAMIC_ROUTING_MS = 10000;
+
     // Timeout before we terminate the outgoing DSDA call if HOLD did not complete in time on the
     // existing call.
     private static final int DEFAULT_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS = 2000;
@@ -1083,6 +1089,8 @@
             if (isEmergencyNumber) {
                 mIsEmergencyCallPending = true;
             }
+            int timeoutToOnTimeoutCallback = mDomainSelectionResolver.isDomainSelectionSupported()
+                    ? TIMEOUT_TO_DYNAMIC_ROUTING_MS : 0;
             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
@@ -1091,13 +1099,33 @@
                 }
 
                 @Override
-                public boolean isOkToCall(Phone phone, int serviceState) {
+                public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) {
+                    if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+                        return isEmergencyNumber;
+                    }
+                    return false;
+                }
+
+                @Override
+                public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) {
                     // HAL 1.4 introduced a new variant of dial for emergency calls, which includes
                     // an isTesting parameter. For HAL 1.4+, do not wait for IN_SERVICE, this will
                     // be handled at the RIL/vendor level by emergencyDial(...).
                     boolean waitForInServiceToDialEmergency = isTestEmergencyNumber
                             && phone.getHalVersion(HAL_SERVICE_VOICE)
                             .less(RIL.RADIO_HAL_VERSION_1_4);
+                    if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+                        if (isEmergencyNumber) {
+                            // Since the domain selection service is enabled,
+                            // dilaing normal routing emergency number only reaches here.
+                            if (!isVoiceInService(phone, imsVoiceCapable)) {
+                                // Wait for voice in service.
+                                // That is, wait for IMS registration on PS only network.
+                                serviceState = ServiceState.STATE_OUT_OF_SERVICE;
+                                waitForInServiceToDialEmergency = true;
+                            }
+                        }
+                    }
                     if (isEmergencyNumber && !waitForInServiceToDialEmergency) {
                         // We currently only look to make sure that the radio is on before dialing.
                         // We should be able to make emergency calls at any time after the radio has
@@ -1119,7 +1147,8 @@
                                 || serviceState == ServiceState.STATE_IN_SERVICE;
                     }
                 }
-            }, isEmergencyNumber && !isTestEmergencyNumber, phone, isTestEmergencyNumber);
+            }, isEmergencyNumber && !isTestEmergencyNumber, phone, isTestEmergencyNumber,
+                    timeoutToOnTimeoutCallback);
             // Return the still unconnected GsmConnection and wait for the Radios to boot before
             // connecting it to the underlying Phone.
             return resultConnection;
@@ -2527,6 +2556,44 @@
         return false;
     }
 
+    private boolean isVoiceInService(Phone phone, boolean imsVoiceCapable) {
+        // Dialing normal call is available.
+        if (phone.isWifiCallingEnabled()) {
+            Log.i(this, "isVoiceInService VoWi-Fi available");
+            return true;
+        }
+
+        ServiceState ss = phone.getServiceStateTracker().getServiceState();
+        if (ss.getState() != ServiceState.STATE_IN_SERVICE) return false;
+
+        NetworkRegistrationInfo regState = ss.getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regState != null) {
+            int registrationState = regState.getRegistrationState();
+            if (registrationState != NetworkRegistrationInfo.REGISTRATION_STATE_HOME
+                    && registrationState != NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING) {
+                return true;
+            }
+
+            int networkType = regState.getAccessNetworkTechnology();
+            if (networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+                DataSpecificRegistrationInfo regInfo = regState.getDataSpecificInfo();
+                if (regInfo.getLteAttachResultType()
+                        == DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED) {
+                    Log.i(this, "isVoiceInService combined attach");
+                    return true;
+                }
+            }
+
+            if (networkType == TelephonyManager.NETWORK_TYPE_NR
+                    || networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+                Log.i(this, "isVoiceInService PS only network, IMS available " + imsVoiceCapable);
+                return imsVoiceCapable;
+            }
+        }
+        return true;
+    }
+
     private boolean maybeReselectDomainForNormalCall(
             final TelephonyConnection c, int callFailCause, ImsReasonInfo reasonInfo) {
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index d144d9d..fadfaaf 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -27,6 +27,7 @@
 import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
 
 import static com.android.internal.telephony.RILConstants.GSM_PHONE;
+import static com.android.services.telephony.TelephonyConnectionService.TIMEOUT_TO_DYNAMIC_ROUTING_MS;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -62,8 +63,11 @@
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.DomainSelectionService;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -1240,11 +1244,13 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false));
+                eq(testPhone), eq(false), eq(0));
 
-        assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        assertFalse(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
         when(mSST.isRadioOn()).thenReturn(true);
-        assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        assertTrue(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
 
         mConnection.setDisconnected(null);
         callback.getValue().onComplete(null, true);
@@ -1266,11 +1272,13 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false));
+                eq(testPhone), eq(false), eq(0));
 
-        assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        assertFalse(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
         when(mSST.isRadioOn()).thenReturn(true);
-        assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        assertTrue(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
 
         callback.getValue().onComplete(null, true);
 
@@ -2010,6 +2018,255 @@
     }
 
     @Test
+    public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_InService()
+            throws Exception {
+        setupForCallTest();
+
+        doReturn(false).when(mPhone0).isRadioOn();
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_POWER_OFF);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+
+        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                Collections.emptyList(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+        assertFalse(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+        when(mSST.isRadioOn()).thenReturn(true);
+
+        assertFalse(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+        ss.setState(ServiceState.STATE_IN_SERVICE);
+
+        assertTrue(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+    }
+
+    @Test
+    public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_Timeout()
+            throws Exception {
+        setupForCallTest();
+
+        doReturn(false).when(mPhone0).isRadioOn();
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_POWER_OFF);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+
+        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                Collections.emptyList(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+        when(mSST.isRadioOn()).thenReturn(true);
+
+        assertFalse(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+        assertTrue(callback.getValue()
+                .onTimeout(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+    }
+
+    @Test
+    public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_CombinedAttach()
+            throws Exception {
+        setupForCallTest();
+
+        doReturn(false).when(mPhone0).isRadioOn();
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_POWER_OFF);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+
+        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                Collections.emptyList(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+        when(mSST.isRadioOn()).thenReturn(true);
+        ss.setState(ServiceState.STATE_IN_SERVICE);
+
+        DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
+                .setLteAttachResultType(DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED)
+                .build();
+
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .setDataSpecificInfo(dsri)
+                .build();
+        ss.addNetworkRegistrationInfo(nri);
+
+        assertTrue(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+    }
+
+    @Test
+    public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_PsOnly()
+            throws Exception {
+        setupForCallTest();
+
+        doReturn(false).when(mPhone0).isRadioOn();
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_POWER_OFF);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+
+        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                Collections.emptyList(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+        when(mSST.isRadioOn()).thenReturn(true);
+        ss.setState(ServiceState.STATE_IN_SERVICE);
+
+        DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
+                .build();
+
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .setDataSpecificInfo(dsri)
+                .build();
+        ss.addNetworkRegistrationInfo(nri);
+
+        assertFalse(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+        assertTrue(callback.getValue()
+                .onTimeout(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+    }
+
+    @Test
+    public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_PsOnly_ImsRegistered()
+            throws Exception {
+        setupForCallTest();
+
+        doReturn(false).when(mPhone0).isRadioOn();
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_POWER_OFF);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+
+        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
+
+        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                Collections.emptyList(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+        when(mSST.isRadioOn()).thenReturn(true);
+        ss.setState(ServiceState.STATE_IN_SERVICE);
+
+        DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
+                .build();
+
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .setDataSpecificInfo(dsri)
+                .build();
+        ss.addNetworkRegistrationInfo(nri);
+
+        assertFalse(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+        assertTrue(callback.getValue()
+                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, true));
+    }
+
+    @Test
     public void testDomainSelectionNormalToEmergencyCs() throws Exception {
         setupForCallTest();