blob: bf9decbb486c1ba1713913c10c6c867fa24db673 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.telecom.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyChar;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.net.Uri;
import android.os.SystemClock;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.server.telecom.AsyncRingtonePlayer;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.CallState;
import com.android.server.telecom.CallerInfoAsyncQueryFactory;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.ClockProxy;
import com.android.server.telecom.ConnectionServiceWrapper;
import com.android.server.telecom.ContactsAsyncHelper;
import com.android.server.telecom.DefaultDialerCache;
import com.android.server.telecom.EmergencyCallHelper;
import com.android.server.telecom.HeadsetMediaButton;
import com.android.server.telecom.HeadsetMediaButtonFactory;
import com.android.server.telecom.InCallController;
import com.android.server.telecom.InCallControllerFactory;
import com.android.server.telecom.InCallTonePlayer;
import com.android.server.telecom.InCallWakeLockController;
import com.android.server.telecom.InCallWakeLockControllerFactory;
import com.android.server.telecom.MissedCallNotifier;
import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.PhoneNumberUtilsAdapter;
import com.android.server.telecom.ProximitySensorManager;
import com.android.server.telecom.ProximitySensorManagerFactory;
import com.android.server.telecom.SystemStateProvider;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
import com.android.server.telecom.WiredHeadsetManager;
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@RunWith(JUnit4.class)
public class CallsManagerTest extends TelecomTestCase {
private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
private static final PhoneAccountHandle SELF_MANAGED_HANDLE = new PhoneAccountHandle(
ComponentName.unflattenFromString("com.foo/.Self"), "Self");
private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.Builder(SIM_1_HANDLE, "Sim1")
.setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
| PhoneAccount.CAPABILITY_CALL_PROVIDER)
.setIsEnabled(true)
.build();
private static final PhoneAccount SIM_2_ACCOUNT = new PhoneAccount.Builder(SIM_2_HANDLE, "Sim2")
.setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
| PhoneAccount.CAPABILITY_CALL_PROVIDER
| PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
.setIsEnabled(true)
.build();
private static final PhoneAccount SELF_MANAGED_ACCOUNT = new PhoneAccount.Builder(
SELF_MANAGED_HANDLE, "Self")
.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
.setIsEnabled(true)
.build();
private static final Uri TEST_ADDRESS = Uri.parse("tel:555-1212");
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
@Mock private ContactsAsyncHelper mContactsAsyncHelper;
@Mock private CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
@Mock private MissedCallNotifier mMissedCallNotifier;
@Mock private PhoneAccountRegistrar mPhoneAccountRegistrar;
@Mock private HeadsetMediaButton mHeadsetMediaButton;
@Mock private HeadsetMediaButtonFactory mHeadsetMediaButtonFactory;
@Mock private ProximitySensorManager mProximitySensorManager;
@Mock private ProximitySensorManagerFactory mProximitySensorManagerFactory;
@Mock private InCallWakeLockController mInCallWakeLockController;
@Mock private InCallWakeLockControllerFactory mInCallWakeLockControllerFactory;
@Mock private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
@Mock private BluetoothRouteManager mBluetoothRouteManager;
@Mock private WiredHeadsetManager mWiredHeadsetManager;
@Mock private SystemStateProvider mSystemStateProvider;
@Mock private DefaultDialerCache mDefaultDialerCache;
@Mock private Timeouts.Adapter mTimeoutsAdapter;
@Mock private AsyncRingtonePlayer mAsyncRingtonePlayer;
@Mock private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
@Mock private EmergencyCallHelper mEmergencyCallHelper;
@Mock private InCallTonePlayer.ToneGeneratorFactory mToneGeneratorFactory;
@Mock private ClockProxy mClockProxy;
@Mock private InCallControllerFactory mInCallControllerFactory;
@Mock private InCallController mInCallController;
private CallsManager mCallsManager;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
when(mInCallWakeLockControllerFactory.create(any(), any())).thenReturn(
mInCallWakeLockController);
when(mHeadsetMediaButtonFactory.create(any(), any(), any())).thenReturn(
mHeadsetMediaButton);
when(mProximitySensorManagerFactory.create(any(), any())).thenReturn(
mProximitySensorManager);
when(mInCallControllerFactory.create(any(), any(), any(), any(), any(), any(),
any())).thenReturn(mInCallController);
when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
when(mClockProxy.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
mCallsManager = new CallsManager(
mComponentContextFixture.getTestDouble().getApplicationContext(),
mLock,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
mMissedCallNotifier,
mPhoneAccountRegistrar,
mHeadsetMediaButtonFactory,
mProximitySensorManagerFactory,
mInCallWakeLockControllerFactory,
mAudioServiceFactory,
mBluetoothRouteManager,
mWiredHeadsetManager,
mSystemStateProvider,
mDefaultDialerCache,
mTimeoutsAdapter,
mAsyncRingtonePlayer,
mPhoneNumberUtilsAdapter,
mEmergencyCallHelper,
mToneGeneratorFactory,
mClockProxy,
mInCallControllerFactory);
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SIM_1_HANDLE), any())).thenReturn(SIM_1_ACCOUNT);
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SIM_2_HANDLE), any())).thenReturn(SIM_2_ACCOUNT);
}
@MediumTest
@Test
public void testConstructPossiblePhoneAccounts() throws Exception {
// Should be empty since the URI is null.
assertEquals(0, mCallsManager.constructPossiblePhoneAccounts(null, null, false).size());
}
/**
* Verify behavior for multisim devices where we want to ensure that the active sim is used for
* placing a new call.
* @throws Exception
*/
@MediumTest
@Test
public void testConstructPossiblePhoneAccountsMultiSimActive() throws Exception {
setupMsimAccounts();
Call ongoingCall = new Call("1", /* callId */
mComponentContextFixture.getTestDouble(),
mCallsManager,
mLock, /* ConnectionServiceRepository */
null,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
mPhoneNumberUtilsAdapter,
TEST_ADDRESS,
null /* GatewayInfo */,
null /* connectionManagerPhoneAccountHandle */,
SIM_2_HANDLE,
Call.CALL_DIRECTION_INCOMING,
false /* shouldAttachToExistingConnection*/,
false /* isConference */,
mClockProxy);
ongoingCall.setState(CallState.ACTIVE, "just cuz");
mCallsManager.addCall(ongoingCall);
List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
TEST_ADDRESS, null, false);
assertEquals(1, phoneAccountHandles.size());
assertEquals(SIM_2_HANDLE, phoneAccountHandles.get(0));
}
/**
* Verify behavior for multisim devices when there are no calls active; expect both accounts.
* @throws Exception
*/
@MediumTest
@Test
public void testConstructPossiblePhoneAccountsMultiSimIdle() throws Exception {
setupMsimAccounts();
List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
TEST_ADDRESS, null, false);
assertEquals(2, phoneAccountHandles.size());
}
/**
* Tests finding the outgoing call phone account where the call is being placed on a
* self-managed ConnectionService.
* @throws Exception
*/
@MediumTest
@Test
public void testFindOutgoingCallPhoneAccountSelfManaged() throws Exception {
List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
SELF_MANAGED_HANDLE, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
assertEquals(1, accounts.size());
assertEquals(SELF_MANAGED_HANDLE, accounts.get(0));
}
/**
* Tests finding the outgoing calling account where the call has no associated phone account,
* but there is a user specified default which can be used.
* @throws Exception
*/
@MediumTest
@Test
public void testFindOutgoingCallAccountDefault() throws Exception {
when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
SIM_1_HANDLE);
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), anyInt())).thenReturn(
new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
null /* phoneAcct */, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
// Should have found just the default.
assertEquals(1, accounts.size());
assertEquals(SIM_1_HANDLE, accounts.get(0));
}
/**
* Tests finding the outgoing calling account where the call has no associated phone account,
* but there is no user specified default which can be used.
* @throws Exception
*/
@MediumTest
@Test
public void testFindOutgoingCallAccountNoDefault() throws Exception {
when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
null);
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), anyInt())).thenReturn(
new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
null /* phoneAcct */, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
assertEquals(2, accounts.size());
assertTrue(accounts.contains(SIM_1_HANDLE));
assertTrue(accounts.contains(SIM_2_HANDLE));
}
/**
* Tests that we will default to a video capable phone account if one is available for a video
* call.
* @throws Exception
*/
@MediumTest
@Test
public void testFindOutgoingCallAccountVideo() throws Exception {
when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
null);
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), eq(PhoneAccount.CAPABILITY_VIDEO_CALLING))).thenReturn(
new ArrayList<>(Arrays.asList(SIM_2_HANDLE)));
List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
null /* phoneAcct */, TEST_ADDRESS, true /* isVideo */, null /* userHandle */);
assertEquals(1, accounts.size());
assertTrue(accounts.contains(SIM_2_HANDLE));
}
/**
* Tests that we will default to a non-video capable phone account for a video call if no video
* capable phone accounts are available.
* @throws Exception
*/
@MediumTest
@Test
public void testFindOutgoingCallAccountVideoNotAvailable() throws Exception {
when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
null);
// When querying for video capable accounts, return nothing.
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), eq(PhoneAccount.CAPABILITY_VIDEO_CALLING))).thenReturn(
Collections.emptyList());
// When querying for non-video capable accounts, return one.
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), eq(0 /* none specified */))).thenReturn(
new ArrayList<>(Arrays.asList(SIM_1_HANDLE)));
List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
null /* phoneAcct */, TEST_ADDRESS, true /* isVideo */, null /* userHandle */);
// Should have found one.
assertEquals(1, accounts.size());
assertTrue(accounts.contains(SIM_1_HANDLE));
}
/**
* Tests that we will use the provided target phone account if it exists.
* @throws Exception
*/
@MediumTest
@Test
public void testUseSpecifiedAccount() throws Exception {
when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
null);
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), anyInt())).thenReturn(
new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
SIM_2_HANDLE, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
assertEquals(1, accounts.size());
assertTrue(accounts.contains(SIM_2_HANDLE));
}
/**
* Verifies that an active call will result in playing a DTMF tone when requested.
* @throws Exception
*/
@MediumTest
@Test
public void testPlayDtmfWhenActive() throws Exception {
Call callSpy = addSpyCall();
mCallsManager.playDtmfTone(callSpy, '1');
verify(callSpy).playDtmfTone(anyChar());
}
/**
* Verifies that DTMF requests are suppressed when a call is held.
* @throws Exception
*/
@MediumTest
@Test
public void testSuppessDtmfWhenHeld() throws Exception {
Call callSpy = addSpyCall();
callSpy.setState(CallState.ON_HOLD, "test");
mCallsManager.playDtmfTone(callSpy, '1');
verify(callSpy, never()).playDtmfTone(anyChar());
}
/**
* Verifies that DTMF requests are suppressed when a call is held.
* @throws Exception
*/
@MediumTest
@Test
public void testCancelDtmfWhenHeld() throws Exception {
Call callSpy = addSpyCall();
mCallsManager.playDtmfTone(callSpy, '1');
mCallsManager.markCallAsOnHold(callSpy);
verify(callSpy).stopDtmfTone();
}
private Call addSpyCall() {
Call ongoingCall = new Call("1", /* callId */
mComponentContextFixture.getTestDouble(),
mCallsManager,
mLock, /* ConnectionServiceRepository */
null,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
mPhoneNumberUtilsAdapter,
TEST_ADDRESS,
null /* GatewayInfo */,
null /* connectionManagerPhoneAccountHandle */,
SIM_2_HANDLE,
Call.CALL_DIRECTION_INCOMING,
false /* shouldAttachToExistingConnection*/,
false /* isConference */,
mClockProxy);
ongoingCall.setState(CallState.ACTIVE, "just cuz");
Call callSpy = Mockito.spy(ongoingCall);
mCallsManager.addCall(callSpy);
return callSpy;
}
private void setupMsimAccounts() {
TelephonyManager mockTelephonyManager = mComponentContextFixture.getTelephonyManager();
when(mockTelephonyManager.getMultiSimConfiguration()).thenReturn(
TelephonyManager.MultiSimVariants.DSDS);
when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
any(), anyInt())).thenReturn(
new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
when(mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser()).thenReturn(
new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
}
}