Support dialing e-call based on dialing sim's emergency number list.

Support a carrier config for dialing an emergency call based on the
dialing sim's emergency number list.

When this config is enabled, we will only consider the dialed SIM's
emergency number list when placing an emergency call instead of the current
platform behavior of using the platform's combined list of known emergency
numbers across all SIMs.

Test: atest com.android.internal.telephony.GsmCdmaPhoneTest#testEmergencyCallAnySim
Test: atest com.android.internal.telephony.GsmCdmaPhoneTest#testNotEmergencyNumberOnDialedSim1
Test: atest com.android.internal.telephony.GsmCdmaPhoneTest#testNotEmergencyNumberOnDialedSim2
Bug: 203947159
Change-Id: I292d7f27b253f71066194e3f60bd7114e579c87c
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index e359b2a..aebd916 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1340,8 +1340,35 @@
                     + possibleEmergencyNumber);
             dialString = possibleEmergencyNumber;
         }
+
+        CarrierConfigManager configManager =
+                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle carrierConfig = configManager.getConfigForSubId(getSubId());
+        boolean allowWpsOverIms = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL);
+        boolean useOnlyDialedSimEccList = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL);
+
+
         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-        boolean isEmergency = tm.isEmergencyNumber(dialString);
+        boolean isEmergency;
+        // Check if the carrier wants to treat a call as an emergency call based on its own list of
+        // known emergency numbers.
+        // useOnlyDialedSimEccList is false for the vast majority of carriers.  There are, however,
+        // some carriers which do not want to handle dial requests for numbers which are in the
+        // emergency number list on another SIM, but is not on theirs.  In this case we will use the
+        // emergency number list for this carrier's SIM only.
+        if (useOnlyDialedSimEccList) {
+            isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString,
+                    true /* exactMatch */);
+            logi("dial; isEmergency=" + isEmergency
+                    + " (based on this phone only); globalIsEmergency="
+                    + tm.isEmergencyNumber(dialString));
+        } else {
+            isEmergency = tm.isEmergencyNumber(dialString);
+            logi("dial; isEmergency=" + isEmergency + " (based on all phones)");
+        }
+
         /** Check if the call is Wireless Priority Service call */
         boolean isWpsCall = dialString != null ? (dialString.startsWith(PREFIX_WPS)
                 || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
@@ -1355,12 +1382,6 @@
 
         Phone imsPhone = mImsPhone;
 
-        CarrierConfigManager configManager =
-                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-
-        boolean allowWpsOverIms = configManager.getConfigForSubId(getSubId())
-                .getBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL);
-
         boolean useImsForEmergency = isEmergency && useImsForEmergency();
 
         String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
