blob: c5be59553b4b051ac2d3d3325aa72ba2a3f5a53f [file] [log] [blame]
package android.telecom.cts;
import static android.telecom.cts.TestUtils.TEST_PHONE_ACCOUNT_HANDLE;
import static android.telecom.cts.TestUtils.waitOnAllHandlers;
import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Bundle;
import android.provider.CallLog;
import android.telecom.Call;
import android.telecom.Call.Details;
import android.telecom.CallScreeningService.CallResponse;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telecom.cts.MockCallScreeningService.CallScreeningServiceCallbacks;
import android.telecom.cts.api29incallservice.ICtsApi29InCallServiceControl;
import android.text.TextUtils;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import java.util.concurrent.TimeUnit;
public class BackgroundCallAudioTest extends BaseTelecomTestWithMockServices {
private static final String LOG_TAG = BackgroundCallAudioTest.class.getSimpleName();
private ServiceConnection mApiCompatControlServiceConnection;
// copied from AudioSystem.java -- defined here because that change isn't in AOSP yet.
private static final int MODE_CALL_SCREENING = 4;
// true if there's platform support for call screening in the audio stack.
private boolean doesAudioManagerSupportCallScreening = false;
@Override
protected void setUp() throws Exception {
super.setUp();
if (mShouldTestTelecom) {
mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
// Some of the tests expect changes in audio mode when the ringer starts, so we're
// going to turn up the ring stream volume.
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
audioManager.adjustStreamVolume(AudioManager.STREAM_RING,
AudioManager.ADJUST_UNMUTE, 0);
// TODO: uncomment when call screening APIs in AudioManager come to AOSP
/*
doesAudioManagerSupportCallScreening =
audioManager.isCallScreeningModeSupported();
*/
}
}
@Override
protected void tearDown() throws Exception {
if (mShouldTestTelecom && !TextUtils.isEmpty(mPreviousDefaultDialer)) {
TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
mTelecomManager.unregisterPhoneAccount(TEST_PHONE_ACCOUNT_HANDLE);
CtsConnectionService.tearDown();
MockCallScreeningService.disableService(mContext);
}
super.tearDown();
}
public void testAudioProcessingFromCallScreeningAllow() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
verifySimulateRingAndUserPickup(call, connection);
}
public void testAudioProcessingFromCallScreeningDisallow() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
call.disconnect();
assertCallState(call, Call.STATE_DISCONNECTED);
assertEquals(DisconnectCause.REJECTED, call.getDetails().getDisconnectCause().getCode());
}
public void testAudioProcessingFromCallScreeningMissed() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
verifySimulateRingAndUserMissed(call, connection);
}
public void testAudioProcessingFromCallScreeningRemoteHangupDuringRing() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
call.exitBackgroundAudioProcessing(true);
assertCallState(call, Call.STATE_SIMULATED_RINGING);
waitOnAllHandlers(getInstrumentation());
// We expect the audio mode to stay in CALL_SCREENING when going into simulated ringing.
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
assertConnectionState(connection, Connection.STATE_ACTIVE);
connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
assertCallState(call, Call.STATE_DISCONNECTED);
assertEquals(DisconnectCause.MISSED, call.getDetails().getDisconnectCause().getCode());
connection.destroy();
}
public void testAudioProcessingFromCallScreeningAllowPlaceEmergencyCall() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupForEmergencyCalling(TEST_EMERGENCY_NUMBER);
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
call.exitBackgroundAudioProcessing(true);
assertCallState(call, Call.STATE_SIMULATED_RINGING);
waitOnAllHandlers(getInstrumentation());
// We expect the audio mode to stay in CALL_SCREENING when going into simulated ringing.
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
assertConnectionState(connection, Connection.STATE_ACTIVE);
placeAndVerifyEmergencyCall(false /*supportsHold*/);
waitOnAllHandlers(getInstrumentation());
Call eCall = getInCallService().getLastCall();
assertCallState(eCall, Call.STATE_DIALING);
// Even though the connection was technically active, it is "simulated ringing", so
// disconnect as you would a normal ringing call in favor of an emergency call.
assertCallState(call, Call.STATE_DISCONNECTED);
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
// Notify as missed instead of rejected, since the user did not explicitly reject.
verifyCallLogging(connection.getAddress(), CallLog.Calls.MISSED_TYPE);
}
public void testAudioProcessingFromIncomingActivePlaceEmergencyCall() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupForEmergencyCalling(TEST_EMERGENCY_NUMBER);
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
verifySimulateRingAndUserPickup(call, connection);
// Go back into audio processing for hold case
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
waitOnAllHandlers(getInstrumentation());
placeAndVerifyEmergencyCall(false /*supportsHold*/);
waitOnAllHandlers(getInstrumentation());
Call eCall = getInCallService().getLastCall();
assertCallState(eCall, Call.STATE_DIALING);
// Even though the connection was technically active, it is "simulated ringing", so
// disconnect as you would a normal ringing call in favor of an emergency call.
assertCallState(call, Call.STATE_DISCONNECTED);
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
// Notify as incoming, since the user has already answered the call.
verifyCallLogging(connection.getAddress(), CallLog.Calls.INCOMING_TYPE);
}
public void testAudioProcessActiveCall() {
if (!mShouldTestTelecom) {
return;
}
Connection connection = placeActiveOutgoingCall();
Call call = mInCallCallbacks.getService().getLastCall();
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
waitOnAllHandlers(getInstrumentation());
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
assertConnectionState(connection, Connection.STATE_ACTIVE);
verifySimulateRingAndUserPickup(call, connection);
}
public void testAudioProcessActiveCallMissed() throws Exception {
if (!mShouldTestTelecom) {
return;
}
Connection connection = placeActiveOutgoingCall();
Call call = mInCallCallbacks.getService().getLastCall();
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
verifySimulateRingAndUserMissed(call, connection);
}
public void testAudioProcessActiveCallRemoteHangup() {
if (!mShouldTestTelecom) {
return;
}
Connection connection = placeActiveOutgoingCall();
Call call = mInCallCallbacks.getService().getLastCall();
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
assertCallState(call, Call.STATE_DISCONNECTED);
assertEquals(DisconnectCause.REMOTE, call.getDetails().getDisconnectCause().getCode());
connection.destroy();
}
public void testAudioProcessOutgoingActiveEmergencyCallPlaced() throws Exception {
if (!mShouldTestTelecom) {
return;
}
setupForEmergencyCalling(TEST_EMERGENCY_NUMBER);
Connection connection = placeActiveOutgoingCall();
Call call = mInCallCallbacks.getService().getLastCall();
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
placeAndVerifyEmergencyCall(false /*supportsHold*/);
waitOnAllHandlers(getInstrumentation());
Call eCall = getInCallService().getLastCall();
// emergency call should be dialing
assertCallState(eCall, Call.STATE_DIALING);
// audio processing call should be disconnected
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
assertCallState(call, Call.STATE_DISCONNECTED);
// If we went to AUDIO_PROCESSING from an active outgoing call, Make sure the call is
// marked outgoing, not missed.
verifyCallLogging(connection.getAddress(), CallLog.Calls.OUTGOING_TYPE);
}
public void testManualAudioCallScreenAccept() {
if (!mShouldTestTelecom) {
return;
}
addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_RINGING);
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
call.exitBackgroundAudioProcessing(false);
assertCallState(call, Call.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
assertAudioMode(audioManager, AudioManager.MODE_IN_CALL);
}
public void testManualAudioCallScreenReject() {
if (!mShouldTestTelecom) {
return;
}
addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_RINGING);
call.enterBackgroundAudioProcessing();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
call.disconnect();
assertCallState(call, Call.STATE_DISCONNECTED);
assertEquals(DisconnectCause.REJECTED, call.getDetails().getDisconnectCause().getCode());
}
public void testEnterAudioProcessingWithoutPermission() {
if (!mShouldTestTelecom) {
return;
}
if (true) {
// TODO: enable test
return;
}
placeAndVerifyCall();
final MockConnection connection = verifyConnectionForOutgoingCall();
final MockInCallService inCallService = mInCallCallbacks.getService();
connection.setActive();
final Call call = inCallService.getLastCall();
assertCallState(call, Call.STATE_ACTIVE);
try {
call.enterBackgroundAudioProcessing();
fail("Expected SecurityException");
} catch (SecurityException e) {
// expected
}
}
public void testLowerApiLevelCompatibility1() throws Exception {
if (!mShouldTestTelecom) {
return;
}
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity("android.permission.CONTROL_INCALL_EXPERIENCE");
try {
ICtsApi29InCallServiceControl controlInterface = setUpControl();
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
// Make sure that the dummy app never got any calls
assertEquals(0, controlInterface.getHistoricalCallCount());
call.exitBackgroundAudioProcessing(true);
assertCallState(call, Call.STATE_SIMULATED_RINGING);
waitOnAllHandlers(getInstrumentation());
assertConnectionState(connection, Connection.STATE_ACTIVE);
// Make sure that the dummy app sees a ringing call.
assertEquals(Call.STATE_RINGING,
controlInterface.getCallState(call.getDetails().getTelecomCallId()));
call.answer(VideoProfile.STATE_AUDIO_ONLY);
assertCallState(call, Call.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
assertConnectionState(connection, Connection.STATE_ACTIVE);
// Make sure that the dummy app sees an active call.
assertEquals(Call.STATE_ACTIVE,
controlInterface.getCallState(call.getDetails().getTelecomCallId()));
tearDownControl();
} finally {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
}
public void testLowerApiLevelCompatibility2() throws Exception {
if (!mShouldTestTelecom) {
return;
}
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity("android.permission.CONTROL_INCALL_EXPERIENCE");
try {
ICtsApi29InCallServiceControl controlInterface = setUpControl();
setupIncomingCallWithCallScreening();
final MockConnection connection = verifyConnectionForIncomingCall();
if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_AUDIO_PROCESSING);
assertConnectionState(connection, Connection.STATE_ACTIVE);
// Make sure that the dummy app never got any calls
assertEquals(0, controlInterface.getHistoricalCallCount());
call.disconnect();
assertCallState(call, Call.STATE_DISCONNECTED);
waitOnAllHandlers(getInstrumentation());
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
// Make sure that the dummy app never saw the call
assertEquals(0, controlInterface.getHistoricalCallCount());
tearDownControl();
} finally {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
}
private Connection placeActiveOutgoingCall() {
placeAndVerifyCall();
Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_DIALING);
final MockConnection connection = verifyConnectionForOutgoingCall();
connection.setActive();
assertCallState(call, Call.STATE_ACTIVE);
return connection;
}
private void verifySimulateRingAndUserPickup(Call call, Connection connection) {
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
call.exitBackgroundAudioProcessing(true);
assertCallState(call, Call.STATE_SIMULATED_RINGING);
waitOnAllHandlers(getInstrumentation());
// We expect the audio mode to stay in CALL_SCREENING when going into simulated ringing.
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
assertConnectionState(connection, Connection.STATE_ACTIVE);
call.answer(VideoProfile.STATE_AUDIO_ONLY);
assertCallState(call, Call.STATE_ACTIVE);
waitOnAllHandlers(getInstrumentation());
assertAudioMode(audioManager, AudioManager.MODE_IN_CALL);
assertConnectionState(connection, Connection.STATE_ACTIVE);
}
private void verifySimulateRingAndUserMissed(Call call, Connection connection) {
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
call.exitBackgroundAudioProcessing(true);
assertCallState(call, Call.STATE_SIMULATED_RINGING);
waitOnAllHandlers(getInstrumentation());
// We expect the audio mode to stay in CALL_SCREENING when going into simulated ringing.
if (doesAudioManagerSupportCallScreening) {
assertAudioMode(audioManager, MODE_CALL_SCREENING);
}
assertConnectionState(connection, Connection.STATE_ACTIVE);
assertTrue(mTelecomManager.isRinging());
call.disconnect();
assertCallState(call, Call.STATE_DISCONNECTED);
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
assertEquals(DisconnectCause.MISSED, call.getDetails().getDisconnectCause().getCode());
}
private void setupIncomingCallWithCallScreening() throws Exception {
CallScreeningServiceCallbacks callback = new CallScreeningServiceCallbacks() {
@Override
public void onScreenCall(Details callDetails) {
getService().respondToCall(callDetails, new CallResponse.Builder()
.setDisallowCall(false)
.setShouldScreenCallViaAudioProcessing(true)
.build());
lock.release();
}
};
MockCallScreeningService.enableService(mContext);
MockCallScreeningService.setCallbacks(callback);
Bundle extras = new Bundle();
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, createTestNumber());
mTelecomManager.addNewIncomingCall(TEST_PHONE_ACCOUNT_HANDLE, extras);
if (!callback.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
TimeUnit.SECONDS)) {
fail("Call screening service never got the call");
}
}
private ICtsApi29InCallServiceControl setUpControl() throws Exception {
Pair<ServiceConnection, ICtsApi29InCallServiceControl> setupResult =
Api29InCallUtils.setupControl(mContext);
mApiCompatControlServiceConnection = setupResult.first;
return setupResult.second;
}
private void tearDownControl() throws Exception {
Api29InCallUtils.tearDownControl(mContext,
mApiCompatControlServiceConnection);
}
}