Clear IMS call information when SRVCC has completed
Bug: 255450284
Test: atest ImsCallInfoNotifierTest
Change-Id: I82bd36dd736b2770e4ada47c4cb59e36318640a3
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java b/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java
index 718a988..79ab9c5 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java
@@ -78,6 +78,11 @@
return changed;
}
+ /** Called when clearing orphaned connection. */
+ public void onDisconnect() {
+ mState = Call.State.DISCONNECTED;
+ }
+
/** @return the call index. */
public int getIndex() {
return mIndex;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java
index aabe8d3..5783e48 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java
@@ -25,6 +25,7 @@
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
+import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.Collection;
@@ -39,6 +40,8 @@
* Contains the state of all IMS calls.
*/
public class ImsCallInfoTracker {
+ private static final String LOG_TAG = "ImsCallInfoTracker";
+ private static final boolean DBG = false;
private final Phone mPhone;
private final List<ImsCallInfo> mQueue = new ArrayList<>();
@@ -56,6 +59,8 @@
* @param c The instance of {@link ImsPhoneConnection}.
*/
public void addImsCallStatus(@NonNull ImsPhoneConnection c) {
+ if (DBG) Rlog.d(LOG_TAG, "addImsCallStatus");
+
synchronized (mImsCallInfo) {
if (mQueue.isEmpty()) {
mQueue.add(new ImsCallInfo(mNextIndex++));
@@ -69,6 +74,8 @@
mImsCallInfo.put(c, imsCallInfo);
notifyImsCallStatus();
+
+ if (DBG) dump();
}
}
@@ -90,16 +97,26 @@
*/
public void updateImsCallStatus(@NonNull ImsPhoneConnection c,
boolean holdReceived, boolean resumeReceived) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateImsCallStatus holdReceived=" + holdReceived
+ + ", resumeReceived=" + resumeReceived);
+ }
synchronized (mImsCallInfo) {
ImsCallInfo info = mImsCallInfo.get(c);
+ if (info == null) {
+ // This happens when the user tries to hangup the call after handover has completed.
+ return;
+ }
+
boolean changed = info.update(c, holdReceived, resumeReceived);
if (changed) notifyImsCallStatus();
Call.State state = c.getState();
+ if (DBG) Rlog.d(LOG_TAG, "updateImsCallStatus state=" + state);
// Call is disconnected. There are 2 cases in disconnected state:
// if silent redial, state == IDLE, otherwise, state == DISCONNECTED.
if (state == DISCONNECTED || state == IDLE) {
@@ -113,6 +130,42 @@
mNextIndex--;
}
}
+
+ if (DBG) dump();
+ }
+ }
+
+ /** Clears all orphaned IMS call information. */
+ public void clearAllOrphanedConnections() {
+ if (DBG) Rlog.d(LOG_TAG, "clearAllOrphanedConnections");
+
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ infos.stream().forEach(info -> { info.onDisconnect(); });
+ notifyImsCallStatus();
+ clearAllCallInfo();
+
+ if (DBG) dump();
+ }
+
+ /** Notifies that SRVCC has completed. */
+ public void notifySrvccCompleted() {
+ if (DBG) Rlog.d(LOG_TAG, "notifySrvccCompleted");
+
+ clearAllCallInfo();
+ notifyImsCallStatus();
+
+ if (DBG) dump();
+ }
+
+ private void clearAllCallInfo() {
+ try {
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ infos.stream().forEach(info -> { info.reset(); });
+ mImsCallInfo.clear();
+ mQueue.clear();
+ mNextIndex = 1;
+ } catch (UnsupportedOperationException e) {
+ Rlog.e(LOG_TAG, "e=" + e);
}
}
@@ -142,4 +195,11 @@
}
});
}
+
+ private void dump() {
+ Collection<ImsCallInfo> infos = mImsCallInfo.values();
+ ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos);
+ sort(imsCallInfo);
+ Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo);
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 59f01ec..6840f4b 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1479,6 +1479,7 @@
// above. Remove all references to it.
mPendingMO = null;
updatePhoneState();
+ mImsCallInfoTracker.clearAllOrphanedConnections();
}
/**
@@ -4658,6 +4659,7 @@
transferHandoverConnections(mBackgroundCall);
transferHandoverConnections(mRingingCall);
updatePhoneState();
+ mImsCallInfoTracker.notifySrvccCompleted();
break;
case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java
index a0fc4b9..e3fc6d3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java
@@ -363,6 +363,60 @@
assertEquals(2, imsCallInfos.get(1).getIndex());
}
+ @Test
+ public void testSrvccCompleted() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ mImsCallInfoTracker.notifySrvccCompleted();
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(0, imsCallInfos.size());
+ }
+
+ @Test
+ public void testClearAllOrphanedConnections() throws Exception {
+ ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+ ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
+ mImsCallInfoTracker.addImsCallStatus(c);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());
+
+ List<ImsCallInfo> imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ mImsCallInfoTracker.clearAllOrphanedConnections();
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());
+
+ imsCallInfos = captor.getValue();
+
+ assertNotNull(imsCallInfos);
+ assertEquals(1, imsCallInfos.size());
+
+ ImsCallInfo info = imsCallInfos.get(0);
+
+ assertNotNull(info);
+ assertEquals(1, info.getIndex());
+ assertEquals(Call.State.IDLE, info.getCallState());
+ }
+
private ImsPhoneConnection getConnection(Call.State state, boolean isEmergency) {
ImsPhoneConnection c = mock(ImsPhoneConnection.class);
doReturn(isEmergency).when(c).isEmergencyCall();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 6ab7651..0af95e8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -2464,6 +2464,41 @@
verify(mImsPhone, times(7)).updateImsCallStatus(any(), any());
}
+ @Test
+ public void testUpdateImsCallStatusSrvccCompleted() throws Exception {
+ // Incoming call
+ setupRingingConnection();
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ // no interaction when SRVCC has started, failed, or canceled.
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_STARTED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_FAILED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_CANCELED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+
+ // interaction when SRVCC has completed
+ mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);
+
+ verify(mImsPhone, times(2)).updateImsCallStatus(any(), any());
+ }
+
+ @Test
+ public void testClearAllOrphanedConnectionInfo() throws Exception {
+ verify(mImsPhone, times(0)).updateImsCallStatus(any(), any());
+
+ mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
+
+ verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
+ }
+
/** Verifies that the request from ImsService is passed to ImsPhone as expected. */
@Test
@SmallTest