Loading src/com/android/server/telecom/CallsManager.java +5 −3 Original line number Diff line number Diff line Loading @@ -266,7 +266,7 @@ public class CallsManager extends Call.ListenerBase RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context); SystemVibrator systemVibrator = new SystemVibrator(context); mInCallController = new InCallController( context, mLock, this, systemStateProvider, defaultDialerAdapter); context, mLock, this, systemStateProvider, defaultDialerAdapter, mTimeoutsAdapter); mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer, ringtoneFactory, systemVibrator, mInCallController); Loading Loading @@ -629,7 +629,8 @@ public class CallsManager extends Call.ListenerBase return false; } CallAudioState getAudioState() { @VisibleForTesting public CallAudioState getAudioState() { return mCallAudioManager.getCallAudioState(); } Loading Loading @@ -1489,7 +1490,8 @@ public class CallsManager extends Call.ListenerBase /** * Returns true if telecom supports adding another top-level call. */ boolean canAddCall() { @VisibleForTesting public boolean canAddCall() { boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; if (!isDeviceProvisioned) { Loading src/com/android/server/telecom/InCallController.java +35 −31 Original line number Diff line number Diff line Loading @@ -217,7 +217,7 @@ public final class InCallController extends CallsManagerListenerBase { InCallController.this.onConnected(mInCallServiceInfo, service); if (!shouldRemainConnected) { // Sometimes we can opt to disconnect for certain reasons, like if the // InCallService rejected our intialization step, or the calls went away // InCallService rejected our initialization step, or the calls went away // in the time it took us to bind to the InCallService. In such cases, we go // ahead and disconnect ourselves. disconnect(); Loading Loading @@ -599,17 +599,19 @@ public final class InCallController extends CallsManagerListenerBase { private final CallsManager mCallsManager; private final SystemStateProvider mSystemStateProvider; private final DefaultDialerManagerAdapter mDefaultDialerAdapter; private final Timeouts.Adapter mTimeoutsAdapter; private CarSwappingInCallServiceConnection mInCallServiceConnection; private NonUIInCallServiceConnectionCollection mNonUIInCallServiceConnections; public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager, SystemStateProvider systemStateProvider, DefaultDialerManagerAdapter defaultDialerAdapter) { DefaultDialerManagerAdapter defaultDialerAdapter, Timeouts.Adapter timeoutsAdapter) { mContext = context; mLock = lock; mCallsManager = callsManager; mSystemStateProvider = systemStateProvider; mDefaultDialerAdapter = defaultDialerAdapter; mTimeoutsAdapter = timeoutsAdapter; Resources resources = mContext.getResources(); mSystemInCallComponentName = new ComponentName( Loading Loading @@ -671,7 +673,7 @@ public final class InCallController extends CallsManagerListenerBase { } } } }.prepare(), Timeouts.getCallRemoveUnbindInCallServicesDelay( }.prepare(), mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay( mContext.getContentResolver())); } call.removeListener(mCallListener); Loading Loading @@ -842,12 +844,16 @@ public final class InCallController extends CallsManagerListenerBase { */ private void unbindFromServices() { if (isBoundToServices()) { if (mInCallServiceConnection != null) { mInCallServiceConnection.disconnect(); mInCallServiceConnection = null; } if (mNonUIInCallServiceConnections != null) { mNonUIInCallServiceConnections.disconnect(); mNonUIInCallServiceConnections = null; } } } /** * Binds to all the UI-providing InCallService as well as system-implemented non-UI Loading Loading @@ -1079,9 +1085,9 @@ public final class InCallController extends CallsManagerListenerBase { // Upon successful connection, send the state of the world to the service. List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls()); if (!calls.isEmpty()) { Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(), info.getComponentName()); Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " + "calls", calls.size(), info.getComponentName()); int numCallsSent = 0; for (Call call : calls) { try { if (call.isExternalCall() && !info.isExternalCallsSupported()) { Loading @@ -1090,7 +1096,7 @@ public final class InCallController extends CallsManagerListenerBase { // Track the call if we don't already know about it. addCall(call); numCallsSent += 1; inCallService.addCall(ParcelableCallUtils.toParcelableCall( call, true /* includeVideoProvider */, Loading @@ -1104,9 +1110,7 @@ public final class InCallController extends CallsManagerListenerBase { inCallService.onCanAddCallChanged(mCallsManager.canAddCall()); } catch (RemoteException ignored) { } } else { return false; } Log.i(this, "%s calls sent to InCallService.", numCallsSent); Trace.endSection(); return true; } Loading src/com/android/server/telecom/ParcelableCallUtils.java +1 −1 Original line number Diff line number Diff line Loading @@ -97,7 +97,7 @@ public class ParcelableCallUtils { } // If this is a single-SIM device, the "default SIM" will always be the only SIM. boolean isDefaultSmsAccount = boolean isDefaultSmsAccount = phoneAccountRegistrar != null && phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount()); if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) { capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT; Loading src/com/android/server/telecom/Timeouts.java +4 −16 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ public final class Timeouts { public long getCallScreeningTimeoutMillis(ContentResolver cr) { return Timeouts.getCallScreeningTimeoutMillis(cr); } public long getCallRemoveUnbindInCallServicesDelay(ContentResolver cr) { return Timeouts.getCallRemoveUnbindInCallServicesDelay(cr); } } /** A prefix to use for all keys so to not clobber the global namespace. */ Loading @@ -52,15 +56,6 @@ public final class Timeouts { return Settings.Secure.getLong(contentResolver, PREFIX + key, defaultValue); } /** * Returns the longest period, in milliseconds, to wait for the query for direct-to-voicemail * to complete. If the query goes beyond this timeout, the incoming call screen is shown to the * user. */ public static long getDirectToVoicemailMillis(ContentResolver contentResolver) { return get(contentResolver, "direct_to_voicemail_ms", 500L); } /** * Returns the amount of time to wait before disconnecting a call that was canceled via * NEW_OUTGOING_CALL broadcast. This timeout allows apps which repost the call using a gateway Loading Loading @@ -141,11 +136,4 @@ public final class Timeouts { public static long getCallScreeningTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "call_screening_timeout", 5000L /* 5 seconds */); } /** * Returns the amount of time to wait for the block checker to allow or disallow a call. */ public static long getBlockCheckTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "block_check_timeout_millis", 500L); } } tests/src/com/android/server/telecom/tests/InCallControllerTests.java +65 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.telecom.tests; import android.Manifest; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; Loading @@ -29,6 +30,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; import android.telecom.InCallService; import android.telecom.ParcelableCall; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.test.mock.MockContext; Loading @@ -46,6 +48,7 @@ import com.android.server.telecom.R; import com.android.server.telecom.SystemStateProvider; import com.android.server.telecom.TelecomServiceImpl.DefaultDialerManagerAdapter; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import org.mockito.ArgumentCaptor; import org.mockito.Mock; Loading @@ -53,6 +56,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.Collections; import java.util.LinkedList; import static org.mockito.Matchers.any; Loading @@ -79,6 +83,7 @@ public class InCallControllerTests extends TelecomTestCase { @Mock Resources mMockResources; @Mock MockContext mMockContext; @Mock DefaultDialerManagerAdapter mMockDefaultDialerAdapter; @Mock Timeouts.Adapter mTimeoutsAdapter; private static final int CURRENT_USER_ID = 900973; private static final String DEF_PKG = "defpkg"; Loading @@ -100,7 +105,7 @@ public class InCallControllerTests extends TelecomTestCase { doReturn(SYS_PKG).when(mMockResources).getString(R.string.ui_default_package); doReturn(SYS_CLASS).when(mMockResources).getString(R.string.incall_default_class); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateProvider, mMockDefaultDialerAdapter); mMockSystemStateProvider, mMockDefaultDialerAdapter, mTimeoutsAdapter); } @Override Loading Loading @@ -278,10 +283,14 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockCallsManager.hasEmergencyCall()).thenReturn(false); when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); when(mMockCallsManager.getAudioState()).thenReturn(null); when(mMockCallsManager.canAddCall()).thenReturn(false); when(mMockCall.isIncoming()).thenReturn(false); when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE); when(mMockCall.getIntentExtras()).thenReturn(callExtras); when(mMockCall.isExternalCall()).thenReturn(false); when(mMockCall.getConferenceableCalls()).thenReturn(Collections.emptyList()); when(mMockDefaultDialerAdapter.getDefaultDialerApplication(mMockContext, CURRENT_USER_ID)) .thenReturn(DEF_PKG); when(mMockContext.bindServiceAsUser( Loading Loading @@ -386,6 +395,61 @@ public class InCallControllerTests extends TelecomTestCase { assertEquals(DEF_CLASS, bindIntent.getComponent().getClassName()); } /** * Make sure that if a call goes away before the in-call service finishes binding and another * call gets connected soon after, the new call will still be sent to the in-call service. */ @MediumTest public void testUnbindDueToCallDisconnect() throws Exception { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockCallsManager.hasEmergencyCall()).thenReturn(false); when(mMockCall.isIncoming()).thenReturn(true); when(mMockCall.isExternalCall()).thenReturn(false); when(mMockDefaultDialerAdapter.getDefaultDialerApplication(mMockContext, CURRENT_USER_ID)) .thenReturn(DEF_PKG); when(mMockContext.bindServiceAsUser( any(Intent.class), any(ServiceConnection.class), anyInt(), any(UserHandle.class))) .thenReturn(true); when(mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(any(ContentResolver.class))) .thenReturn(500L); when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); setupMockPackageManager(true /* default */, true /* system */, false /* external calls */); mInCallController.bindToServices(mMockCall); ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class); ArgumentCaptor<ServiceConnection> serviceConnectionCaptor = ArgumentCaptor.forClass(ServiceConnection.class); verify(mMockContext, times(1)).bindServiceAsUser( bindIntentCaptor.capture(), serviceConnectionCaptor.capture(), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.CURRENT)); // Pretend that the call has gone away. when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); mInCallController.onCallRemoved(mMockCall); // Start the connection, make sure we don't unbind, and make sure that we don't send // anything to the in-call service yet. ServiceConnection serviceConnection = serviceConnectionCaptor.getValue(); ComponentName defDialerComponentName = new ComponentName(DEF_PKG, DEF_CLASS); IBinder mockBinder = mock(IBinder.class); IInCallService mockInCallService = mock(IInCallService.class); when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService); serviceConnection.onServiceConnected(defDialerComponentName, mockBinder); verify(mockInCallService).setInCallAdapter(any(IInCallAdapter.class)); verify(mMockContext, never()).unbindService(serviceConnection); verify(mockInCallService, never()).addCall(any(ParcelableCall.class)); // Now, we add in the call again and make sure that it's sent to the InCallService. when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); mInCallController.onCallAdded(mMockCall); verify(mockInCallService).addCall(any(ParcelableCall.class)); } private void setupMocks(boolean isExternalCall) { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); Loading Loading
src/com/android/server/telecom/CallsManager.java +5 −3 Original line number Diff line number Diff line Loading @@ -266,7 +266,7 @@ public class CallsManager extends Call.ListenerBase RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context); SystemVibrator systemVibrator = new SystemVibrator(context); mInCallController = new InCallController( context, mLock, this, systemStateProvider, defaultDialerAdapter); context, mLock, this, systemStateProvider, defaultDialerAdapter, mTimeoutsAdapter); mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer, ringtoneFactory, systemVibrator, mInCallController); Loading Loading @@ -629,7 +629,8 @@ public class CallsManager extends Call.ListenerBase return false; } CallAudioState getAudioState() { @VisibleForTesting public CallAudioState getAudioState() { return mCallAudioManager.getCallAudioState(); } Loading Loading @@ -1489,7 +1490,8 @@ public class CallsManager extends Call.ListenerBase /** * Returns true if telecom supports adding another top-level call. */ boolean canAddCall() { @VisibleForTesting public boolean canAddCall() { boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; if (!isDeviceProvisioned) { Loading
src/com/android/server/telecom/InCallController.java +35 −31 Original line number Diff line number Diff line Loading @@ -217,7 +217,7 @@ public final class InCallController extends CallsManagerListenerBase { InCallController.this.onConnected(mInCallServiceInfo, service); if (!shouldRemainConnected) { // Sometimes we can opt to disconnect for certain reasons, like if the // InCallService rejected our intialization step, or the calls went away // InCallService rejected our initialization step, or the calls went away // in the time it took us to bind to the InCallService. In such cases, we go // ahead and disconnect ourselves. disconnect(); Loading Loading @@ -599,17 +599,19 @@ public final class InCallController extends CallsManagerListenerBase { private final CallsManager mCallsManager; private final SystemStateProvider mSystemStateProvider; private final DefaultDialerManagerAdapter mDefaultDialerAdapter; private final Timeouts.Adapter mTimeoutsAdapter; private CarSwappingInCallServiceConnection mInCallServiceConnection; private NonUIInCallServiceConnectionCollection mNonUIInCallServiceConnections; public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager, SystemStateProvider systemStateProvider, DefaultDialerManagerAdapter defaultDialerAdapter) { DefaultDialerManagerAdapter defaultDialerAdapter, Timeouts.Adapter timeoutsAdapter) { mContext = context; mLock = lock; mCallsManager = callsManager; mSystemStateProvider = systemStateProvider; mDefaultDialerAdapter = defaultDialerAdapter; mTimeoutsAdapter = timeoutsAdapter; Resources resources = mContext.getResources(); mSystemInCallComponentName = new ComponentName( Loading Loading @@ -671,7 +673,7 @@ public final class InCallController extends CallsManagerListenerBase { } } } }.prepare(), Timeouts.getCallRemoveUnbindInCallServicesDelay( }.prepare(), mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay( mContext.getContentResolver())); } call.removeListener(mCallListener); Loading Loading @@ -842,12 +844,16 @@ public final class InCallController extends CallsManagerListenerBase { */ private void unbindFromServices() { if (isBoundToServices()) { if (mInCallServiceConnection != null) { mInCallServiceConnection.disconnect(); mInCallServiceConnection = null; } if (mNonUIInCallServiceConnections != null) { mNonUIInCallServiceConnections.disconnect(); mNonUIInCallServiceConnections = null; } } } /** * Binds to all the UI-providing InCallService as well as system-implemented non-UI Loading Loading @@ -1079,9 +1085,9 @@ public final class InCallController extends CallsManagerListenerBase { // Upon successful connection, send the state of the world to the service. List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls()); if (!calls.isEmpty()) { Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(), info.getComponentName()); Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " + "calls", calls.size(), info.getComponentName()); int numCallsSent = 0; for (Call call : calls) { try { if (call.isExternalCall() && !info.isExternalCallsSupported()) { Loading @@ -1090,7 +1096,7 @@ public final class InCallController extends CallsManagerListenerBase { // Track the call if we don't already know about it. addCall(call); numCallsSent += 1; inCallService.addCall(ParcelableCallUtils.toParcelableCall( call, true /* includeVideoProvider */, Loading @@ -1104,9 +1110,7 @@ public final class InCallController extends CallsManagerListenerBase { inCallService.onCanAddCallChanged(mCallsManager.canAddCall()); } catch (RemoteException ignored) { } } else { return false; } Log.i(this, "%s calls sent to InCallService.", numCallsSent); Trace.endSection(); return true; } Loading
src/com/android/server/telecom/ParcelableCallUtils.java +1 −1 Original line number Diff line number Diff line Loading @@ -97,7 +97,7 @@ public class ParcelableCallUtils { } // If this is a single-SIM device, the "default SIM" will always be the only SIM. boolean isDefaultSmsAccount = boolean isDefaultSmsAccount = phoneAccountRegistrar != null && phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount()); if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) { capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT; Loading
src/com/android/server/telecom/Timeouts.java +4 −16 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ public final class Timeouts { public long getCallScreeningTimeoutMillis(ContentResolver cr) { return Timeouts.getCallScreeningTimeoutMillis(cr); } public long getCallRemoveUnbindInCallServicesDelay(ContentResolver cr) { return Timeouts.getCallRemoveUnbindInCallServicesDelay(cr); } } /** A prefix to use for all keys so to not clobber the global namespace. */ Loading @@ -52,15 +56,6 @@ public final class Timeouts { return Settings.Secure.getLong(contentResolver, PREFIX + key, defaultValue); } /** * Returns the longest period, in milliseconds, to wait for the query for direct-to-voicemail * to complete. If the query goes beyond this timeout, the incoming call screen is shown to the * user. */ public static long getDirectToVoicemailMillis(ContentResolver contentResolver) { return get(contentResolver, "direct_to_voicemail_ms", 500L); } /** * Returns the amount of time to wait before disconnecting a call that was canceled via * NEW_OUTGOING_CALL broadcast. This timeout allows apps which repost the call using a gateway Loading Loading @@ -141,11 +136,4 @@ public final class Timeouts { public static long getCallScreeningTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "call_screening_timeout", 5000L /* 5 seconds */); } /** * Returns the amount of time to wait for the block checker to allow or disallow a call. */ public static long getBlockCheckTimeoutMillis(ContentResolver contentResolver) { return get(contentResolver, "block_check_timeout_millis", 500L); } }
tests/src/com/android/server/telecom/tests/InCallControllerTests.java +65 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.telecom.tests; import android.Manifest; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; Loading @@ -29,6 +30,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; import android.telecom.InCallService; import android.telecom.ParcelableCall; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.test.mock.MockContext; Loading @@ -46,6 +48,7 @@ import com.android.server.telecom.R; import com.android.server.telecom.SystemStateProvider; import com.android.server.telecom.TelecomServiceImpl.DefaultDialerManagerAdapter; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import org.mockito.ArgumentCaptor; import org.mockito.Mock; Loading @@ -53,6 +56,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.Collections; import java.util.LinkedList; import static org.mockito.Matchers.any; Loading @@ -79,6 +83,7 @@ public class InCallControllerTests extends TelecomTestCase { @Mock Resources mMockResources; @Mock MockContext mMockContext; @Mock DefaultDialerManagerAdapter mMockDefaultDialerAdapter; @Mock Timeouts.Adapter mTimeoutsAdapter; private static final int CURRENT_USER_ID = 900973; private static final String DEF_PKG = "defpkg"; Loading @@ -100,7 +105,7 @@ public class InCallControllerTests extends TelecomTestCase { doReturn(SYS_PKG).when(mMockResources).getString(R.string.ui_default_package); doReturn(SYS_CLASS).when(mMockResources).getString(R.string.incall_default_class); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateProvider, mMockDefaultDialerAdapter); mMockSystemStateProvider, mMockDefaultDialerAdapter, mTimeoutsAdapter); } @Override Loading Loading @@ -278,10 +283,14 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockCallsManager.hasEmergencyCall()).thenReturn(false); when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); when(mMockCallsManager.getAudioState()).thenReturn(null); when(mMockCallsManager.canAddCall()).thenReturn(false); when(mMockCall.isIncoming()).thenReturn(false); when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE); when(mMockCall.getIntentExtras()).thenReturn(callExtras); when(mMockCall.isExternalCall()).thenReturn(false); when(mMockCall.getConferenceableCalls()).thenReturn(Collections.emptyList()); when(mMockDefaultDialerAdapter.getDefaultDialerApplication(mMockContext, CURRENT_USER_ID)) .thenReturn(DEF_PKG); when(mMockContext.bindServiceAsUser( Loading Loading @@ -386,6 +395,61 @@ public class InCallControllerTests extends TelecomTestCase { assertEquals(DEF_CLASS, bindIntent.getComponent().getClassName()); } /** * Make sure that if a call goes away before the in-call service finishes binding and another * call gets connected soon after, the new call will still be sent to the in-call service. */ @MediumTest public void testUnbindDueToCallDisconnect() throws Exception { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockCallsManager.hasEmergencyCall()).thenReturn(false); when(mMockCall.isIncoming()).thenReturn(true); when(mMockCall.isExternalCall()).thenReturn(false); when(mMockDefaultDialerAdapter.getDefaultDialerApplication(mMockContext, CURRENT_USER_ID)) .thenReturn(DEF_PKG); when(mMockContext.bindServiceAsUser( any(Intent.class), any(ServiceConnection.class), anyInt(), any(UserHandle.class))) .thenReturn(true); when(mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(any(ContentResolver.class))) .thenReturn(500L); when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); setupMockPackageManager(true /* default */, true /* system */, false /* external calls */); mInCallController.bindToServices(mMockCall); ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class); ArgumentCaptor<ServiceConnection> serviceConnectionCaptor = ArgumentCaptor.forClass(ServiceConnection.class); verify(mMockContext, times(1)).bindServiceAsUser( bindIntentCaptor.capture(), serviceConnectionCaptor.capture(), eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), eq(UserHandle.CURRENT)); // Pretend that the call has gone away. when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); mInCallController.onCallRemoved(mMockCall); // Start the connection, make sure we don't unbind, and make sure that we don't send // anything to the in-call service yet. ServiceConnection serviceConnection = serviceConnectionCaptor.getValue(); ComponentName defDialerComponentName = new ComponentName(DEF_PKG, DEF_CLASS); IBinder mockBinder = mock(IBinder.class); IInCallService mockInCallService = mock(IInCallService.class); when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService); serviceConnection.onServiceConnected(defDialerComponentName, mockBinder); verify(mockInCallService).setInCallAdapter(any(IInCallAdapter.class)); verify(mMockContext, never()).unbindService(serviceConnection); verify(mockInCallService, never()).addCall(any(ParcelableCall.class)); // Now, we add in the call again and make sure that it's sent to the InCallService. when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall)); mInCallController.onCallAdded(mMockCall); verify(mockInCallService).addCall(any(ParcelableCall.class)); } private void setupMocks(boolean isExternalCall) { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); Loading