@@ -1374,7 +1395,8 @@
                 && (isWpsCall ? allowWpsOverIms : true);
 
         if (DBG) {
-            logd("useImsForCall=" + useImsForCall
+            logi("useImsForCall=" + useImsForCall
+                    + ", useOnlyDialedSimEccList=" + useOnlyDialedSimEccList
                     + ", isEmergency=" + isEmergency
                     + ", useImsForEmergency=" + useImsForEmergency
                     + ", useImsForUt=" + useImsForUt
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index c3e96f6..e720e71 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -81,6 +81,7 @@
 import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
 import com.android.internal.telephony.dataconnection.TransportManager;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.metrics.SmsStats;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
@@ -3952,6 +3953,11 @@
         return mImsPhone;
     }
 
+    @VisibleForTesting
+    public void setImsPhone(ImsPhone imsPhone) {
+        mImsPhone = imsPhone;
+    }
+
     /**
      * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
      * @param keyType whether the key is being used for WLAN or ePDG.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 2f98164..4efa3f3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
@@ -52,6 +53,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.WorkSource;
 import android.preference.PreferenceManager;
@@ -73,6 +75,7 @@
 
 import androidx.test.filters.FlakyTest;
 
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.test.SimulatedCommandsVerifier;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
@@ -99,6 +102,7 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class GsmCdmaPhoneTest extends TelephonyTest {
+    private static final String TEST_EMERGENCY_NUMBER = "555";
     @Mock
     private Handler mTestHandler;
     @Mock
@@ -1651,4 +1655,89 @@
 
         assertEquals(false, mPhoneUT.isAllowedNetworkTypesLoadedFromDb());
     }
+
+    /**
+     * Verifies that an emergency call placed on a SIM which does NOT explicitly define a number as
+     * an emergency call will still be placed as an emergency call.
+     * @throws CallStateException
+     */
+    @Test
+    public void testEmergencyCallAnySim() throws CallStateException {
+        setupEmergencyCallScenario(false /* USE_ONLY_DIALED_SIM_ECC_LIST */,
+                false /* isEmergencyOnDialedSim */);
+
+        ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsArgumentCaptor =
+                ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
+        mPhoneUT.dial(TEST_EMERGENCY_NUMBER, new ImsPhone.ImsDialArgs.Builder().build());
+
+        // Should have dialed out over IMS and should have specified that it is an emergency call
+        verify(mImsPhone).dial(anyString(), dialArgsArgumentCaptor.capture());
+        PhoneInternalInterface.DialArgs args = dialArgsArgumentCaptor.getValue();
+        assertTrue(args.isEmergency);
+    }
+
+    /**
+     * Tests the scenario where a number is dialed on a sim where it is NOT an emergency number,
+     * but it IS an emergency number based on {@link TelephonyManager#isEmergencyNumber(String)},
+     * and the carrier wants to ONLY use the dialed SIM's ECC list.
+     * @throws CallStateException
+     */
+    @Test
+    public void testNotEmergencyNumberOnDialedSim1() throws CallStateException {
+        setupEmergencyCallScenario(true /* USE_ONLY_DIALED_SIM_ECC_LIST */,
+                false /* isEmergencyOnDialedSim */);
+
+        ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsArgumentCaptor =
+                ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
+        mPhoneUT.dial(TEST_EMERGENCY_NUMBER, new ImsPhone.ImsDialArgs.Builder().build());
+
+        // Should have dialed out over IMS and should have specified that it is NOT an emergency
+        // call
+        verify(mImsPhone).dial(anyString(), dialArgsArgumentCaptor.capture());
+        PhoneInternalInterface.DialArgs args = dialArgsArgumentCaptor.getValue();
+        assertFalse(args.isEmergency);
+    }
+
+    /**
+     * Tests the scenario where a number is dialed on a sim where it is NOT an emergency number,
+     * but it IS an emergency number based on {@link TelephonyManager#isEmergencyNumber(String)},
+     * and the carrier wants to use the global ECC list.
+     * @throws CallStateException
+     */
+    @Test
+    public void testNotEmergencyNumberOnDialedSim2() throws CallStateException {
+        setupEmergencyCallScenario(false /* USE_ONLY_DIALED_SIM_ECC_LIST */,
+                false /* isEmergencyOnDialedSim */);
+
+        ArgumentCaptor<PhoneInternalInterface.DialArgs> dialArgsArgumentCaptor =
+                ArgumentCaptor.forClass(PhoneInternalInterface.DialArgs.class);
+        mPhoneUT.dial(TEST_EMERGENCY_NUMBER, new ImsPhone.ImsDialArgs.Builder().build());
+
+        // Should have dialed out over IMS and should have specified that it is an emergency call
+        verify(mImsPhone).dial(anyString(), dialArgsArgumentCaptor.capture());
+        PhoneInternalInterface.DialArgs args = dialArgsArgumentCaptor.getValue();
+        assertTrue(args.isEmergency);
+    }
+
+    private void setupEmergencyCallScenario(boolean isUsingOnlyDialedSim,
+            boolean isEmergencyPerDialedSim) {
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL,
+                isUsingOnlyDialedSim);
+        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+        doReturn(true).when(mImsPhone).isImsAvailable();
+        doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
+        doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
+        doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled();
+        doReturn(true).when(mImsPhone).isVoiceOverCellularImsEnabled();
+        ServiceState ss = mock(ServiceState.class);
+        doReturn(ServiceState.STATE_IN_SERVICE).when(ss).getState();
+        doReturn(ss).when(mImsPhone).getServiceState();
+
+        doReturn(true).when(mTelephonyManager).isEmergencyNumber(anyString());
+        doReturn(isEmergencyPerDialedSim).when(mEmergencyNumberTracker).isEmergencyNumber(
+                anyString(), anyBoolean());
+
+        mPhoneUT.setImsPhone(mImsPhone);
+    }
 }