Loading src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java +5 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,11 @@ public class ImsCallInfo { return changed; } /** Called when clearing orphaned connection. */ public void onDisconnect() { mState = Call.State.DISCONNECTED; } /** @return the call index. */ public int getIndex() { return mIndex; Loading src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java +60 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.internal.annotations.VisibleForTesting; 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; Loading @@ -39,6 +40,8 @@ import java.util.Map; * 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<>(); Loading @@ -56,6 +59,8 @@ public class ImsCallInfoTracker { * @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++)); Loading @@ -69,6 +74,8 @@ public class ImsCallInfoTracker { mImsCallInfo.put(c, imsCallInfo); notifyImsCallStatus(); if (DBG) dump(); } } Loading @@ -90,16 +97,26 @@ public class ImsCallInfoTracker { */ 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) { Loading @@ -113,6 +130,42 @@ public class ImsCallInfoTracker { 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); } } Loading Loading @@ -142,4 +195,11 @@ public class ImsCallInfoTracker { } }); } private void dump() { Collection<ImsCallInfo> infos = mImsCallInfo.values(); ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos); sort(imsCallInfo); Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo); } } src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +2 −0 Original line number Diff line number Diff line Loading @@ -1479,6 +1479,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { // above. Remove all references to it. mPendingMO = null; updatePhoneState(); mImsCallInfoTracker.clearAllOrphanedConnections(); } /** Loading Loading @@ -4658,6 +4659,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { transferHandoverConnections(mBackgroundCall); transferHandoverConnections(mRingingCall); updatePhoneState(); mImsCallInfoTracker.notifySrvccCompleted(); break; case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: Loading tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,60 @@ public class ImsCallInfoTrackerTest extends TelephonyTest { 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(); Loading tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -2464,6 +2464,41 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { 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 Loading Loading
src/java/com/android/internal/telephony/imsphone/ImsCallInfo.java +5 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,11 @@ public class ImsCallInfo { return changed; } /** Called when clearing orphaned connection. */ public void onDisconnect() { mState = Call.State.DISCONNECTED; } /** @return the call index. */ public int getIndex() { return mIndex; Loading
src/java/com/android/internal/telephony/imsphone/ImsCallInfoTracker.java +60 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.internal.annotations.VisibleForTesting; 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; Loading @@ -39,6 +40,8 @@ import java.util.Map; * 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<>(); Loading @@ -56,6 +59,8 @@ public class ImsCallInfoTracker { * @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++)); Loading @@ -69,6 +74,8 @@ public class ImsCallInfoTracker { mImsCallInfo.put(c, imsCallInfo); notifyImsCallStatus(); if (DBG) dump(); } } Loading @@ -90,16 +97,26 @@ public class ImsCallInfoTracker { */ 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) { Loading @@ -113,6 +130,42 @@ public class ImsCallInfoTracker { 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); } } Loading Loading @@ -142,4 +195,11 @@ public class ImsCallInfoTracker { } }); } private void dump() { Collection<ImsCallInfo> infos = mImsCallInfo.values(); ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos); sort(imsCallInfo); Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo); } }
src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +2 −0 Original line number Diff line number Diff line Loading @@ -1479,6 +1479,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { // above. Remove all references to it. mPendingMO = null; updatePhoneState(); mImsCallInfoTracker.clearAllOrphanedConnections(); } /** Loading Loading @@ -4658,6 +4659,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { transferHandoverConnections(mBackgroundCall); transferHandoverConnections(mRingingCall); updatePhoneState(); mImsCallInfoTracker.notifySrvccCompleted(); break; case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED: Loading
tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallInfoTrackerTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,60 @@ public class ImsCallInfoTrackerTest extends TelephonyTest { 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(); Loading
tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -2464,6 +2464,41 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { 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 Loading