Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d80cf1a7 authored by Grace Jia's avatar Grace Jia
Browse files

DO NOT MERGE

Fix security vulnerability issue for multi user call redirections.

Currently we won't check if the PhoneAccountHandle provided by a
CallRedirectionService has multi-user capability or belong to the same
user as the current user. Add the check and disconnect the call if this
is an unexpected cross-user call redirection.

Bug: 235098883
Test: CallsManagerTest, manual test with test app provided in
b/235098883.

Change-Id: Ia8b9468aa2bb8e3157c227e2617ff6a52e0af119
(cherry picked from commit f29ab7e1)
parent 960147d4
Loading
Loading
Loading
Loading
+15 −5
Original line number Original line Diff line number Diff line
@@ -1722,8 +1722,17 @@ public class CallsManager extends Call.ListenerBase


        boolean endEarly = false;
        boolean endEarly = false;
        String disconnectReason = "";
        String disconnectReason = "";

        String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp();
        String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp();
        PhoneAccount phoneAccount = mPhoneAccountRegistrar
                .getPhoneAccountUnchecked(phoneAccountHandle);
        if (phoneAccount != null
                && !phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
            // Check if the phoneAccountHandle belongs to the current user
            if (phoneAccountHandle != null &&
                    !phoneAccountHandle.getUserHandle().equals(mCurrentUserHandle)) {
                phoneAccountHandle = null;
            }
        }


        if (shouldCancelCall) {
        if (shouldCancelCall) {
            Log.w(this, "onCallRedirectionComplete: call is canceled");
            Log.w(this, "onCallRedirectionComplete: call is canceled");
@@ -1746,9 +1755,9 @@ public class CallsManager extends Call.ListenerBase
            endEarly = true;
            endEarly = true;
            disconnectReason = "Null handle from Call Redirection Service";
            disconnectReason = "Null handle from Call Redirection Service";
        } else if (phoneAccountHandle == null) {
        } else if (phoneAccountHandle == null) {
            Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null");
            Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is unavailable");
            endEarly = true;
            endEarly = true;
            disconnectReason = "Null phoneAccountHandle from Call Redirection Service";
            disconnectReason = "Unavailable phoneAccountHandle from Call Redirection Service";
        } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext,
        } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext,
                handle.getSchemeSpecificPart())) {
                handle.getSchemeSpecificPart())) {
            Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
            Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
@@ -1770,6 +1779,7 @@ public class CallsManager extends Call.ListenerBase
            return;
            return;
        }
        }


        final PhoneAccountHandle finalPhoneAccountHandle = phoneAccountHandle;
        if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) {
        if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) {
            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION);
            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION);
            mPendingRedirectedOutgoingCall = call;
            mPendingRedirectedOutgoingCall = call;
@@ -1779,7 +1789,7 @@ public class CallsManager extends Call.ListenerBase
                        @Override
                        @Override
                        public void loggedRun() {
                        public void loggedRun() {
                            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED);
                            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED);
                            call.setTargetPhoneAccount(phoneAccountHandle);
                            call.setTargetPhoneAccount(finalPhoneAccountHandle);
                            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn,
                            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn,
                                    videoState);
                                    videoState);
                        }
                        }
