Merge "Added getMaxCharPerTextMessage() API."
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 09b9b6d..8253f71 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -595,6 +595,30 @@
public void onSelectionTerminated(@DisconnectCauses int cause) {
if (mEmergencyCallDomainSelectionConnection != null) {
Log.i(this, "onSelectionTerminated cause=" + cause);
+
+ // Cross stack redial
+ if (cause == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
+ || cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE) {
+ if (mEmergencyConnection != null) {
+ final boolean isPermanentFailure =
+ cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+ Log.i(this, "onSelectionTerminated trigger cross stack redial"
+ + " permanent=" + isPermanentFailure);
+ mDomainSelectionMainExecutor.execute(() -> {
+ Log.i(this, "onSelectionTerminated execute cross stack redial"
+ + " permanent=" + isPermanentFailure);
+ TelephonyConnection c = mEmergencyConnection;
+ Phone phone = mEmergencyCallDomainSelectionConnection.getPhone();
+ mEmergencyConnection.removeTelephonyConnectionListener(
+ mEmergencyConnectionListener);
+ mEmergencyStateTracker.endCall(
+ mEmergencyConnection.getTelecomCallId());
+ releaseEmergencyCallDomainSelection(true);
+ retryOutgoingOriginalConnection(c, phone, isPermanentFailure);
+ });
+ return;
+ }
+ }
mEmergencyCallDomainSelectionConnection = null;
if (mEmergencyConnection != null) {
mEmergencyConnection.hangup(android.telephony.DisconnectCause.OUT_OF_NETWORK);
@@ -658,7 +682,7 @@
@Override
public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
- retryOutgoingOriginalConnection(c, isPermanentFailure);
+ retryOutgoingOriginalConnection(c, c.getPhone(), isPermanentFailure);
}
};
@@ -1809,7 +1833,7 @@
// Update the mEmergencyRetryCache by removing the Phone used to call the last failed emergency
// number and then moving it to the back of the queue if it is not a permanent failure cause
// from the modem.
- private void updateCachedConnectionPhonePair(TelephonyConnection c,
+ private void updateCachedConnectionPhonePair(TelephonyConnection c, Phone phone,
boolean isPermanentFailure) {
// No cache exists, create a new one.
if (mEmergencyRetryCache == null) {
@@ -1824,7 +1848,7 @@
Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
// Need to refer default phone considering ImsPhone because
// cachedPhones is a list that contains default phones.
- Phone phoneUsed = c.getPhone().getDefaultPhone();
+ Phone phoneUsed = phone.getDefaultPhone();
if (phoneUsed == null) {
return;
}
@@ -1853,9 +1877,10 @@
* This will continue until there are no more slots to dial on.
*/
@VisibleForTesting
- public void retryOutgoingOriginalConnection(TelephonyConnection c, boolean isPermanentFailure) {
- int phoneId = (c.getPhone() == null) ? -1 : c.getPhone().getPhoneId();
- updateCachedConnectionPhonePair(c, isPermanentFailure);
+ public void retryOutgoingOriginalConnection(TelephonyConnection c,
+ Phone phone, boolean isPermanentFailure) {
+ int phoneId = (phone == null) ? -1 : phone.getPhoneId();
+ updateCachedConnectionPhonePair(c, phone, isPermanentFailure);
// Pull next phone to use from the cache or null if it is empty
Phone newPhoneToUse = (mEmergencyRetryCache.second != null)
? mEmergencyRetryCache.second.peek() : null;
@@ -2330,10 +2355,12 @@
CompletableFuture<Integer> future =
mEmergencyCallDomainSelectionConnection.reselectDomain(attr);
+ // TeleponyConnection will clear original connection. Keep the reference to Phone.
+ final Phone phone = c.getPhone().getDefaultPhone();
if (future != null) {
future.thenAcceptAsync((result) -> {
Log.d(this, "reselectDomain-complete");
- onEmergencyRedialOnDomain(c, c.getPhone().getDefaultPhone(), result);
+ onEmergencyRedialOnDomain(c, phone, result);
}, mDomainSelectionMainExecutor);
return true;
}
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index fa11a5d..d1eda18 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -68,6 +68,7 @@
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.BarringInfo;
import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
import android.telephony.DomainSelectionService;
import android.telephony.DomainSelectionService.SelectionAttributes;
import android.telephony.EmergencyRegResult;
@@ -106,6 +107,18 @@
private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
+ private static final ArrayList<String> sAllowOnlyWithSimReady = new ArrayList<>();
+
+ static {
+ // b/177967010, JP
+ sAllowOnlyWithSimReady.add("jp"); // Japan
+ // b/198393826, DE
+ sAllowOnlyWithSimReady.add("de"); // Germany
+ // b/230443699, IN and SG
+ sAllowOnlyWithSimReady.add("in"); // India
+ sAllowOnlyWithSimReady.add("sg"); // Singapore
+ }
+
/**
* Network callback used to determine whether Wi-Fi is connected or not.
*/
@@ -167,6 +180,7 @@
private boolean mRequiresVoLteEnabled;
private boolean mLtePreferredAfterNrFailure;
private boolean mTryCsWhenPsFails;
+ private int mModemCount;
/** Indicates whether this instance is deactivated. */
private boolean mDestroyed = false;
@@ -228,6 +242,12 @@
private void handleScanResult(EmergencyRegResult result) {
logi("handleScanResult result=" + result);
+ // Detected the country and found that emergency calls are not allowed with this slot.
+ if (!allowEmergencyCalls(result)) {
+ terminateSelectionPermanentlyForSlot();
+ return;
+ }
+
if (result.getAccessNetwork() == UNKNOWN) {
if ((mPreferredNetworkScanType == SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE)
|| (mScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
@@ -247,7 +267,7 @@
removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
onWwanNetworkTypeSelected(result.getAccessNetwork());
- mIsScanRequested = false;
+ mCancelSignal = null;
}
@Override
@@ -318,11 +338,14 @@
mTransportSelectorCallback = cb;
mSelectionAttributes = attr;
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ mModemCount = tm.getActiveModemCount();
+
sendEmptyMessage(MSG_START_DOMAIN_SELECTION);
}
private void startDomainSelection() {
- logi("startDomainSelection");
+ logi("startDomainSelection modemCount=" + mModemCount);
updateCarrierConfiguration();
mDomainSelectionRequested = true;
if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
@@ -458,6 +481,12 @@
return;
}
+ if (!allowEmergencyCalls(mSelectionAttributes.getEmergencyRegResult())) {
+ // Detected the country and found that emergency calls are not allowed with this slot.
+ terminateSelectionPermanentlyForSlot();
+ return;
+ }
+
if (isWifiPreferred()) {
onWlanSelected();
return;
@@ -793,7 +822,7 @@
List<Integer> ratList = getImsNetworkTypeConfiguration();
if (ratList.contains(accessNetwork)) {
if (mIsEmergencyBarred) {
- logi("sgetSelectablePsNetworkType barred");
+ logi("getSelectablePsNetworkType barred");
return UNKNOWN;
}
if (accessNetwork == NGRAN) {
@@ -1100,6 +1129,36 @@
}
}
+ private boolean allowEmergencyCalls(EmergencyRegResult regResult) {
+ if (mModemCount < 2) return true;
+ if (regResult == null) {
+ loge("allowEmergencyCalls null regResult");
+ return true;
+ }
+
+ String iso = regResult.getIso();
+ if (sAllowOnlyWithSimReady.contains(iso)) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ int simState = tm.getSimState(getSlotId());
+ if (simState != TelephonyManager.SIM_STATE_READY) {
+ logi("allowEmergencyCalls not ready, simState=" + simState + ", iso=" + iso);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void terminateSelectionPermanentlyForSlot() {
+ logi("terminateSelectionPermanentlyForSlot");
+ mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.EMERGENCY_PERM_FAILURE);
+
+ if (mIsScanRequested && mCancelSignal != null) {
+ mCancelSignal.cancel();
+ mCancelSignal = null;
+ }
+ }
+
private static String arrayToString(int[] intArray, IntFunction<String> func) {
int length = intArray.length;
StringBuilder sb = new StringBuilder("{");
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 3d6a0aa..ba064e5 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,6 +16,8 @@
package com.android.services.telephony;
+import static android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
import static android.telephony.DisconnectCause.NOT_DISCONNECTED;
import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
@@ -55,6 +57,7 @@
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.DomainSelectionService;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -76,6 +79,7 @@
import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.domainselection.EmergencyCallDomainSelectionConnection;
import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
@@ -864,7 +868,8 @@
setPhones(phones);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// We never need to be notified in telecom that the PhoneAccount has changed, because it
// was redialed on the same slot
@@ -895,7 +900,8 @@
setPhones(phones);
c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// We never need to be notified in telecom that the PhoneAccount has changed, because it
// was never redialed
@@ -936,7 +942,8 @@
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// The cache should still contain all of the Phones, since it was a temporary failure.
assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
@@ -977,7 +984,8 @@
doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
slot1Phone);
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// The cache should only contain the slot1Phone.
assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
@@ -1019,7 +1027,8 @@
slot1Phone);
// First Temporary failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot1Phone);
// The cache should still contain all of the Phones, since it was a temporary failure.
@@ -1027,7 +1036,8 @@
// Make sure slot 1 is next in the queue.
assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// Second Temporary failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), false /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot0Phone);
// The cache should still contain all of the Phones, since it was a temporary failure.
@@ -1074,7 +1084,8 @@
slot1Phone);
// First Permanent failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// Set the Phone to the new phone that was just used to dial.
c.setMockPhone(slot1Phone);
// The cache should only contain one phone
@@ -1082,7 +1093,8 @@
// Make sure slot 1 is next in the queue.
assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
// Second Permanent failure
- mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ mTestConnectionService.retryOutgoingOriginalConnection(c,
+ c.getPhone(), true /*isPermanentFailure*/);
// The cache should be empty
assertEquals(true, mTestConnectionService.mEmergencyRetryCache.second.isEmpty());
@@ -1631,6 +1643,98 @@
}
@Test
+ public void testOnSelectionTerminatedPerm() throws Exception {
+ setupForCallTest();
+
+ doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+ doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+ any(), callbackCaptor.capture());
+
+ DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+ callbackCaptor.getValue();
+
+ assertNotNull(callback);
+
+ EmergencyCallDomainSelectionConnection ecdsc =
+ Mockito.mock(EmergencyCallDomainSelectionConnection.class);
+ doReturn(ecdsc).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+
+ callback.onSelectionTerminated(EMERGENCY_PERM_FAILURE);
+
+ ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionService.SelectionAttributes.class);
+
+ verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
+
+ DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
+
+ assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+ }
+
+ @Test
+ public void testOnSelectionTerminatedTemp() throws Exception {
+ setupForCallTest();
+
+ doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+ doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+ mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+ createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+ TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+ ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+ verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+ any(), callbackCaptor.capture());
+
+ DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+ callbackCaptor.getValue();
+
+ assertNotNull(callback);
+
+ EmergencyCallDomainSelectionConnection ecdsc =
+ Mockito.mock(EmergencyCallDomainSelectionConnection.class);
+ doReturn(ecdsc).when(mDomainSelectionResolver)
+ .getDomainSelectionConnection(any(), anyInt(), eq(true));
+
+ callback.onSelectionTerminated(EMERGENCY_TEMP_FAILURE);
+
+ ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
+ ArgumentCaptor.forClass(
+ DomainSelectionService.SelectionAttributes.class);
+
+ verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
+
+ DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
+
+ assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+ }
+
+ @Test
public void testDomainSelectionWithMmiCode() {
//UT domain selection should not be handled by new domain selector.
doNothing().when(mContext).startActivity(any());
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index c50c6b5..27b3f0a 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -80,6 +80,7 @@
import android.telephony.BarringInfo;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentityLte;
+import android.telephony.DisconnectCause;
import android.telephony.DomainSelectionService;
import android.telephony.DomainSelectionService.SelectionAttributes;
import android.telephony.EmergencyRegResult;
@@ -1106,6 +1107,27 @@
verify(mTransportSelectorCallback, times(1)).onWlanSelected();
}
+ @Test
+ public void testDualSimInvalidSubscription() throws Exception {
+ createSelector(SLOT_0_SUB_ID);
+ unsolBarringInfoChanged(false);
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+ doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+ .when(mTelephonyManager).getSimState(anyInt());
+
+ EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+ 0, false, false, 0, 0, "", "", "jp");
+ SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+ mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+ processAllMessages();
+
+ bindImsServiceUnregistered();
+ processAllMessages();
+
+ verify(mTransportSelectorCallback, times(1))
+ .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+ }
+
private void createSelector(int subId) throws Exception {
mDomainSelector = new EmergencyCallDomainSelector(
mContext, SLOT_0, subId, mHandlerThread.getLooper(),
@@ -1174,9 +1196,19 @@
@NetworkRegistrationInfo.Domain int domain,
boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
@NonNull String mcc, @NonNull String mnc) {
+ return getEmergencyRegResult(accessNetwork, regState, domain, isVopsSupported,
+ isEmcBearerSupported, emc, emf, mcc, mnc, "");
+ }
+
+ private static EmergencyRegResult getEmergencyRegResult(
+ @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
+ @NetworkRegistrationInfo.RegistrationState int regState,
+ @NetworkRegistrationInfo.Domain int domain,
+ boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
+ @NonNull String mcc, @NonNull String mnc, @NonNull String iso) {
return new EmergencyRegResult(accessNetwork, regState,
domain, isVopsSupported, isEmcBearerSupported,
- emc, emf, mcc, mnc, "");
+ emc, emf, mcc, mnc, iso);
}
private static PersistableBundle getDefaultPersistableBundle() {