diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-20 21:56:49 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-20 21:56:49 +0000 |
commit | 2bfb9f00140730b96c4c543b6b2e722601900158 (patch) | |
tree | 1d49760d5dedb85780da1c21ccb474f8520b6b6e | |
parent | 3956de147c434663bfd9154a6065487a07ec6e81 (diff) | |
parent | 66840a93b6b6e2e2d849b94daeab8d03d8f8acfd (diff) | |
download | Telephony-android14-qpr1-s2-release.tar.gz |
Merge cherrypicks of ['googleplex-android-review.googlesource.com/24627634'] into udc-qpr1-release.android-14.0.0_r27android-14.0.0_r26android-14.0.0_r25android-14.0.0_r24android-14.0.0_r23android-14.0.0_r22android-14.0.0_r21android-14.0.0_r20android-14.0.0_r19android-14.0.0_r18android-14.0.0_r17android-14.0.0_r16android14-qpr1-s2-releaseandroid14-qpr1-release
Change-Id: I3c60e75a550f86c8dbb3c506ccf020462dbc2872
-rw-r--r-- | ecc/input/eccdata.txt | 7 | ||||
-rw-r--r-- | ecc/output/eccdata | bin | 2136 -> 2142 bytes | |||
-rw-r--r-- | src/com/android/services/telephony/TelephonyConnectionService.java | 80 | ||||
-rw-r--r-- | tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java | 285 |
4 files changed, 354 insertions, 18 deletions
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt index 7252480e3..83b5c39a1 100644 --- a/ecc/input/eccdata.txt +++ b/ecc/input/eccdata.txt @@ -1372,6 +1372,13 @@ countries { 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 Binary files differindex fc8696124..3f28a3a88 100644 --- a/ecc/output/eccdata +++ b/ecc/output/eccdata diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java index 93a38ddd9..fcf9531a5 100644 --- a/src/com/android/services/telephony/TelephonyConnectionService.java +++ b/src/com/android/services/telephony/TelephonyConnectionService.java @@ -114,6 +114,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.regex.Pattern; +import java.util.stream.Stream; import javax.annotation.Nullable; @@ -2576,15 +2577,32 @@ public class TelephonyConnectionService extends ConnectionService { 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()) { @@ -3032,15 +3050,34 @@ public class TelephonyConnectionService extends ConnectionService { 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 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; } - // If this is an emergency call and the phone we originally planned to make this call + + // 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; @@ -3460,10 +3497,20 @@ public class TelephonyConnectionService extends ConnectionService { } } + private boolean isAvailableForEmergencyCalls(Phone phone) { + return isAvailableForEmergencyCalls(phone, + EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY); + } + /** - * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only. + * 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 */ - private boolean isAvailableForEmergencyCalls(Phone phone) { + @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. @@ -3471,8 +3518,17 @@ public class TelephonyConnectionService extends ConnectionService { + 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 9e1ea24d1..090fc6e05 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 com.android.services.telephony.TelephonyConnectionService.TIMEOUT_ 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; @@ -62,6 +63,7 @@ import android.telecom.Conference; 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; @@ -75,7 +77,9 @@ import android.telephony.SubscriptionManager; 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; @@ -117,8 +121,10 @@ import java.util.Collection; 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. @@ -126,6 +132,34 @@ import java.util.concurrent.Executor; @RunWith(AndroidJUnit4.class) public class TelephonyConnectionServiceTest extends TelephonyTestBase { + 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 * that does not try to configure anything. @@ -175,8 +209,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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( @@ -1298,6 +1332,82 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { } /** + * 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. */ @@ -2037,6 +2147,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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, @@ -2079,6 +2191,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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); @@ -2126,6 +2240,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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); @@ -2167,6 +2283,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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); @@ -2220,6 +2338,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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); @@ -2274,6 +2394,8 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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); @@ -2930,6 +3052,153 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { 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) @@ -3138,10 +3407,14 @@ public class TelephonyConnectionServiceTest extends TelephonyTestBase { } 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) { |