Provide ability to place normal routed e-call on in-service sim first.
Handle the case where an emergency number has normal routing, and a
carrier wants to use the in-service SIM to place a normal routed
emergency call. Ensures we'll prefer the in-service SIM in this case over
trying to use a limited service one.
Add '100' as a normal routed emergency number in India.
Test: testCreateOutgoingConnectionForNormalRoutedEmergencyCall - this
verifies the end-to-end case this bug was raised for and ensures that we
will prefer the in-service sim over the limited service sim.
Test: testIsNormalRoutedEmergencyCallForCarrier - this verifies the
new method isNormalRoutedEmergencyCallForCarrier to ensure it can properly
detect a normal routed emergency call for carriers that prefer to use an
in service sim.
Test: testIsAvailableForEmergencyCallsNotForCrossSim - regression tests
the method which was refactored and covers the new cases.
Test: testIsAvailableForEmergencyCallsForEmergencyRoutingInEmergencyOnly -
verifies that we can recognize whether a phone is available when the
phone is limited service.
Test: testIsAvailableForEmergencyCallsForEmergencyRoutingInService -
verifies that we can recognize a phone is available when the phone is in
service.
Test: testGetPhoneForNormalRoutedEmergencyCallWhenCarrierDoesntSupport -
verifies that if the carrier doesn't support using normal routing on the
in service phone we won't return anything.
Test: testGetPhoneForNormalRoutedEmergencyCallWhenCarrierDoesSupport -
verifies that if the carries does support using normal routing on the
in service phone we'll find it.
Test: testGetPhoneForNormalRoutedEmergencyCallWhenCarrierDoesSupportMultiSim
Similar to the latter but with multisim specifically.
Test: com.android.phone.ecc.EccDataTest#testEccDataContent
Bug: 281934614
Flag: This is a cherry-pick from udc-qpr-dev, so no flag is needed.
Change-Id: I308285585a78052a4f95ffc7f3f503629b11dda9
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index 7252480..83b5c39 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -1372,6 +1372,13 @@
types: AMBULANCE
types: FIRE
}
+ eccs {
+ phone_number: "100"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ routing: NORMAL
+ }
ecc_fallback: "112"
}
countries {
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index fc86961..3f28a3a 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a381d7c..651bbd1 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -116,6 +116,7 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import javax.annotation.Nullable;
@@ -2579,15 +2580,32 @@
private boolean isNormalRouting(Phone phone, String number) {
if (phone.getEmergencyNumberTracker() != null) {
- EmergencyNumber num = phone.getEmergencyNumberTracker().getEmergencyNumber(number);
- if (num != null) {
- return num.getEmergencyCallRouting()
- == EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL;
- }
+ // Note: There can potentially be multiple instances of EmergencyNumber found; if any of
+ // them have normal routing, then use normal routing.
+ List<EmergencyNumber> nums = phone.getEmergencyNumberTracker().getEmergencyNumbers(
+ number);
+ return nums.stream().anyMatch(n ->
+ n.getEmergencyCallRouting() == EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
}
return false;
}
+ /**
+ * Determines the phone to use for a normal routed emergency call.
+ * @param number The emergency number.
+ * @return The {@link Phone} to place the normal routed emergency call on, or {@code null} if
+ * none was found.
+ */
+ @VisibleForTesting
+ public Phone getPhoneForNormalRoutedEmergencyCall(String number) {
+ return Stream.of(mPhoneFactoryProxy.getPhones())
+ .filter(p -> p.shouldPreferInServiceSimForNormalRoutedEmergencyCall()
+ && isNormalRouting(p, number)
+ && isAvailableForEmergencyCalls(p,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL))
+ .findFirst().orElse(null);
+ }
+
private boolean isVoiceInService(Phone phone, boolean imsVoiceCapable) {
// Dialing normal call is available.
if (phone.isWifiCallingEnabled()) {
@@ -3035,15 +3053,34 @@
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
+ Log.i(this, "getPhoneForAccount: handle=%s, subId=%s", accountHandle,
+ (chosenPhone == null ? "null" : chosenPhone.getSubId()));
}
- // If this is an emergency call and the phone we originally planned to make this call
+
+ // If this isn't an emergency call, just use the chosen phone (or null if none was found).
+ if (!isEmergency) {
+ return chosenPhone;
+ }
+
+ // Check if this call should be treated as a normal routed emergency call; we'll return null
+ // if this is not a normal routed emergency call.
+ Phone normalRoutingPhone = getPhoneForNormalRoutedEmergencyCall(emergencyNumberAddress);
+ if (normalRoutingPhone != null) {
+ Log.i(this, "getPhoneForAccount: normal routed emergency number,"
+ + "using phoneId=%d/subId=%d", normalRoutingPhone.getPhoneId(),
+ normalRoutingPhone.getSubId());
+ return normalRoutingPhone;
+ }
+
+ // Default emergency call phone selection logic:
+ // This is an emergency call and the phone we originally planned to make this call
// with is not in service or was invalid, try to find one that is in service, using the
// default as a last chance backup.
- if (isEmergency && (chosenPhone == null || !isAvailableForEmergencyCalls(chosenPhone))) {
+ if (chosenPhone == null || !isAvailableForEmergencyCalls(chosenPhone)) {
Log.d(this, "getPhoneForAccount: phone for phone acct handle %s is out of service "
+ "or invalid for emergency call.", accountHandle);
chosenPhone = getPhoneForEmergencyCall(emergencyNumberAddress);
- Log.d(this, "getPhoneForAccount: using subId: " +
+ Log.i(this, "getPhoneForAccount: emergency call - using subId: %s",
(chosenPhone == null ? "null" : chosenPhone.getSubId()));
}
return chosenPhone;
@@ -3463,10 +3500,20 @@
}
}
- /**
- * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only.
- */
private boolean isAvailableForEmergencyCalls(Phone phone) {
+ return isAvailableForEmergencyCalls(phone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+ }
+
+ /**
+ * Determines if the phone is available for an emergency call given the specified routing.
+ *
+ * @param phone the phone to check the service availability for
+ * @param routing the emergency call routing for this call
+ */
+ @VisibleForTesting
+ public boolean isAvailableForEmergencyCalls(Phone phone,
+ @EmergencyNumber.EmergencyCallRouting int routing) {
if (phone.getImsRegistrationTech() == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
// When a Phone is registered to Cross-SIM calling, there must always be a Phone on the
// other sub which is registered to cellular, so that must be selected.
@@ -3474,8 +3521,17 @@
+ phone + " as it is registered to CROSS_SIM");
return false;
}
- return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() ||
- phone.getServiceState().isEmergencyOnly();
+
+ // In service phones are always appropriate for emergency calls.
+ if (ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState()) {
+ return true;
+ }
+
+ // If the call routing is unknown or is using emergency routing, an emergency only attach is
+ // sufficient for placing the emergency call. Normal routed emergency calls cannot be
+ // placed on an emergency-only phone.
+ return (routing != EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL
+ && phone.getServiceState().isEmergencyOnly());
}
/**
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 34cbfdd..36513cf 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -32,6 +32,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -63,6 +64,7 @@
import android.telecom.Conferenceable;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
@@ -76,7 +78,9 @@
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
@@ -120,8 +124,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Unit tests for TelephonyConnectionService.
@@ -130,6 +136,33 @@
@RunWith(AndroidJUnit4.class)
public class TelephonyConnectionServiceTest extends TelephonyTestBase {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private static final String NORMAL_ROUTED_EMERGENCY_NUMBER = "110";
+ private static final String EMERGENCY_ROUTED_EMERGENCY_NUMBER = "911";
+ private static final EmergencyNumber MOCK_NORMAL_NUMBER = new EmergencyNumber(
+ NORMAL_ROUTED_EMERGENCY_NUMBER,
+ "US" /* country */,
+ null /* mcc */,
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList() /* categories */,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ private static final EmergencyNumber MOCK_NORMAL_NUMBER_WITH_UNKNOWN_ROUTING =
+ new EmergencyNumber(
+ NORMAL_ROUTED_EMERGENCY_NUMBER,
+ "US" /* country */,
+ "455" /* mcc */,
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList() /* categories */,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ private static final EmergencyNumber MOCK_EMERGENCY_NUMBER = new EmergencyNumber(
+ EMERGENCY_ROUTED_EMERGENCY_NUMBER,
+ "US" /* country */,
+ null /* mcc */,
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList() /* categories */,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
/**
* Unlike {@link TestTelephonyConnection}, a bare minimal {@link TelephonyConnection} impl
@@ -180,8 +213,8 @@
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
"com.android.phone.tests", TelephonyConnectionServiceTest.class.getName());
- private static final String TEST_ACCOUNT_ID1 = "id1";
- private static final String TEST_ACCOUNT_ID2 = "id2";
+ private static final String TEST_ACCOUNT_ID1 = "0"; // subid 0
+ private static final String TEST_ACCOUNT_ID2 = "1"; // subid 1
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_1 = new PhoneAccountHandle(
TEST_COMPONENT_NAME, TEST_ACCOUNT_ID1);
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
@@ -1305,6 +1338,82 @@
}
/**
+ * Test that the TelephonyConnectionService successfully dials an outgoing normal routed
+ * emergency call on an in-service sim.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingConnectionForNormalRoutedEmergencyCall()
+ throws CallStateException {
+ // A whole load of annoying mocks to set up to test this scenario.
+ // We'll purposely try to start the call on the limited service phone.
+ ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+ .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, NORMAL_ROUTED_EMERGENCY_NUMBER,
+ null))
+ .build();
+
+ // First phone is in limited service.
+ Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_EMERGENCY_ONLY,
+ true /*isEmergencyOnly*/);
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(testPhone0)
+ .getImsRegistrationTech();
+ doReturn(0).when(testPhone0).getSubId();
+ setupMockEmergencyNumbers(testPhone0, List.of(MOCK_NORMAL_NUMBER,
+ MOCK_NORMAL_NUMBER_WITH_UNKNOWN_ROUTING, MOCK_EMERGENCY_NUMBER));
+
+ // Second phone is in full service; this is ultimately the one we want to pick.
+ Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
+ false /*isEmergencyOnly*/);
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(testPhone1)
+ .getImsRegistrationTech();
+ doReturn(1).when(testPhone1).getSubId();
+ setupMockEmergencyNumbers(testPhone1, List.of(MOCK_NORMAL_NUMBER,
+ MOCK_NORMAL_NUMBER_WITH_UNKNOWN_ROUTING, MOCK_EMERGENCY_NUMBER));
+
+ // Make sure both phones are going to prefer in service for normal routed ecalls.
+ doReturn(true).when(testPhone0).shouldPreferInServiceSimForNormalRoutedEmergencyCall();
+ doReturn(true).when(testPhone1).shouldPreferInServiceSimForNormalRoutedEmergencyCall();
+
+ // A whole load of other stuff that needs to be setup for this to work.
+ doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
+ doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
+ List<Phone> phones = new ArrayList<>(2);
+ doReturn(true).when(testPhone0).isRadioOn();
+ doReturn(true).when(testPhone1).isRadioOn();
+ phones.add(testPhone0);
+ phones.add(testPhone1);
+ setPhones(phones);
+ doReturn(0).when(mPhoneUtilsProxy).getSubIdForPhoneAccountHandle(
+ eq(PHONE_ACCOUNT_HANDLE_1));
+ doReturn(1).when(mPhoneUtilsProxy).getSubIdForPhoneAccountHandle(
+ eq(PHONE_ACCOUNT_HANDLE_2));
+ setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+ setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_2, testPhone1);
+ setupDeviceConfig(testPhone0, testPhone1, 0);
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
+ eq(NORMAL_ROUTED_EMERGENCY_NUMBER));
+ HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
+ List<EmergencyNumber> numbers = new ArrayList<>();
+ numbers.add(MOCK_EMERGENCY_NUMBER);
+ numbers.add(MOCK_NORMAL_NUMBER);
+ numbers.add(MOCK_NORMAL_NUMBER_WITH_UNKNOWN_ROUTING);
+ emergencyNumbers.put(0 /*subId*/, numbers);
+ doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
+ doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+ // All of that for... this.
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(
+ PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+ assertNotNull("test connection was not set up correctly.", mConnection);
+
+ // Lets make sure we DID try to place the call on phone 1, which is the in service phone.
+ verify(testPhone1).dial(anyString(), any(DialArgs.class), any(Consumer.class));
+ // And make sure we DID NOT try to place the call on phone 0, which is in limited service.
+ verify(testPhone0, never()).dial(anyString(), any(DialArgs.class), any(Consumer.class));
+ }
+
+ /**
* Test that the TelephonyConnectionService successfully turns satellite off before placing the
* emergency call.
*/
@@ -2110,6 +2219,8 @@
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,
@@ -2152,6 +2263,8 @@
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+ doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+ anyString());
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
@@ -2199,6 +2312,8 @@
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+ doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+ anyString());
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
@@ -2240,6 +2355,8 @@
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+ doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+ anyString());
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
@@ -2293,6 +2410,8 @@
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+ doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+ anyString());
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
@@ -2347,6 +2466,8 @@
doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+ doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+ anyString());
when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
@@ -3025,6 +3146,153 @@
disconnectCause.getTelephonyDisconnectCause());
}
+ @Test
+ public void testIsAvailableForEmergencyCallsNotForCrossSim() {
+ Phone mockPhone = Mockito.mock(Phone.class);
+ when(mockPhone.getImsRegistrationTech()).thenReturn(
+ ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
+ assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+ assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+ assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+ }
+
+ @Test
+ public void testIsAvailableForEmergencyCallsForEmergencyRoutingInEmergencyOnly() {
+ ServiceState mockService = Mockito.mock(ServiceState.class);
+ when(mockService.isEmergencyOnly()).thenReturn(true);
+ when(mockService.getState()).thenReturn(ServiceState.STATE_EMERGENCY_ONLY);
+
+ Phone mockPhone = Mockito.mock(Phone.class);
+ when(mockPhone.getImsRegistrationTech()).thenReturn(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ when(mockPhone.getServiceState()).thenReturn(mockService);
+
+ assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+ assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+ assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+ }
+
+ @Test
+ public void testIsAvailableForEmergencyCallsForEmergencyRoutingInService() {
+ ServiceState mockService = Mockito.mock(ServiceState.class);
+ when(mockService.isEmergencyOnly()).thenReturn(false);
+ when(mockService.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+ Phone mockPhone = Mockito.mock(Phone.class);
+ when(mockPhone.getImsRegistrationTech()).thenReturn(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ when(mockPhone.getServiceState()).thenReturn(mockService);
+
+ assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+ assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+ assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+ }
+
+ /**
+ * Verify that is the carrier config indicates that the carrier does not prefer to use an in
+ * service sim for a normal routed emergency call that we'll get no result.
+ */
+ @Test
+ public void testGetPhoneForNormalRoutedEmergencyCallWhenCarrierDoesntSupport() {
+ ServiceState mockService = Mockito.mock(ServiceState.class);
+ when(mockService.isEmergencyOnly()).thenReturn(false);
+ when(mockService.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+ Phone mockPhone = Mockito.mock(Phone.class);
+ when(mockPhone.shouldPreferInServiceSimForNormalRoutedEmergencyCall()).thenReturn(
+ false);
+ setupMockEmergencyNumbers(mockPhone, List.of(MOCK_NORMAL_NUMBER));
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(new Phone[] {mockPhone});
+
+ assertNull(mTestConnectionService.getPhoneForNormalRoutedEmergencyCall(
+ NORMAL_ROUTED_EMERGENCY_NUMBER));
+ }
+
+ /**
+ * Verify that is the carrier config indicates that the carrier prefers to use an in service sim
+ * for a normal routed emergency call that we'll get the in service sim that supports it.
+ */
+ @Test
+ public void testGetPhoneForNormalRoutedEmergencyCallWhenCarrierDoesSupport() {
+ ServiceState mockService = Mockito.mock(ServiceState.class);
+ when(mockService.isEmergencyOnly()).thenReturn(false);
+ when(mockService.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+ Phone mockPhone = Mockito.mock(Phone.class);
+ when(mockPhone.shouldPreferInServiceSimForNormalRoutedEmergencyCall()).thenReturn(
+ true);
+ when(mockPhone.getServiceState()).thenReturn(mockService);
+ setupMockEmergencyNumbers(mockPhone, List.of(MOCK_NORMAL_NUMBER));
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(new Phone[] {mockPhone});
+
+ assertEquals(mockPhone,
+ mTestConnectionService.getPhoneForNormalRoutedEmergencyCall(
+ NORMAL_ROUTED_EMERGENCY_NUMBER));
+ }
+
+ /**
+ * Verify where there are two sims, one in limited service, and another in full service, if the
+ * carrier prefers to use an in-service sim, we choose the in-service sim.
+ */
+ @Test
+ public void testGetPhoneForNormalRoutedEmergencyCallWhenCarrierDoesSupportMultiSim() {
+ ServiceState mockInService = Mockito.mock(ServiceState.class);
+ when(mockInService.isEmergencyOnly()).thenReturn(false);
+ when(mockInService.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ ServiceState mockLimitedService = Mockito.mock(ServiceState.class);
+ when(mockLimitedService.isEmergencyOnly()).thenReturn(true);
+ when(mockLimitedService.getState()).thenReturn(ServiceState.STATE_EMERGENCY_ONLY);
+
+ Phone mockInservicePhone = Mockito.mock(Phone.class);
+ when(mockInservicePhone.shouldPreferInServiceSimForNormalRoutedEmergencyCall()).thenReturn(
+ true);
+ when(mockInservicePhone.getServiceState()).thenReturn(mockInService);
+ setupMockEmergencyNumbers(mockInservicePhone, List.of(MOCK_NORMAL_NUMBER));
+
+ Phone mockLimitedServicePhone = Mockito.mock(Phone.class);
+ when(mockLimitedServicePhone.shouldPreferInServiceSimForNormalRoutedEmergencyCall())
+ .thenReturn(true);
+ when(mockLimitedServicePhone.getServiceState()).thenReturn(mockLimitedService);
+ setupMockEmergencyNumbers(mockLimitedServicePhone, List.of(MOCK_NORMAL_NUMBER));
+
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(new Phone[] {mockLimitedServicePhone,
+ mockInservicePhone});
+
+ assertEquals(mockInservicePhone,
+ mTestConnectionService.getPhoneForNormalRoutedEmergencyCall(
+ NORMAL_ROUTED_EMERGENCY_NUMBER));
+ }
+
+ private void setupMockEmergencyNumbers(Phone mockPhone, List<EmergencyNumber> numbers) {
+ EmergencyNumberTracker emergencyNumberTracker = Mockito.mock(EmergencyNumberTracker.class);
+ // Yuck. There should really be a fake emergency number class which makes it easy to inject
+ // the numbers for testing.
+ ArrayMap<String, List<EmergencyNumber>> numbersMap = new ArrayMap<>();
+ for (EmergencyNumber number : numbers) {
+ when(emergencyNumberTracker.getEmergencyNumber(eq(number.getNumber())))
+ .thenReturn(number);
+ if (!numbersMap.containsKey(number.getNumber())) {
+ numbersMap.put(number.getNumber(), new ArrayList<>());
+ }
+ numbersMap.get(number.getNumber()).add(number);
+ }
+ // Double yuck.
+ for (Map.Entry<String, List<EmergencyNumber>> entry : numbersMap.entrySet()) {
+ when(emergencyNumberTracker.getEmergencyNumbers(eq(entry.getKey()))).thenReturn(
+ entry.getValue());
+ }
+ when(mockPhone.getEmergencyNumberTracker()).thenReturn(emergencyNumberTracker);
+ }
+
private void setupForDialForDomainSelection(Phone mockPhone, int domain, boolean isEmergency) {
if (isEmergency) {
doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
@@ -3233,10 +3501,14 @@
}
private void setupHandleToPhoneMap(PhoneAccountHandle handle, Phone phone) {
- // use subId 0
- when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(handle))).thenReturn(0);
- when(mSubscriptionManagerProxy.getPhoneId(eq(0))).thenReturn(0);
- when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(phone);
+ // The specified handle has an id which is subID
+ doReturn(Integer.parseInt(handle.getId())).when(mPhoneUtilsProxy)
+ .getSubIdForPhoneAccountHandle(eq(handle));
+ // The specified sub id in the passed handle will correspond to the phone's phone id.
+ doReturn(phone.getPhoneId()).when(mSubscriptionManagerProxy)
+ .getPhoneId(eq(Integer.parseInt(handle.getId())));
+ int phoneId = phone.getPhoneId();
+ doReturn(phone).when(mPhoneFactoryProxy).getPhone(eq(phoneId));
}
private AsyncResult getSuppServiceNotification(int notificationType, int code) {