Merge changes from topics "msim_tests_fixes", "redial_emer_phone_account"
* changes:
Adds tests for MSIM redial and fixes bug
Emergency call redial implementation
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 26690b9..a420100 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -128,7 +128,6 @@
case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
case android.telephony.DisconnectCause.DATA_DISABLED:
case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
- case android.telephony.DisconnectCause.DIALED_ON_WRONG_SLOT:
case android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING:
case android.telephony.DisconnectCause.IMEI_NOT_ACCEPTED:
case android.telephony.DisconnectCause.WIFI_LOST:
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 064d1f1..6a14e88 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -94,6 +94,7 @@
private static final int MSG_ON_HOLD_TONE = 14;
private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
+ private static final int MSG_HANGUP = 17;
private final Handler mHandler = new Handler() {
@Override
@@ -240,6 +241,10 @@
Log.d(this, "MSG_CDMA_VOICE_PRIVACY_OFF received");
setCdmaVoicePrivacy(false);
break;
+ case MSG_HANGUP:
+ int cause = (int) msg.obj;
+ hangup(cause);
+ break;
}
}
};
@@ -258,7 +263,7 @@
*/
public abstract static class TelephonyConnectionListener {
public void onOriginalConnectionConfigured(TelephonyConnection c) {}
- public void onOriginalConnectionRetry(TelephonyConnection c) {}
+ public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
}
private final PostDialListener mPostDialListener = new PostDialListener() {
@@ -544,7 +549,7 @@
@Override
public void onDisconnect() {
Log.v(this, "onDisconnect");
- hangup(android.telephony.DisconnectCause.LOCAL);
+ mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
}
/**
@@ -579,7 +584,7 @@
@Override
public void onAbort() {
Log.v(this, "onAbort");
- hangup(android.telephony.DisconnectCause.LOCAL);
+ mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
}
@Override
@@ -608,7 +613,8 @@
public void onReject() {
Log.v(this, "onReject");
if (isValidRingingCall()) {
- hangup(android.telephony.DisconnectCause.INCOMING_REJECTED);
+ mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
+ .sendToTarget();
}
super.onReject();
}
@@ -1093,8 +1099,8 @@
if (mOriginalConnection != null) {
try {
// Hanging up a ringing call requires that we invoke call.hangup() as opposed to
- // connection.hangup(). Without this change, the party originating the call will not
- // get sent to voicemail if the user opts to reject the call.
+ // connection.hangup(). Without this change, the party originating the call
+ // will not get sent to voicemail if the user opts to reject the call.
if (isValidRingingCall()) {
Call call = getCall();
if (call != null) {
@@ -1103,10 +1109,10 @@
Log.w(this, "Attempting to hangup a connection without backing call.");
}
} else {
- // We still prefer to call connection.hangup() for non-ringing calls in order
- // to support hanging-up specific calls within a conference call. If we invoked
- // call.hangup() while in a conference, we would end up hanging up the entire
- // conference call instead of the specific connection.
+ // We still prefer to call connection.hangup() for non-ringing calls
+ // in order to support hanging-up specific calls within a conference call.
+ // If we invoked call.hangup() while in a conference, we would end up
+ // hanging up the entire conference call instead of the specific connection.
mOriginalConnection.hangup();
}
} catch (CallStateException e) {
@@ -1297,6 +1303,7 @@
} else {
newState = mOriginalConnection.getState();
}
+ int cause = mOriginalConnection.getDisconnectCause();
Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, this);
if (mConnectionState != newState) {
@@ -1323,13 +1330,18 @@
setRinging();
break;
case DISCONNECTED:
- // We can get into a situation where the radio wants us to redial the same
- // emergency call on the other available slot. This will not set the state to
- // disconnected and will instead tell the TelephonyConnectionService to create
- // a new originalConnection using the new Slot.
- if (mOriginalConnection.getDisconnectCause() ==
- DisconnectCause.DIALED_ON_WRONG_SLOT) {
- fireOnOriginalConnectionRetryDial();
+ if (shouldTreatAsEmergencyCall()
+ && (cause
+ == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
+ || cause
+ == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) {
+ // We can get into a situation where the radio wants us to redial the
+ // same emergency call on the other available slot. This will not set
+ // the state to disconnected and will instead tell the
+ // TelephonyConnectionService to
+ // create a new originalConnection using the new Slot.
+ fireOnOriginalConnectionRetryDial(cause
+ == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE);
} else {
setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
mOriginalConnection.getDisconnectCause(),
@@ -1728,9 +1740,9 @@
}
}
- private final void fireOnOriginalConnectionRetryDial() {
+ private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) {
for (TelephonyConnectionListener l : mTelephonyListeners) {
- l.onOriginalConnectionRetry(this);
+ l.onOriginalConnectionRetry(this, isPermanentFailure);
}
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 53bfd68..ded2468 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -61,7 +61,9 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Queue;
import java.util.regex.Pattern;
/**
@@ -126,7 +128,8 @@
// call at one time. We also only access this cache from a TelephonyConnection that wishes to
// redial, so we use a WeakReference that will become stale once the TelephonyConnection is
// destroyed.
- private Pair<WeakReference<TelephonyConnection>, List<Phone>> mEmergencyRetryCache;
+ @VisibleForTesting
+ public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
/**
* Keeps track of the status of a SIM slot.
@@ -238,8 +241,8 @@
}
@Override
- public void onOriginalConnectionRetry(TelephonyConnection c) {
- retryOutgoingOriginalConnection(c);
+ public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
+ retryOutgoingOriginalConnection(c, isPermanentFailure);
}
};
@@ -887,54 +890,69 @@
return result;
}
- private Pair<WeakReference<TelephonyConnection>, List<Phone>> makeCachedConnectionPhonePair(
+ private Pair<WeakReference<TelephonyConnection>, Queue<Phone>> makeCachedConnectionPhonePair(
TelephonyConnection c) {
- List<Phone> phones = new ArrayList<>(Arrays.asList(mPhoneFactoryProxy.getPhones()));
+ Queue<Phone> phones = new LinkedList<>(Arrays.asList(mPhoneFactoryProxy.getPhones()));
return new Pair<>(new WeakReference<>(c), phones);
}
- // Check the mEmergencyRetryCache to see if it contains the TelephonyConnection. If it doesn't,
- // then it is stale. Create a new one!
- private void updateCachedConnectionPhonePair(TelephonyConnection c) {
+ // 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,
+ boolean isPermanentFailure) {
+ // No cache exists, create a new one.
if (mEmergencyRetryCache == null) {
Log.i(this, "updateCachedConnectionPhonePair, cache is null. Generating new cache");
mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
- } else {
- // Check to see if old cache is stale. If it is, replace it
- WeakReference<TelephonyConnection> cachedConnection = mEmergencyRetryCache.first;
- if (cachedConnection.get() != c) {
- Log.i(this, "updateCachedConnectionPhonePair, cache is stale. Regenerating.");
- mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
- }
+ // Cache is stale, create a new one with the new TelephonyConnection.
+ } else if (mEmergencyRetryCache.first.get() != c) {
+ Log.i(this, "updateCachedConnectionPhonePair, cache is stale. Regenerating.");
+ mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
+ }
+
+ Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
+ Phone phoneUsed = c.getPhone();
+ if (phoneUsed == null) {
+ return;
+ }
+ // Remove phone used from the list, but for temporary fail cause, it will be added
+ // back to list further in this method. However in case of permanent failure, the
+ // phone shouldn't be reused, hence it will not be added back again.
+ cachedPhones.remove(phoneUsed);
+ Log.i(this, "updateCachedConnectionPhonePair, isPermanentFailure:" + isPermanentFailure);
+ if (!isPermanentFailure) {
+ // In case of temporary failure, add the phone back, this will result adding it
+ // to tail of list mEmergencyRetryCache.second, giving other phone more
+ // priority and that is what we want.
+ cachedPhones.offer(phoneUsed);
}
}
/**
- * Returns the first Phone that has not been used yet to place the call. Any Phones that have
- * been used to place a call will have already been removed from mEmergencyRetryCache.second.
- * The phone that it excluded will be removed from mEmergencyRetryCache.second in this method.
- * @param phoneToExclude The Phone object that will be removed from our cache of available
- * phones.
- * @return the first Phone that is available to be used to retry the call.
+ * Updates a cache containing all of the slots that are available for redial at any point.
+ *
+ * - If a Connection returns with the disconnect cause EMERGENCY_TEMP_FAILURE, keep that phone
+ * in the cache, but move it to the lowest priority in the list. Then, place the emergency call
+ * on the next phone in the list.
+ * - If a Connection returns with the disconnect cause EMERGENCY_PERM_FAILURE, remove that phone
+ * from the cache and pull another phone from the cache to place the emergency call.
+ *
+ * This will continue until there are no more slots to dial on.
*/
- private Phone getPhoneForRedial(Phone phoneToExclude) {
- List<Phone> cachedPhones = mEmergencyRetryCache.second;
- if (cachedPhones.contains(phoneToExclude)) {
- Log.i(this, "getPhoneForRedial, removing Phone[" + phoneToExclude.getPhoneId() +
- "] from the available Phone cache.");
- cachedPhones.remove(phoneToExclude);
- }
- return cachedPhones.isEmpty() ? null : cachedPhones.get(0);
- }
-
- private void retryOutgoingOriginalConnection(TelephonyConnection c) {
- updateCachedConnectionPhonePair(c);
- Phone newPhoneToUse = getPhoneForRedial(c.getPhone());
+ @VisibleForTesting
+ public void retryOutgoingOriginalConnection(TelephonyConnection c, boolean isPermanentFailure) {
+ int phoneId = (c.getPhone() == null) ? -1 : c.getPhone().getPhoneId();
+ updateCachedConnectionPhonePair(c, isPermanentFailure);
+ // Pull next phone to use from the cache or null if it is empty
+ Phone newPhoneToUse = (mEmergencyRetryCache.second != null)
+ ? mEmergencyRetryCache.second.peek() : null;
if (newPhoneToUse != null) {
int videoState = c.getVideoState();
Bundle connExtras = c.getExtras();
Log.i(this, "retryOutgoingOriginalConnection, redialing on Phone Id: " + newPhoneToUse);
c.clearOriginalConnection();
+ if (phoneId != newPhoneToUse.getPhoneId()) updatePhoneAccount(c, newPhoneToUse);
placeOutgoingConnection(c, newPhoneToUse, videoState, connExtras);
} else {
// We have run out of Phones to use. Disconnect the call and destroy the connection.
@@ -945,6 +963,15 @@
}
}
+ private void updatePhoneAccount(TelephonyConnection connection, Phone phone) {
+ PhoneAccountHandle pHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
+ // For ECall handling on MSIM, until the request reaches here (i.e PhoneApp), we don't know
+ // on which phone account ECall can be placed. After deciding, we should notify Telecom of
+ // the change so that the proper PhoneAccount can be displayed.
+ Log.i(this, "updatePhoneAccount setPhoneAccountHandle, account = " + pHandle);
+ connection.notifyPhoneAccountChanged(pHandle);
+ }
+
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, ConnectionRequest request) {
placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index 044f26b..a61ffe9 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -39,6 +39,14 @@
// Set up the looper if it does not exist on the test thread.
if (Looper.myLooper() == null) {
Looper.prepare();
+ // Wait until the looper is not null anymore
+ for(int i = 0; i < 5; i++) {
+ if (Looper.myLooper() != null) {
+ break;
+ }
+ Looper.prepare();
+ Thread.sleep(100);
+ }
}
}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
index 3d88af7..229bdee 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -34,7 +34,6 @@
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
/**
@@ -48,8 +47,8 @@
private TelecomAccountRegistry mTelecomAccountRegistry;
- private MockTelephonyConnection mMockTelephonyConnectionA;
- private MockTelephonyConnection mMockTelephonyConnectionB;
+ private TestTelephonyConnection mTestTelephonyConnectionA;
+ private TestTelephonyConnection mTestTelephonyConnectionB;
private ImsConferenceController mControllerTest;
@@ -60,8 +59,8 @@
Looper.prepare();
}
mTelecomAccountRegistry = TelecomAccountRegistry.getInstance(null);
- mMockTelephonyConnectionA = new MockTelephonyConnection();
- mMockTelephonyConnectionB = new MockTelephonyConnection();
+ mTestTelephonyConnectionA = new TestTelephonyConnection();
+ mTestTelephonyConnectionB = new TestTelephonyConnection();
mControllerTest = new ImsConferenceController(mTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy);
@@ -80,25 +79,25 @@
@SmallTest
public void testConferenceable() {
- mControllerTest.add(mMockTelephonyConnectionB);
- mControllerTest.add(mMockTelephonyConnectionA);
+ mControllerTest.add(mTestTelephonyConnectionB);
+ mControllerTest.add(mTestTelephonyConnectionA);
- mMockTelephonyConnectionA.setActive();
- mMockTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionA.setActive();
+ mTestTelephonyConnectionB.setOnHold();
- assertTrue(mMockTelephonyConnectionA.getConferenceables()
- .contains(mMockTelephonyConnectionB));
- assertTrue(mMockTelephonyConnectionB.getConferenceables()
- .contains(mMockTelephonyConnectionA));
+ assertTrue(mTestTelephonyConnectionA.getConferenceables()
+ .contains(mTestTelephonyConnectionB));
+ assertTrue(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
// verify addConference method is never called
verify(mMockTelephonyConnectionServiceProxy, never())
.addConference(any(ImsConference.class));
// call A removed
- mControllerTest.remove(mMockTelephonyConnectionA);
- assertFalse(mMockTelephonyConnectionB.getConferenceables()
- .contains(mMockTelephonyConnectionA));
+ mControllerTest.remove(mTestTelephonyConnectionA);
+ assertFalse(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
}
/**
@@ -114,18 +113,18 @@
@SmallTest
public void testMergeMultiPartyCalls() {
- when(mMockTelephonyConnectionA.mMockRadioConnection.getPhoneType())
+ when(mTestTelephonyConnectionA.mMockRadioConnection.getPhoneType())
.thenReturn(PhoneConstants.PHONE_TYPE_IMS);
- when(mMockTelephonyConnectionB.mMockRadioConnection.getPhoneType())
+ when(mTestTelephonyConnectionB.mMockRadioConnection.getPhoneType())
.thenReturn(PhoneConstants.PHONE_TYPE_IMS);
- when(mMockTelephonyConnectionA.mMockRadioConnection.isMultiparty()).thenReturn(true);
- when(mMockTelephonyConnectionB.mMockRadioConnection.isMultiparty()).thenReturn(true);
+ when(mTestTelephonyConnectionA.mMockRadioConnection.isMultiparty()).thenReturn(true);
+ when(mTestTelephonyConnectionB.mMockRadioConnection.isMultiparty()).thenReturn(true);
- mControllerTest.add(mMockTelephonyConnectionB);
- mControllerTest.add(mMockTelephonyConnectionA);
+ mControllerTest.add(mTestTelephonyConnectionB);
+ mControllerTest.add(mTestTelephonyConnectionA);
- mMockTelephonyConnectionA.setActive();
- mMockTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionA.setActive();
+ mTestTelephonyConnectionB.setOnHold();
verify(mMockTelephonyConnectionServiceProxy, times(2))
.addConference(any(ImsConference.class));
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index 739359a..275bcc6 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -25,7 +25,6 @@
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.ArgumentCaptor;
@@ -53,8 +52,8 @@
@Mock
private Conference.Listener mMockListener;
- private MockTelephonyConnection mMockTelephonyConnectionA;
- private MockTelephonyConnection mMockTelephonyConnectionB;
+ private TestTelephonyConnection mTestTelephonyConnectionA;
+ private TestTelephonyConnection mTestTelephonyConnectionB;
private TelephonyConferenceController mControllerTest;
@@ -64,8 +63,8 @@
if (Looper.myLooper() == null) {
Looper.prepare();
}
- mMockTelephonyConnectionA = new MockTelephonyConnection();
- mMockTelephonyConnectionB = new MockTelephonyConnection();
+ mTestTelephonyConnectionA = new TestTelephonyConnection();
+ mTestTelephonyConnectionB = new TestTelephonyConnection();
mControllerTest = new TelephonyConferenceController(mMockTelephonyConnectionServiceProxy);
}
@@ -84,33 +83,33 @@
@SmallTest
public void testConferenceable() {
- when(mMockTelephonyConnectionA.mMockRadioConnection.getCall()
+ when(mTestTelephonyConnectionA.mMockRadioConnection.getCall()
.isMultiparty()).thenReturn(false);
- when(mMockTelephonyConnectionB.mMockRadioConnection.getCall()
+ when(mTestTelephonyConnectionB.mMockRadioConnection.getCall()
.isMultiparty()).thenReturn(false);
// add telephony connection B
- mControllerTest.add(mMockTelephonyConnectionB);
+ mControllerTest.add(mTestTelephonyConnectionB);
// add telephony connection A
- mControllerTest.add(mMockTelephonyConnectionA);
+ mControllerTest.add(mTestTelephonyConnectionA);
- mMockTelephonyConnectionA.setActive();
- mMockTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionA.setActive();
+ mTestTelephonyConnectionB.setOnHold();
- assertTrue(mMockTelephonyConnectionA.getConferenceables()
- .contains(mMockTelephonyConnectionB));
- assertTrue(mMockTelephonyConnectionB.getConferenceables()
- .contains(mMockTelephonyConnectionA));
+ assertTrue(mTestTelephonyConnectionA.getConferenceables()
+ .contains(mTestTelephonyConnectionB));
+ assertTrue(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
// verify addConference method is never called
verify(mMockTelephonyConnectionServiceProxy, never())
.addConference(any(TelephonyConference.class));
// call A removed
- mControllerTest.remove(mMockTelephonyConnectionA);
- assertFalse(mMockTelephonyConnectionB.getConferenceables()
- .contains(mMockTelephonyConnectionA));
+ mControllerTest.remove(mTestTelephonyConnectionA);
+ assertFalse(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
}
/**
@@ -129,31 +128,31 @@
public void testMergeMultiPartyCalls() {
// set isMultiparty() true to create the same senario of merge behaviour
- when(mMockTelephonyConnectionA.mMockRadioConnection.getCall()
+ when(mTestTelephonyConnectionA.mMockRadioConnection.getCall()
.isMultiparty()).thenReturn(true);
- when(mMockTelephonyConnectionB.mMockRadioConnection.getCall()
+ when(mTestTelephonyConnectionB.mMockRadioConnection.getCall()
.isMultiparty()).thenReturn(true);
// Add connections into connection Service
Collection<Connection> allConnections = new ArrayList<Connection>();
- allConnections.add(mMockTelephonyConnectionA);
- allConnections.add(mMockTelephonyConnectionB);
+ allConnections.add(mTestTelephonyConnectionA);
+ allConnections.add(mTestTelephonyConnectionB);
when(mMockTelephonyConnectionServiceProxy.getAllConnections())
.thenReturn(allConnections);
// add telephony connection B
- mControllerTest.add(mMockTelephonyConnectionB);
+ mControllerTest.add(mTestTelephonyConnectionB);
// add telephony connection A
- mControllerTest.add(mMockTelephonyConnectionA);
+ mControllerTest.add(mTestTelephonyConnectionA);
- mMockTelephonyConnectionA.setActive();
- mMockTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionA.setActive();
+ mTestTelephonyConnectionB.setOnHold();
- assertTrue(mMockTelephonyConnectionA.getConferenceables()
- .contains(mMockTelephonyConnectionB));
- assertTrue(mMockTelephonyConnectionB.getConferenceables()
- .contains(mMockTelephonyConnectionA));
+ assertTrue(mTestTelephonyConnectionA.getConferenceables()
+ .contains(mTestTelephonyConnectionB));
+ assertTrue(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
// capture the argument in the addConference method, and verify it is called
ArgumentCaptor<TelephonyConference> argumentCaptor = ArgumentCaptor.
@@ -166,9 +165,9 @@
verify(mMockListener, never()).onDestroyed(any(Conference.class));
// call A removed
- mControllerTest.remove(mMockTelephonyConnectionA);
- assertFalse(mMockTelephonyConnectionB.getConferenceables()
- .contains(mMockTelephonyConnectionA));
+ mControllerTest.remove(mTestTelephonyConnectionA);
+ assertFalse(mTestTelephonyConnectionB.getConferenceables()
+ .contains(mTestTelephonyConnectionA));
//onDestroy should be called during the destroy
verify(mMockListener).onDestroyed(any(Conference.class));
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 45f74df..eb8c48a 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,13 +16,19 @@
package com.android.services.telephony;
+import android.net.Uri;
+import android.telecom.DisconnectCause;
+import android.telecom.TelecomManager;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
+import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.TelephonyTestBase;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import org.junit.After;
@@ -31,9 +37,20 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import java.util.ArrayList;
+import java.util.List;
+
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@@ -491,6 +508,250 @@
assertEquals(slot0Phone, resultPhone);
}
+ /**
+ * The modem has returned a temporary error when placing an emergency call on a phone with one
+ * SIM slot.
+ *
+ * Verify that dial is called on the same phone again when retryOutgoingOriginalConnection is
+ * called.
+ */
+ @Test
+ @FlakyTest
+ @SmallTest
+ public void testRetryOutgoingOriginalConnection_redialTempFailOneSlot() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ Phone slot0Phone = c.getPhone();
+ when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
+ List<Phone> phones = new ArrayList<>(1);
+ phones.add(slot0Phone);
+ setPhones(phones);
+ c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+
+ mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+
+ // We never need to be notified in telecom that the PhoneAccount has changed, because it
+ // was redialed on the same slot
+ assertEquals(0, c.getNotifyPhoneAccountChangedCount());
+ try {
+ verify(slot0Phone).dial(anyString(), any(), anyInt(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
+ /**
+ * The modem has returned a permanent failure when placing an emergency call on a phone with one
+ * SIM slot.
+ *
+ * Verify that the connection is set to disconnected with an error disconnect cause and dial is
+ * not called.
+ */
+ @Test
+ @FlakyTest
+ @SmallTest
+ public void testRetryOutgoingOriginalConnection_redialPermFailOneSlot() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ Phone slot0Phone = c.getPhone();
+ when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
+ List<Phone> phones = new ArrayList<>(1);
+ phones.add(slot0Phone);
+ setPhones(phones);
+ c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+
+ mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+
+ // We never need to be notified in telecom that the PhoneAccount has changed, because it
+ // was never redialed
+ assertEquals(0, c.getNotifyPhoneAccountChangedCount());
+ try {
+ verify(slot0Phone, never()).dial(anyString(), any(), anyInt(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ assertEquals(c.getState(), android.telecom.Connection.STATE_DISCONNECTED);
+ assertEquals(c.getDisconnectCause().getCode(), DisconnectCause.ERROR);
+ }
+
+ /**
+ * The modem has returned a temporary failure when placing an emergency call on a phone with two
+ * SIM slots.
+ *
+ * Verify that the emergency call is dialed on the other slot and telecom is notified of the new
+ * PhoneAccount.
+ */
+ @Test
+ @FlakyTest
+ @SmallTest
+ public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ Phone slot0Phone = c.getPhone();
+ when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
+ c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ List<Phone> phones = new ArrayList<>(2);
+ phones.add(slot0Phone);
+ phones.add(slot1Phone);
+ setPhones(phones);
+
+ mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+
+ // The cache should still contain all of the Phones, since it was a temporary failure.
+ assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
+ // We need to be notified in Telecom that the PhoneAccount has changed, because it was
+ // redialed on another slot
+ assertEquals(1, c.getNotifyPhoneAccountChangedCount());
+ try {
+ verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
+ /**
+ * The modem has returned a temporary failure when placing an emergency call on a phone with two
+ * SIM slots.
+ *
+ * Verify that the emergency call is dialed on the other slot and telecom is notified of the new
+ * PhoneAccount.
+ */
+ @Test
+ @FlakyTest
+ @SmallTest
+ public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ Phone slot0Phone = c.getPhone();
+ when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
+ c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ List<Phone> phones = new ArrayList<>(2);
+ phones.add(slot0Phone);
+ phones.add(slot1Phone);
+ setPhones(phones);
+
+ mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+
+ // The cache should only contain the slot1Phone.
+ assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
+ // We need to be notified in Telecom that the PhoneAccount has changed, because it was
+ // redialed on another slot
+ assertEquals(1, c.getNotifyPhoneAccountChangedCount());
+ try {
+ verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
+ /**
+ * The modem has returned a temporary failure twice while placing an emergency call on a phone
+ * with two SIM slots.
+ *
+ * Verify that the emergency call is dialed on slot 1 and then on slot 0 and telecom is
+ * notified of this twice.
+ */
+ @Test
+ @FlakyTest
+ @SmallTest
+ public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot_twoFailure() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ Phone slot0Phone = c.getPhone();
+ when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
+ c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ List<Phone> phones = new ArrayList<>(2);
+ phones.add(slot0Phone);
+ phones.add(slot1Phone);
+ setPhones(phones);
+
+ // First Temporary failure
+ mTestConnectionService.retryOutgoingOriginalConnection(c, 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.
+ assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
+ // Make sure slot 1 is next in the queue.
+ assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
+ // Second Temporary failure
+ mTestConnectionService.retryOutgoingOriginalConnection(c, 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.
+ assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
+ // Make sure slot 0 is next in the queue.
+ assertEquals(slot0Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
+
+ // We need to be notified in Telecom that the PhoneAccount has changed, because it was
+ // redialed on another slot
+ assertEquals(2, c.getNotifyPhoneAccountChangedCount());
+ try {
+ verify(slot0Phone).dial(anyString(), any(), anyInt(), any());
+ verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
+ /**
+ * The modem has returned a permanent failure twice while placing an emergency call on a phone
+ * with two SIM slots.
+ *
+ * Verify that the emergency call is dialed on slot 1 and then disconnected and telecom is
+ * notified of the change to slot 1.
+ */
+ @Test
+ @FlakyTest
+ @SmallTest
+ public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot_twoFailure() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ Phone slot0Phone = c.getPhone();
+ when(slot0Phone.getPhoneId()).thenReturn(SLOT_0_PHONE_ID);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
+ c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ List<Phone> phones = new ArrayList<>(2);
+ phones.add(slot0Phone);
+ phones.add(slot1Phone);
+ setPhones(phones);
+
+ // First Permanent failure
+ mTestConnectionService.retryOutgoingOriginalConnection(c, 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
+ assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
+ // Make sure slot 1 is next in the queue.
+ assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
+ // Second Permanent failure
+ mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+ // The cache should be empty
+ assertEquals(true, mTestConnectionService.mEmergencyRetryCache.second.isEmpty());
+
+ assertEquals(c.getState(), android.telecom.Connection.STATE_DISCONNECTED);
+ assertEquals(c.getDisconnectCause().getCode(), DisconnectCause.ERROR);
+ // We need to be notified in Telecom that the PhoneAccount has changed, because it was
+ // redialed on another slot
+ assertEquals(1, c.getNotifyPhoneAccountChangedCount());
+ try {
+ verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ verify(slot0Phone, never()).dial(anyString(), any(), anyInt(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
private Phone makeTestPhone(int phoneId, int serviceState, boolean isEmergencyOnly) {
Phone phone = mock(Phone.class);
ServiceState testServiceState = new ServiceState();
@@ -524,4 +785,17 @@
private void setDefaultPhone(Phone phone) {
when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phone);
}
+
+ private void setPhones(List<Phone> phones) {
+ when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
+ }
+
+ private void setPhonesDialConnection(Phone phone, Connection c) {
+ try {
+ when(phone.dial(anyString(), anyInt())).thenReturn(c);
+ } catch (CallStateException e) {
+ // this shouldn't happen
+ fail();
+ }
+ }
}
diff --git a/tests/src/com/android/services/telephony/MockTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
similarity index 73%
rename from tests/src/com/android/services/telephony/MockTelephonyConnection.java
rename to tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 634cbb5..ea0f965 100644
--- a/tests/src/com/android/services/telephony/MockTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -11,11 +11,14 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.services.telephony;
+import android.telecom.PhoneAccountHandle;
+
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.android.internal.telephony.Call;
@@ -28,7 +31,7 @@
* Mock Telephony Connection used in TelephonyConferenceController.java for testing purpose
*/
-public class MockTelephonyConnection extends TelephonyConnection {
+public class TestTelephonyConnection extends TelephonyConnection {
@Mock
com.android.internal.telephony.Connection mMockRadioConnection;
@@ -36,18 +39,19 @@
@Mock
Call mMockCall;
- @Mock
- Phone mMockPhone;
+ private Phone mMockPhone;
+ private int mNotifyPhoneAccountChangedCount = 0;
@Override
public com.android.internal.telephony.Connection getOriginalConnection() {
return mMockRadioConnection;
}
- public MockTelephonyConnection() {
+ public TestTelephonyConnection() {
super(null, null, false);
MockitoAnnotations.initMocks(this);
+ mMockPhone = mock(Phone.class);
// Set up mMockRadioConnection and mMockPhone to contain an active call
when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
@@ -60,6 +64,10 @@
return true;
}
+ public void setMockPhone(Phone newPhone) {
+ mMockPhone = newPhone;
+ }
+
@Override
public Phone getPhone() {
return mMockPhone;
@@ -69,4 +77,12 @@
return this;
}
+ @Override
+ public void notifyPhoneAccountChanged(PhoneAccountHandle pHandle) {
+ mNotifyPhoneAccountChangedCount++;
+ }
+
+ public int getNotifyPhoneAccountChangedCount() {
+ return mNotifyPhoneAccountChangedCount;
+ }
}