| /* |
| * Copyright (C) 2015 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 android.telecom.cts; |
| |
| import static android.telecom.cts.TestUtils.*; |
| |
| import android.os.Bundle; |
| import android.telecom.Call; |
| import android.telecom.Conference; |
| import android.telecom.Connection; |
| import android.telecom.ConnectionRequest; |
| import android.telecom.DisconnectCause; |
| import android.telecom.PhoneAccount; |
| import android.telecom.PhoneAccountHandle; |
| import android.telecom.StatusHints; |
| import android.telecom.TelecomManager; |
| import android.telecom.VideoProfile; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Extended suite of tests that use {@link CtsConnectionService} and {@link MockInCallService} to |
| * verify the functionality of Call Conferencing. |
| */ |
| public class ConferenceTest extends BaseTelecomTestWithMockServices { |
| |
| public static final int CONF_CAPABILITIES = Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE | |
| Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE | Connection.CAPABILITY_HOLD | |
| Connection.CAPABILITY_MERGE_CONFERENCE | Connection.CAPABILITY_SWAP_CONFERENCE; |
| |
| private Call mCall1, mCall2; |
| private MockConnection mConnection1, mConnection2; |
| MockInCallService mInCallService; |
| Conference mConferenceObject; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| if (mShouldTestTelecom) { |
| addOutgoingCalls(); |
| addConferenceCall(mCall1, mCall2); |
| // Use vanilla conference object so that the CTS coverage tool detects the useage. |
| mConferenceObject = verifyConferenceForOutgoingCall(); |
| verifyConferenceObject(mConferenceObject, mConnection1, mConnection2); |
| } |
| } |
| |
| public void testConferenceCreate() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| if (mCall1.getParent() != conf || mCall2.getParent() != conf) { |
| fail("The 2 participating calls should contain the conference call as its parent"); |
| } |
| if (!(conf.getChildren().contains(mCall1) && conf.getChildren().contains(mCall2))) { |
| fail("The conference call should contain the 2 participating calls as its children"); |
| } |
| assertTrue(mConferenceObject.getConnections().contains(mConnection1)); |
| |
| assertConnectionState(mConferenceObject.getConnections().get(0), Connection.STATE_ACTIVE); |
| assertConnectionState(mConferenceObject.getConnections().get(1), Connection.STATE_ACTIVE); |
| assertConferenceState(mConferenceObject, Connection.STATE_ACTIVE); |
| } |
| |
| public void testConferenceSplit() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| if (!(mCall1.getParent() == conf) && (conf.getChildren().contains(mCall1))) { |
| fail("Call 1 not conferenced"); |
| } |
| assertTrue(mConferenceObject.getConnections().contains(mConnection1)); |
| |
| splitFromConferenceCall(mCall1); |
| |
| if ((mCall1.getParent() == conf) || (conf.getChildren().contains(mCall1))) { |
| fail("Call 1 should not be still conferenced"); |
| } |
| assertFalse(mConferenceObject.getConnections().contains(mConnection1)); |
| } |
| |
| public void testConferenceHoldAndUnhold() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| conf.hold(); |
| assertCallState(conf, Call.STATE_HOLDING); |
| assertCallState(mCall1, Call.STATE_HOLDING); |
| assertCallState(mCall2, Call.STATE_HOLDING); |
| |
| conf.unhold(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| assertCallState(mCall1, Call.STATE_ACTIVE); |
| assertCallState(mCall2, Call.STATE_ACTIVE); |
| } |
| |
| public void testConferenceMergeAndSwap() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| conf.mergeConference(); |
| assertCallDisplayName(mCall1, TestUtils.MERGE_CALLER_NAME); |
| assertCallDisplayName(mCall2, TestUtils.MERGE_CALLER_NAME); |
| assertConnectionCallDisplayName(mConferenceObject.getConnections().get(0), |
| TestUtils.MERGE_CALLER_NAME); |
| assertConnectionCallDisplayName(mConferenceObject.getConnections().get(1), |
| TestUtils.MERGE_CALLER_NAME); |
| |
| conf.swapConference(); |
| assertCallDisplayName(mCall1, TestUtils.SWAP_CALLER_NAME); |
| assertCallDisplayName(mCall2, TestUtils.SWAP_CALLER_NAME); |
| assertConnectionCallDisplayName(mConferenceObject.getConnections().get(0), |
| TestUtils.SWAP_CALLER_NAME); |
| assertConnectionCallDisplayName(mConferenceObject.getConnections().get(1), |
| TestUtils.SWAP_CALLER_NAME); |
| |
| } |
| |
| public void testConferenceSetters() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| placeAndVerifyCall(); |
| MockConnection newConnection = verifyConnectionForOutgoingCall(2); |
| final Call newCall = mInCallService.getLastCall(); |
| |
| ArrayList<Connection> connectionList = new ArrayList<>(); |
| connectionList.add(newConnection); |
| ArrayList<Call> callList = new ArrayList<>(); |
| callList.add(newCall); |
| |
| assertFalse(conf.getDetails().can(Call.Details.CAPABILITY_MUTE)); |
| int capabilities = mConferenceObject.getConnectionCapabilities() | |
| Connection.CAPABILITY_MUTE; |
| mConferenceObject.setConnectionCapabilities(capabilities); |
| assertCallCapability(conf, Call.Details.CAPABILITY_MUTE); |
| |
| assertFalse(conf.getConferenceableCalls().contains(newCall)); |
| mConferenceObject.setConferenceableConnections(connectionList); |
| assertCallConferenceableList(conf, callList); |
| |
| mConferenceObject.setConnectionTime(0); |
| |
| Bundle extras = new Bundle(); |
| extras.putString(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE, "Test"); |
| assertNull(conf.getDetails().getExtras()); |
| mConferenceObject.setExtras(extras); |
| assertCallExtras(conf, TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE, "Test"); |
| |
| StatusHints hints = new StatusHints("Test", null, null); |
| assertNull(conf.getDetails().getStatusHints()); |
| mConferenceObject.setStatusHints(hints); |
| assertCallStatusHints(conf, hints); |
| |
| assertFalse(conf.getChildren().contains(newCall)); |
| mConferenceObject.addConnection(newConnection); |
| assertCallChildrenContains(conf, newCall, true); |
| |
| assertTrue(conf.getChildren().contains(newCall)); |
| mConferenceObject.removeConnection(newConnection); |
| assertCallChildrenContains(conf, newCall, false); |
| |
| assertVideoState(conf, VideoProfile.STATE_AUDIO_ONLY); |
| final MockVideoProvider mockVideoProvider = mConnection1.getMockVideoProvider(); |
| mConferenceObject.setVideoProvider(mConnection1, mockVideoProvider); |
| mConferenceObject.setVideoState(mConnection1, VideoProfile.STATE_BIDIRECTIONAL); |
| assertVideoState(conf, VideoProfile.STATE_BIDIRECTIONAL); |
| |
| // Dialing state is unsupported for conference calls. so, the state remains active. |
| mConferenceObject.setDialing(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| mConferenceObject.setOnHold(); |
| assertCallState(conf, Call.STATE_HOLDING); |
| |
| mConferenceObject.setActive(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| mConferenceObject.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); |
| assertCallState(conf, Call.STATE_DISCONNECTED); |
| |
| // Destroy state is unsupported for conference calls. so, the state remains active. |
| mConferenceObject.destroy(); |
| assertCallState(conf, Call.STATE_DISCONNECTED); |
| } |
| |
| public void testConferenceAddAndRemoveConnection() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| placeAndVerifyCall(); |
| MockConnection newConnection = verifyConnectionForOutgoingCall(2); |
| final Call newCall = mInCallService.getLastCall(); |
| |
| ArrayList<Connection> connectionList = new ArrayList<>(); |
| connectionList.add(newConnection); |
| ArrayList<Call> callList = new ArrayList<>(); |
| callList.add(newCall); |
| |
| assertFalse(conf.getChildren().contains(newCall)); |
| mConferenceObject.addConnection(newConnection); |
| assertCallChildrenContains(conf, newCall, true); |
| |
| assertTrue(conf.getChildren().contains(newCall)); |
| mConferenceObject.removeConnection(newConnection); |
| assertCallChildrenContains(conf, newCall, false); |
| } |
| |
| public void testConferenceDTMFTone() { |
| if (!mShouldTestTelecom) { |
| return; |
| } |
| final Call conf = mInCallService.getLastConferenceCall(); |
| assertCallState(conf, Call.STATE_ACTIVE); |
| |
| assertTrue(((MockConference)mConferenceObject).getDtmfString().isEmpty()); |
| conf.playDtmfTone('1'); |
| assertDtmfString((MockConference)mConferenceObject, "1"); |
| conf.stopDtmfTone(); |
| assertDtmfString((MockConference)mConferenceObject, "1."); |
| conf.playDtmfTone('3'); |
| assertDtmfString((MockConference)mConferenceObject, "1.3"); |
| conf.stopDtmfTone(); |
| assertDtmfString((MockConference)mConferenceObject, "1.3."); |
| } |
| |
| private void verifyConferenceObject(Conference mConferenceObject, MockConnection connection1, |
| MockConnection connection2) { |
| assertNull(mConferenceObject.getCallAudioState()); |
| assertTrue(mConferenceObject.getConferenceableConnections().isEmpty()); |
| assertEquals(connection1.getConnectionCapabilities(), |
| mConferenceObject.getConnectionCapabilities()); |
| assertEquals(connection1.getState(), mConferenceObject.getState()); |
| assertEquals(connection2.getState(), mConferenceObject.getState()); |
| assertTrue(mConferenceObject.getConnections().contains(connection1)); |
| assertTrue(mConferenceObject.getConnections().contains(connection2)); |
| assertEquals(connection1.getDisconnectCause(), mConferenceObject.getDisconnectCause()); |
| assertEquals(connection1.getExtras(), mConferenceObject.getExtras()); |
| assertEquals(connection1.getPhoneAccountHandle(), mConferenceObject.getPhoneAccountHandle()); |
| assertEquals(connection1.getStatusHints(), mConferenceObject.getStatusHints()); |
| assertEquals(VideoProfile.STATE_AUDIO_ONLY, mConferenceObject.getVideoState()); |
| assertNull(mConferenceObject.getVideoProvider()); |
| } |
| |
| private void addOutgoingCalls() { |
| try { |
| PhoneAccount account = setupConnectionService( |
| new MockConnectionService() { |
| @Override |
| public Connection onCreateOutgoingConnection( |
| PhoneAccountHandle connectionManagerPhoneAccount, |
| ConnectionRequest request) { |
| Connection connection = super.onCreateOutgoingConnection( |
| connectionManagerPhoneAccount, |
| request); |
| // Modify the connection object created with local values. |
| int capabilities = connection.getConnectionCapabilities(); |
| connection.setConnectionCapabilities(capabilities | CONF_CAPABILITIES); |
| return connection; |
| } |
| }, FLAG_REGISTER | FLAG_ENABLE); |
| } catch(Exception e) { |
| fail("Error in setting up the connection services"); |
| } |
| |
| placeAndVerifyCall(); |
| mConnection1 = verifyConnectionForOutgoingCall(0); |
| mInCallService = mInCallCallbacks.getService(); |
| mCall1 = mInCallService.getLastCall(); |
| assertCallState(mCall1, Call.STATE_DIALING); |
| mConnection1.setActive(); |
| assertCallState(mCall1, Call.STATE_ACTIVE); |
| |
| placeAndVerifyCall(); |
| mConnection2 = verifyConnectionForOutgoingCall(1); |
| mCall2 = mInCallService.getLastCall(); |
| assertCallState(mCall2, Call.STATE_DIALING); |
| mConnection2.setActive(); |
| assertCallState(mCall2, Call.STATE_ACTIVE); |
| |
| setAndVerifyConferenceablesForOutgoingConnection(0); |
| setAndVerifyConferenceablesForOutgoingConnection(1); |
| } |
| |
| private void assertCallCapability(final Call call, final int capability) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return true; |
| } |
| |
| @Override |
| public Object actual() { |
| return call.getDetails().can(capability); |
| } |
| }, |
| WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, |
| "Call should have capability " + capability |
| ); |
| } |
| |
| private void assertCallConnectTime(final Call call, final int connectTime) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return connectTime; |
| } |
| |
| @Override |
| public Object actual() { |
| return call.getDetails().getConnectTimeMillis(); |
| } |
| }, |
| WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, |
| "Call should have connect time " + connectTime |
| ); |
| } |
| |
| private void assertCallExtras(final Call call, final String key, final String value) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return value; |
| } |
| |
| @Override |
| public Object actual() { |
| return call.getDetails().getExtras() != null ? |
| call.getDetails().getExtras().getString(key) : null; |
| } |
| }, |
| WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, |
| "Call should have extra " + key + "=" + value |
| ); |
| } |
| |
| private void assertCallStatusHints(final Call call, final StatusHints hints) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return hints; |
| } |
| |
| @Override |
| public Object actual() { |
| return call.getDetails().getStatusHints(); |
| } |
| }, |
| WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, |
| "Call should have status hints " + hints |
| ); |
| } |
| |
| private void assertCallChildrenContains(final Call call, final Call childrenCall, |
| final boolean expected) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return expected; |
| } |
| |
| @Override |
| public Object actual() { |
| return call.getChildren().contains(childrenCall); |
| } |
| }, |
| WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, |
| expected == true ? "Call should have child call " + childrenCall : |
| "Call should not have child call " + childrenCall |
| ); |
| } |
| |
| private void assertVideoState(final Call call, final int videoState) { |
| waitUntilConditionIsTrueOrTimeout( |
| new Condition() { |
| @Override |
| public Object expected() { |
| return videoState; |
| } |
| |
| @Override |
| public Object actual() { |
| return call.getDetails().getVideoState(); |
| } |
| }, |
| TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, |
| "Call should be in videoState " + videoState |
| ); |
| } |
| } |