Loading src/java/com/android/internal/telephony/ims/ImsServiceController.java +2 −1 Original line number Diff line number Diff line Loading @@ -184,7 +184,8 @@ public class ImsServiceController { @Override public void notifyImsFeatureStatus(int featureStatus) throws RemoteException { Log.i(LOG_TAG, "notifyImsFeatureStatus"); Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature=" + mFeatureType + ", status=" + featureStatus); sendImsFeatureStatusChanged(mSlotId, mFeatureType, featureStatus); } }; Loading src/java/com/android/internal/telephony/imsphone/ImsPhone.java +2 −2 Original line number Diff line number Diff line Loading @@ -954,8 +954,8 @@ public class ImsPhone extends ImsPhoneBase { } } /* package */ void sendErrorResponse(Message onComplete, Throwable e) { @VisibleForTesting public void sendErrorResponse(Message onComplete, Throwable e) { Rlog.d(LOG_TAG, "sendErrorResponse"); if (onComplete != null) { AsyncResult.forMessage(onComplete, null, getCommandException(e)); Loading src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +68 −18 Original line number Diff line number Diff line Loading @@ -580,10 +580,25 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { */ private boolean mShouldUpdateImsConfigOnDisconnect = false; private ImsServiceProxy.INotifyStatusChanged mNotifyFeatureRemovedCallback = () -> { // Callback fires when ImsManager MMTel Feature changes state private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> { try { if (mImsManager.getImsServiceStatus() != ImsFeature.STATE_READY) { retryGetImsService(); int status = mImsManager.getImsServiceStatus(); log("Status Changed: " + status); switch(status) { case ImsFeature.STATE_READY: { startListeningForCalls(); break; } case ImsFeature.STATE_INITIALIZING: // fall through case ImsFeature.STATE_NOT_AVAILABLE: { stopListeningForCalls(); break; } default: { Log.w(LOG_TAG, "Unexpected State!"); } } } catch (ImsException e) { // Could not get the ImsService, retry! Loading @@ -591,6 +606,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } }; @VisibleForTesting public interface IRetryTimeout { int get(); } /** * Default implementation of interface that calculates the ImsService retry timeout. * Override-able for testing. */ @VisibleForTesting public IRetryTimeout mRetryTimeout = () -> { int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS; if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) { mImsServiceRetryCount++; } return timeout; }; //***** Events Loading Loading @@ -626,13 +659,15 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { private void getImsService() throws ImsException { if (DBG) log("getImsService"); mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()); // Adding to set, will be safe adding multiple times. mImsManager.addNotifyStatusChangedCallback(mNotifyFeatureRemovedCallback); if (mImsManager.getImsServiceStatus() != ImsFeature.STATE_READY) { // We can not call "open" until the ims service is ready throw new ImsException("getImsServiceStatus()", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); // Adding to set, will be safe adding multiple times. If the ImsService is not active yet, // this method will throw an ImsException. mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback); // Wait for ImsService.STATE_READY to start listening for calls. // Call the callback right away for compatibility with older devices that do not use states. mNotifyStatusChangedCallback.notifyStatusChanged(); } private void startListeningForCalls() throws ImsException { mImsServiceRetryCount = 0; mServiceId = mImsManager.open(ImsServiceClass.MMTEL, createIncomingCallPendingIntent(), Loading @@ -659,6 +694,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } } private void stopListeningForCalls() { try { // Only close on valid session. if (mImsManager != null && mServiceId > 0) { mImsManager.close(mServiceId); mServiceId = -1; } } catch (ImsException e) { // If the binder is unavailable, then the ImsService doesn't need to close. } } public void dispose() { if (DBG) log("dispose"); mRingingCall.dispose(); Loading Loading @@ -967,6 +1014,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { loge("dialInternal : " + e); conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); retryGetImsService(); } catch (RemoteException e) { } } Loading Loading @@ -1291,6 +1339,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } catch (ImsException e) { loge("setTTYMode : " + e); mPhone.sendErrorResponse(onComplete, e); retryGetImsService(); } } Loading Loading @@ -1490,6 +1539,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } catch (ImsException e) { loge("sendUSSD : " + e); mPhone.sendErrorResponse(response, e); retryGetImsService(); } } Loading Loading @@ -2862,16 +2912,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } private void retryGetImsService() { // The binder connection is already up. Do not try to get it again. if (mImsManager.isServiceAvailable()) { return; } //Leave mImsManager as null, then CallStateException will be thrown when dialing mImsManager = null; // Exponential backoff during retry, limited to 32 seconds. loge("getImsService: Retrying getting ImsService..."); removeMessages(EVENT_GET_IMS_SERVICE); sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS); if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) { mImsServiceRetryCount++; } sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get()); } private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) Loading tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -353,7 +353,7 @@ public abstract class TelephonyTest { doReturn(mImsExternalCallTracker).when(mTelephonyComponentFactory) .makeImsExternalCallTracker(nullable(ImsPhone.class)); doReturn(mCarrierSignalAgent).when(mTelephonyComponentFactory) .makeCarrierActionAgent(nullable(Phone.class)); .makeCarrierSignalAgent(nullable(Phone.class)); doReturn(mCarrierActionAgent).when(mTelephonyComponentFactory) .makeCarrierActionAgent(nullable(Phone.class)); doReturn(mDeviceStateMonitor).when(mTelephonyComponentFactory) Loading tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +60 −2 Original line number Diff line number Diff line Loading @@ -16,24 +16,29 @@ package com.android.internal.telephony.imsphone; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.support.test.filters.FlakyTest; import android.telephony.PhoneNumberUtils; import android.telephony.ims.feature.ImsFeature; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.LargeTest; import com.android.ims.ImsCall; import com.android.ims.ImsCallProfile; import com.android.ims.ImsConfig; import com.android.ims.ImsConnectionStateListener; import com.android.ims.ImsException; import com.android.ims.ImsManager; import com.android.ims.ImsReasonInfo; import com.android.ims.ImsServiceClass; import com.android.ims.internal.ImsCallSession; import com.android.internal.telephony.Call; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyTest; Loading @@ -50,14 +55,20 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading @@ -73,6 +84,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { private int mServiceId; @Mock private ImsCallSession mImsCallSession; private Handler mCTHander; private class ImsCTHandlerThread extends HandlerThread { Loading @@ -87,6 +99,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.", ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTHander = new Handler(mCTUT.getLooper()); setReady(true); } } Loading Loading @@ -191,8 +204,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { waitUntilReady(); logd("ImsPhoneCallTracker initiated"); /* Make sure getImsService is triggered on a separate thread */ waitForMs(100); /* Make sure getImsService is triggered on handler */ waitForHandlerAction(mCTHander, 100); } @After Loading Loading @@ -436,4 +449,49 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { assertEquals(90210, mCTUT.maybeRemapReasonCode(new ImsReasonInfo(90210, 1, "Call answered elsewhere."))); } @Test @SmallTest public void testDialImsServiceUnavailable() throws ImsException { doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when( mImsManager).createCallProfile(anyInt(), anyInt(), anyInt()); mCTUT.mRetryTimeout = () -> 0; //ms assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); try { mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null); } catch (Exception e) { Assert.fail(); } // wait for handler to process ImsService connection retry waitForHandlerAction(mCTHander, 1000); // 1 second timeout verify(mImsManager, never()).makeCall(anyInt(), nullable(ImsCallProfile.class), eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class)); // Make sure that open is called in ImsPhoneCallTracker when it was first connected and // again after retry. verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class), nullable(ImsConnectionStateListener.class)); } @Test @SmallTest public void testTTYImsServiceUnavailable() throws ImsException { doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when( mImsManager).setUiTTYMode(nullable(Context.class), anyInt(), nullable(Message.class)); // Remove retry timeout delay mCTUT.mRetryTimeout = () -> 0; //ms mCTUT.setUiTTYMode(0, new Message()); // wait for handler to process ImsService connection retry waitForHandlerAction(mCTHander, 100); // Make sure that open is called in ImsPhoneCallTracker to re-establish connection to // ImsService verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class), nullable(ImsConnectionStateListener.class)); } } Loading
src/java/com/android/internal/telephony/ims/ImsServiceController.java +2 −1 Original line number Diff line number Diff line Loading @@ -184,7 +184,8 @@ public class ImsServiceController { @Override public void notifyImsFeatureStatus(int featureStatus) throws RemoteException { Log.i(LOG_TAG, "notifyImsFeatureStatus"); Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature=" + mFeatureType + ", status=" + featureStatus); sendImsFeatureStatusChanged(mSlotId, mFeatureType, featureStatus); } }; Loading
src/java/com/android/internal/telephony/imsphone/ImsPhone.java +2 −2 Original line number Diff line number Diff line Loading @@ -954,8 +954,8 @@ public class ImsPhone extends ImsPhoneBase { } } /* package */ void sendErrorResponse(Message onComplete, Throwable e) { @VisibleForTesting public void sendErrorResponse(Message onComplete, Throwable e) { Rlog.d(LOG_TAG, "sendErrorResponse"); if (onComplete != null) { AsyncResult.forMessage(onComplete, null, getCommandException(e)); Loading
src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +68 −18 Original line number Diff line number Diff line Loading @@ -580,10 +580,25 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { */ private boolean mShouldUpdateImsConfigOnDisconnect = false; private ImsServiceProxy.INotifyStatusChanged mNotifyFeatureRemovedCallback = () -> { // Callback fires when ImsManager MMTel Feature changes state private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> { try { if (mImsManager.getImsServiceStatus() != ImsFeature.STATE_READY) { retryGetImsService(); int status = mImsManager.getImsServiceStatus(); log("Status Changed: " + status); switch(status) { case ImsFeature.STATE_READY: { startListeningForCalls(); break; } case ImsFeature.STATE_INITIALIZING: // fall through case ImsFeature.STATE_NOT_AVAILABLE: { stopListeningForCalls(); break; } default: { Log.w(LOG_TAG, "Unexpected State!"); } } } catch (ImsException e) { // Could not get the ImsService, retry! Loading @@ -591,6 +606,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } }; @VisibleForTesting public interface IRetryTimeout { int get(); } /** * Default implementation of interface that calculates the ImsService retry timeout. * Override-able for testing. */ @VisibleForTesting public IRetryTimeout mRetryTimeout = () -> { int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS; if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) { mImsServiceRetryCount++; } return timeout; }; //***** Events Loading Loading @@ -626,13 +659,15 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { private void getImsService() throws ImsException { if (DBG) log("getImsService"); mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()); // Adding to set, will be safe adding multiple times. mImsManager.addNotifyStatusChangedCallback(mNotifyFeatureRemovedCallback); if (mImsManager.getImsServiceStatus() != ImsFeature.STATE_READY) { // We can not call "open" until the ims service is ready throw new ImsException("getImsServiceStatus()", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); // Adding to set, will be safe adding multiple times. If the ImsService is not active yet, // this method will throw an ImsException. mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback); // Wait for ImsService.STATE_READY to start listening for calls. // Call the callback right away for compatibility with older devices that do not use states. mNotifyStatusChangedCallback.notifyStatusChanged(); } private void startListeningForCalls() throws ImsException { mImsServiceRetryCount = 0; mServiceId = mImsManager.open(ImsServiceClass.MMTEL, createIncomingCallPendingIntent(), Loading @@ -659,6 +694,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } } private void stopListeningForCalls() { try { // Only close on valid session. if (mImsManager != null && mServiceId > 0) { mImsManager.close(mServiceId); mServiceId = -1; } } catch (ImsException e) { // If the binder is unavailable, then the ImsService doesn't need to close. } } public void dispose() { if (DBG) log("dispose"); mRingingCall.dispose(); Loading Loading @@ -967,6 +1014,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { loge("dialInternal : " + e); conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); retryGetImsService(); } catch (RemoteException e) { } } Loading Loading @@ -1291,6 +1339,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } catch (ImsException e) { loge("setTTYMode : " + e); mPhone.sendErrorResponse(onComplete, e); retryGetImsService(); } } Loading Loading @@ -1490,6 +1539,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } catch (ImsException e) { loge("sendUSSD : " + e); mPhone.sendErrorResponse(response, e); retryGetImsService(); } } Loading Loading @@ -2862,16 +2912,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } private void retryGetImsService() { // The binder connection is already up. Do not try to get it again. if (mImsManager.isServiceAvailable()) { return; } //Leave mImsManager as null, then CallStateException will be thrown when dialing mImsManager = null; // Exponential backoff during retry, limited to 32 seconds. loge("getImsService: Retrying getting ImsService..."); removeMessages(EVENT_GET_IMS_SERVICE); sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS); if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) { mImsServiceRetryCount++; } sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get()); } private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) Loading
tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -353,7 +353,7 @@ public abstract class TelephonyTest { doReturn(mImsExternalCallTracker).when(mTelephonyComponentFactory) .makeImsExternalCallTracker(nullable(ImsPhone.class)); doReturn(mCarrierSignalAgent).when(mTelephonyComponentFactory) .makeCarrierActionAgent(nullable(Phone.class)); .makeCarrierSignalAgent(nullable(Phone.class)); doReturn(mCarrierActionAgent).when(mTelephonyComponentFactory) .makeCarrierActionAgent(nullable(Phone.class)); doReturn(mDeviceStateMonitor).when(mTelephonyComponentFactory) Loading
tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +60 −2 Original line number Diff line number Diff line Loading @@ -16,24 +16,29 @@ package com.android.internal.telephony.imsphone; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.support.test.filters.FlakyTest; import android.telephony.PhoneNumberUtils; import android.telephony.ims.feature.ImsFeature; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.LargeTest; import com.android.ims.ImsCall; import com.android.ims.ImsCallProfile; import com.android.ims.ImsConfig; import com.android.ims.ImsConnectionStateListener; import com.android.ims.ImsException; import com.android.ims.ImsManager; import com.android.ims.ImsReasonInfo; import com.android.ims.ImsServiceClass; import com.android.ims.internal.ImsCallSession; import com.android.internal.telephony.Call; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyTest; Loading @@ -50,14 +55,20 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading @@ -73,6 +84,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { private int mServiceId; @Mock private ImsCallSession mImsCallSession; private Handler mCTHander; private class ImsCTHandlerThread extends HandlerThread { Loading @@ -87,6 +99,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.", ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTHander = new Handler(mCTUT.getLooper()); setReady(true); } } Loading Loading @@ -191,8 +204,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { waitUntilReady(); logd("ImsPhoneCallTracker initiated"); /* Make sure getImsService is triggered on a separate thread */ waitForMs(100); /* Make sure getImsService is triggered on handler */ waitForHandlerAction(mCTHander, 100); } @After Loading Loading @@ -436,4 +449,49 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { assertEquals(90210, mCTUT.maybeRemapReasonCode(new ImsReasonInfo(90210, 1, "Call answered elsewhere."))); } @Test @SmallTest public void testDialImsServiceUnavailable() throws ImsException { doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when( mImsManager).createCallProfile(anyInt(), anyInt(), anyInt()); mCTUT.mRetryTimeout = () -> 0; //ms assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState()); assertEquals(PhoneConstants.State.IDLE, mCTUT.getState()); try { mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null); } catch (Exception e) { Assert.fail(); } // wait for handler to process ImsService connection retry waitForHandlerAction(mCTHander, 1000); // 1 second timeout verify(mImsManager, never()).makeCall(anyInt(), nullable(ImsCallProfile.class), eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class)); // Make sure that open is called in ImsPhoneCallTracker when it was first connected and // again after retry. verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class), nullable(ImsConnectionStateListener.class)); } @Test @SmallTest public void testTTYImsServiceUnavailable() throws ImsException { doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when( mImsManager).setUiTTYMode(nullable(Context.class), anyInt(), nullable(Message.class)); // Remove retry timeout delay mCTUT.mRetryTimeout = () -> 0; //ms mCTUT.setUiTTYMode(0, new Message()); // wait for handler to process ImsService connection retry waitForHandlerAction(mCTHander, 100); // Make sure that open is called in ImsPhoneCallTracker to re-establish connection to // ImsService verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class), nullable(ImsConnectionStateListener.class)); } }