@@ -1789,7 +1799,7 @@ public class CallsManager extends Call.ListenerBase
                    new Runnable("CM.oCRC", mLock) {
                    new Runnable("CM.oCRC", mLock) {
                        @Override
                        @Override
                        public void loggedRun() {
                        public void loggedRun() {
                            call.setTargetPhoneAccount(phoneAccountHandle);
                            call.setTargetPhoneAccount(finalPhoneAccountHandle);
                            placeOutgoingCall(call, handle, null, speakerphoneOn,
                            placeOutgoingCall(call, handle, null, speakerphoneOn,
                                    videoState);
                                    videoState);
                        }
                        }
+91 −37
Original line number Original line Diff line number Diff line
@@ -31,6 +31,9 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


@@ -39,8 +42,11 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Process;
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.UserHandle;
import android.telecom.Connection;
import android.telecom.Connection;
import android.telecom.Log;
import android.telecom.Log;
import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.TelecomManager;
@@ -108,8 +114,12 @@ import java.util.concurrent.TimeUnit;
@RunWith(JUnit4.class)
@RunWith(JUnit4.class)
public class CallsManagerTest extends TelecomTestCase {
public class CallsManagerTest extends TelecomTestCase {
    private static final int TEST_TIMEOUT = 5000;  // milliseconds
    private static final int TEST_TIMEOUT = 5000;  // milliseconds
    private static final int SECONDARY_USER_ID = 12;
    private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
    private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
    private static final PhoneAccountHandle SIM_1_HANDLE_SECONDARY = new PhoneAccountHandle(
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1",
            new UserHandle(SECONDARY_USER_ID));
    private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
    private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
    private static final PhoneAccountHandle VOIP_1_HANDLE = new PhoneAccountHandle(
    private static final PhoneAccountHandle VOIP_1_HANDLE = new PhoneAccountHandle(
@@ -525,13 +535,13 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testUnholdCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
    public void testUnholdCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // and a held call which has different ConnectionService
        // and a held call which has different ConnectionService
        Call heldCall = addSpyCall(VOIP_1_HANDLE);
        Call heldCall = addSpyCall(VOIP_1_HANDLE, CallState.ON_HOLD);


        // WHEN unhold the held call
        // WHEN unhold the held call
        mCallsManager.unholdCall(heldCall);
        mCallsManager.unholdCall(heldCall);
@@ -549,14 +559,14 @@ public class CallsManagerTest extends TelecomTestCase {
    public void testUnholdCallWhenOngoingEmergCallCanNotBeHeldAndFromDifferentConnectionService() {
    public void testUnholdCallWhenOngoingEmergCallCanNotBeHeldAndFromDifferentConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held, but it also an
        // GIVEN a CallsManager with ongoing call, and this call can not be held, but it also an
        // emergency call.
        // emergency call.
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).isEmergencyCall();
        doReturn(true).when(ongoingCall).isEmergencyCall();
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // and a held call which has different ConnectionService
        // and a held call which has different ConnectionService
        Call heldCall = addSpyCall(VOIP_1_HANDLE);
        Call heldCall = addSpyCall(VOIP_1_HANDLE, CallState.ON_HOLD);


        // WHEN unhold the held call
        // WHEN unhold the held call
        mCallsManager.unholdCall(heldCall);
        mCallsManager.unholdCall(heldCall);
@@ -572,13 +582,13 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
    public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // and a held call which has the same ConnectionService
        // and a held call which has the same ConnectionService
        Call heldCall = addSpyCall(SIM_2_HANDLE);
        Call heldCall = addSpyCall(SIM_2_HANDLE, CallState.ON_HOLD);


        // WHEN unhold the held call
        // WHEN unhold the held call
        mCallsManager.unholdCall(heldCall);
        mCallsManager.unholdCall(heldCall);
@@ -616,12 +626,12 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testAnswerCallWhenOngoingHasSameConnectionService() {
    public void testAnswerCallWhenOngoingHasSameConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // WHEN answer an incoming call
        // WHEN answer an incoming call
        Call incomingCall = addSpyCall(VOIP_1_HANDLE);
        Call incomingCall = addSpyCall(VOIP_1_HANDLE, CallState.RINGING);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);


        // THEN nothing happened on the ongoing call and the focus request for incoming call is sent
        // THEN nothing happened on the ongoing call and the focus request for incoming call is sent
@@ -635,13 +645,13 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testAnswerCallWhenOngoingHasDifferentConnectionService() {
    public void testAnswerCallWhenOngoingHasDifferentConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // WHEN answer an incoming call
        // WHEN answer an incoming call
        Call incomingCall = addSpyCall(VOIP_1_HANDLE);
        Call incomingCall = addSpyCall(VOIP_1_HANDLE, CallState.RINGING);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);


        // THEN the ongoing call is disconnected and the focus request for incoming call is sent
        // THEN the ongoing call is disconnected and the focus request for incoming call is sent
@@ -656,14 +666,14 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testAnswerCallWhenOngoingHasDifferentConnectionServiceButIsEmerg() {
    public void testAnswerCallWhenOngoingHasDifferentConnectionServiceButIsEmerg() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).isEmergencyCall();
        doReturn(true).when(ongoingCall).isEmergencyCall();
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // WHEN answer an incoming call
        // WHEN answer an incoming call
        Call incomingCall = addSpyCall(VOIP_1_HANDLE);
        Call incomingCall = addSpyCall(VOIP_1_HANDLE, CallState.RINGING);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);


        // THEN the ongoing call is not disconnected
        // THEN the ongoing call is not disconnected
@@ -679,21 +689,21 @@ public class CallsManagerTest extends TelecomTestCase {
    public void testAnswerCallWhenMultipleHeldCallsExisted() {
    public void testAnswerCallWhenMultipleHeldCallsExisted() {
        // Given an ongoing call and held call with the ConnectionService connSvr1. The
        // Given an ongoing call and held call with the ConnectionService connSvr1. The
        // ConnectionService connSvr1 can handle one held call
        // ConnectionService connSvr1 can handle one held call
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(CallState.ACTIVE).when(ongoingCall).getState();
        doReturn(CallState.ACTIVE).when(ongoingCall).getState();
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        Call heldCall = addSpyCall(SIM_1_HANDLE);
        Call heldCall = addSpyCall(SIM_1_HANDLE, CallState.ON_HOLD);
        doReturn(CallState.ON_HOLD).when(heldCall).getState();
        doReturn(CallState.ON_HOLD).when(heldCall).getState();


        // and other held call has difference ConnectionService
        // and other held call has difference ConnectionService
        Call heldCall2 = addSpyCall(VOIP_1_HANDLE);
        Call heldCall2 = addSpyCall(VOIP_1_HANDLE, CallState.ON_HOLD);
        doReturn(CallState.ON_HOLD).when(heldCall2).getState();
        doReturn(CallState.ON_HOLD).when(heldCall2).getState();


        // WHEN answer an incoming call which ConnectionService is connSvr1
        // WHEN answer an incoming call which ConnectionService is connSvr1
        Call incomingCall = addSpyCall(SIM_1_HANDLE);
        Call incomingCall = addSpyCall(SIM_1_HANDLE, CallState.RINGING);
        doReturn(true).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);


@@ -752,13 +762,13 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(ongoingCall).when(mConnectionSvrFocusMgr).getCurrentFocusCall();
        doReturn(ongoingCall).when(mConnectionSvrFocusMgr).getCurrentFocusCall();


        // and a new self-managed call which has different ConnectionService
        // and a new self-managed call which has different ConnectionService
        Call newCall = addSpyCall(VOIP_1_HANDLE);
        Call newCall = addSpyCall(VOIP_1_HANDLE, CallState.ACTIVE);
        doReturn(true).when(newCall).isSelfManaged();
        doReturn(true).when(newCall).isSelfManaged();


        // WHEN active the new call
        // WHEN active the new call
@@ -776,13 +786,13 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        // GIVEN a CallsManager with ongoing call, and this call can not be held
        Call ongoingCall = addSpyCall(SIM_1_HANDLE);
        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);


        // and a new self-managed call which has the same ConnectionService
        // and a new self-managed call which has the same ConnectionService
        Call newCall = addSpyCall(SIM_1_HANDLE);
        Call newCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
        doReturn(true).when(newCall).isSelfManaged();
        doReturn(true).when(newCall).isSelfManaged();


        // WHEN active the new call
        // WHEN active the new call
@@ -824,7 +834,7 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testDisconnectDialingCallOnIncoming() {
    public void testDisconnectDialingCallOnIncoming() {
        // GIVEN a CallsManager with a self-managed call which is dialing, and this call can be held
        // GIVEN a CallsManager with a self-managed call which is dialing, and this call can be held
        Call ongoingCall = addSpyCall(SELF_MANAGED_HANDLE);
        Call ongoingCall = addSpyCall(SELF_MANAGED_HANDLE, CallState.DIALING);
        ongoingCall.setState(CallState.DIALING, "test");
        ongoingCall.setState(CallState.DIALING, "test");
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
@@ -857,7 +867,7 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testNoFilteringOfSelfManagedCalls() {
    public void testNoFilteringOfSelfManagedCalls() {
        // GIVEN an incoming call which is self managed.
        // GIVEN an incoming call which is self managed.
        Call incomingCall = addSpyCall(SELF_MANAGED_HANDLE);
        Call incomingCall = addSpyCall(SELF_MANAGED_HANDLE, CallState.NEW);
        doReturn(false).when(incomingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(incomingCall).can(Connection.CAPABILITY_HOLD);
        doReturn(false).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(false).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
        doReturn(true).when(incomingCall).isSelfManaged();
        doReturn(true).when(incomingCall).isSelfManaged();
@@ -974,7 +984,7 @@ public class CallsManagerTest extends TelecomTestCase {
    @Test
    @Test
    public void testNoFilteringOfCallsWhenPhoneAccountRequestsSkipped() {
    public void testNoFilteringOfCallsWhenPhoneAccountRequestsSkipped() {
        // GIVEN an incoming call which is from a PhoneAccount that requested to skip filtering.
        // GIVEN an incoming call which is from a PhoneAccount that requested to skip filtering.
        Call incomingCall = addSpyCall(SIM_1_HANDLE);
        Call incomingCall = addSpyCall(SIM_1_HANDLE, CallState.NEW);
        Bundle extras = new Bundle();
        Bundle extras = new Bundle();
        extras.putBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING, true);
        extras.putBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING, true);
        PhoneAccount skipRequestedAccount = new PhoneAccount.Builder(SIM_2_HANDLE, "Skipper")
        PhoneAccount skipRequestedAccount = new PhoneAccount.Builder(SIM_2_HANDLE, "Skipper")
@@ -1084,11 +1094,61 @@ public class CallsManagerTest extends TelecomTestCase {
                Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
                Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
    }
    }


    @SmallTest
    @Test
    public void testCrossUserCallRedirectionEndEarlyForIncapablePhoneAccount() {
        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(eq(SIM_1_HANDLE_SECONDARY)))
                .thenReturn(SIM_1_ACCOUNT);
        mCallsManager.onUserSwitch(UserHandle.SYSTEM);

        Call callSpy = addSpyCall(CallState.NEW);
        mCallsManager.onCallRedirectionComplete(callSpy, TEST_ADDRESS, SIM_1_HANDLE_SECONDARY,
                new GatewayInfo("foo", TEST_ADDRESS2, TEST_ADDRESS), true /* speakerphoneOn */,
                VideoProfile.STATE_AUDIO_ONLY, false /* shouldCancelCall */, "" /* uiAction */);

        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        verify(callSpy).disconnect(argumentCaptor.capture());
        assertTrue(argumentCaptor.getValue().contains("Unavailable phoneAccountHandle"));
    }

    private Call addSpyCall() {
    private Call addSpyCall() {
        return addSpyCall(SIM_2_HANDLE);
        return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
    }

    private Call addSpyCall(int initialState) {
        return addSpyCall(SIM_2_HANDLE, initialState);
    }

    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, int initialState) {
        return addSpyCall(targetPhoneAccount, null, initialState, 0 /*caps*/, 0 /*props*/);
    }

    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount,
            PhoneAccountHandle connectionMgrAcct, int initialState) {
        return addSpyCall(targetPhoneAccount, connectionMgrAcct, initialState, 0 /*caps*/,
                0 /*props*/);
    }

    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount,
            PhoneAccountHandle connectionMgrAcct, int initialState,
            int connectionCapabilities, int connectionProperties) {
        Call ongoingCall = createCall(targetPhoneAccount, connectionMgrAcct, initialState);
        ongoingCall.setConnectionProperties(connectionProperties);
        ongoingCall.setConnectionCapabilities(connectionCapabilities);
        Call callSpy = Mockito.spy(ongoingCall);

        // Mocks some methods to not call the real method.
        doNothing().when(callSpy).unhold();
        doNothing().when(callSpy).hold();
        doNothing().when(callSpy).answer(Matchers.anyInt());
        doNothing().when(callSpy).setStartWithSpeakerphoneOn(Matchers.anyBoolean());

        mCallsManager.addCall(callSpy);
        return callSpy;
    }
    }


    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount) {
    private Call createCall(PhoneAccountHandle targetPhoneAccount,
            PhoneAccountHandle connectionManagerAccount, int initialState) {
        Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */
        Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */
                mComponentContextFixture.getTestDouble(),
                mComponentContextFixture.getTestDouble(),
                mCallsManager,
                mCallsManager,
@@ -1097,24 +1157,18 @@ public class CallsManagerTest extends TelecomTestCase {
                mPhoneNumberUtilsAdapter,
                mPhoneNumberUtilsAdapter,
                TEST_ADDRESS,
                TEST_ADDRESS,
                null /* GatewayInfo */,
                null /* GatewayInfo */,
                null /* connectionManagerPhoneAccountHandle */,
                connectionManagerAccount,
                targetPhoneAccount,
                targetPhoneAccount,
                Call.CALL_DIRECTION_INCOMING,
                Call.CALL_DIRECTION_INCOMING,
                false /* shouldAttachToExistingConnection*/,
                false /* shouldAttachToExistingConnection*/,
                false /* isConference */,
                false /* isConference */,
                mClockProxy);
                mClockProxy);
        ongoingCall.setState(CallState.ACTIVE, "just cuz");
        ongoingCall.setState(initialState, "just cuz");
        Call callSpy = Mockito.spy(ongoingCall);
        return ongoingCall;

    }
        // Mocks some methods to not call the real method.
        doNothing().when(callSpy).unhold();
        doNothing().when(callSpy).hold();
        doNothing().when(callSpy).disconnect();
        doNothing().when(callSpy).answer(Matchers.anyInt());
        doNothing().when(callSpy).setStartWithSpeakerphoneOn(Matchers.anyBoolean());


        mCallsManager.addCall(callSpy);
    private Call createCall(PhoneAccountHandle targetPhoneAccount, int initialState) {
        return callSpy;
        return createCall(targetPhoneAccount, null /* connectionManager */, initialState);
    }
    }


    private void verifyFocusRequestAndExecuteCallback(Call call) {
    private void verifyFocusRequestAndExecuteCallback(Call call) {