1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.telecom.tests;
18
19import org.mockito.ArgumentCaptor;
20
21import android.os.Process;
22import android.os.RemoteException;
23import android.telecom.CallAudioState;
24import android.telecom.DisconnectCause;
25import android.telecom.VideoProfile;
26import android.test.suitebuilder.annotation.LargeTest;
27import android.test.suitebuilder.annotation.MediumTest;
28
29import com.android.server.telecom.CallAudioModeStateMachine;
30import com.android.server.telecom.CallAudioRouteStateMachine;
31
32import java.util.List;
33
34import static org.mockito.Mockito.atLeastOnce;
35import static org.mockito.Mockito.verify;
36
37/**
38 * System tests for video-specific behavior in telecom.
39 * TODO: Add unit tests which ensure that auto-speakerphone does not occur when using a wired
40 * headset or a bluetooth headset.
41 */
42public class VideoCallTests extends TelecomSystemTest {
43
44    /**
45     * Tests to ensure an incoming video-call is automatically routed to the speakerphone when
46     * the call is answered and neither a wired headset nor bluetooth headset are connected.
47     */
48    @MediumTest
49    public void testAutoSpeakerphoneIncomingBidirectional() throws Exception {
50        // Start an incoming video call.
51        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
52                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
53                VideoProfile.STATE_BIDIRECTIONAL);
54
55        verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
56    }
57
58    /**
59     * Tests to ensure an incoming receive-only video-call is answered in speakerphone mode.  Note
60     * that this is not a scenario we would expect normally with the default dialer as it will
61     * always answer incoming video calls as bi-directional.  It is, however, possible for a third
62     * party dialer to answer an incoming video call a a one-way video call.
63     */
64    @MediumTest
65    public void testAutoSpeakerphoneIncomingReceiveOnly() throws Exception {
66        // Start an incoming video call.
67        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
68                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
69                VideoProfile.STATE_RX_ENABLED);
70
71        verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
72    }
73
74    /**
75     * Tests audio routing for an outgoing video call made with bidirectional video.  Expect to be
76     * in speaker mode.
77     */
78    @MediumTest
79    public void testAutoSpeakerphoneOutgoingBidirectional() throws Exception {
80        // Start an incoming video call.
81        IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
82                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
83                VideoProfile.STATE_BIDIRECTIONAL);
84
85        verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
86    }
87
88    /**
89     * Tests audio routing for an outgoing video call made with transmit only video.  Expect to be
90     * in speaker mode.  Note: The default UI does not support making one-way video calls, but the
91     * APIs do and a third party incall UI could choose to support that.
92     */
93    @MediumTest
94    public void testAutoSpeakerphoneOutgoingTransmitOnly() throws Exception {
95        // Start an incoming video call.
96        IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
97                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
98                VideoProfile.STATE_TX_ENABLED);
99
100        verifyAudioRoute(CallAudioState.ROUTE_SPEAKER);
101    }
102
103    /**
104     * Tests audio routing for an outgoing video call made with transmit only video.  Expect to be
105     * in speaker mode.  Note: The default UI does not support making one-way video calls, but the
106     * APIs do and a third party incall UI could choose to support that.
107     */
108    @MediumTest
109    public void testNoAutoSpeakerphoneOnOutgoing() throws Exception {
110        // Start an incoming video call.
111        IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
112                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
113                VideoProfile.STATE_AUDIO_ONLY);
114
115        verifyAudioRoute(CallAudioState.ROUTE_EARPIECE);
116    }
117
118    /**
119     * Tests to ensure an incoming audio-only call is routed to the earpiece.
120     */
121    @MediumTest
122    public void testNoAutoSpeakerphoneOnIncoming() throws Exception {
123
124        // Start an incoming video call.
125        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
126                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
127                VideoProfile.STATE_AUDIO_ONLY);
128
129        verifyAudioRoute(CallAudioState.ROUTE_EARPIECE);
130    }
131
132    /**
133     * Ensure that when an incoming video call is missed, the video state history still includes
134     * video calling. This is important for the call log.
135     */
136    @LargeTest
137    public void testIncomingVideoCallMissedCheckVideoHistory() throws Exception {
138        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
139                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
140        com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
141                .iterator().next();
142
143        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.MISSED);
144
145        assertTrue(VideoProfile.isVideo(call.getVideoStateHistory()));
146    }
147
148    /**
149     * Ensure that when an incoming video call is rejected, the video state history still includes
150     * video calling. This is important for the call log.
151     */
152    @LargeTest
153    public void testIncomingVideoCallRejectedCheckVideoHistory() throws Exception {
154        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
155                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
156        com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
157                .iterator().next();
158
159        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.REJECTED);
160
161        assertTrue(VideoProfile.isVideo(call.getVideoStateHistory()));
162    }
163
164
165    /**
166     * Ensure that when an outgoing video call is canceled, the video state history still includes
167     * video calling. This is important for the call log.
168     */
169    @LargeTest
170    public void testOutgoingVideoCallCanceledCheckVideoHistory() throws Exception {
171        IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
172                mConnectionServiceFixtureA, Process.myUserHandle(),
173                VideoProfile.STATE_BIDIRECTIONAL);
174        com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
175                .iterator().next();
176
177        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
178
179        assertTrue(VideoProfile.isVideo(call.getVideoStateHistory()));
180    }
181
182    /**
183     * Ensure that when an outgoing video call is rejected, the video state history still includes
184     * video calling. This is important for the call log.
185     */
186    @LargeTest
187    public void testOutgoingVideoCallRejectedCheckVideoHistory() throws Exception {
188        IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
189                mConnectionServiceFixtureA, Process.myUserHandle(),
190                VideoProfile.STATE_BIDIRECTIONAL);
191        com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
192                .iterator().next();
193
194        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.REMOTE);
195
196        assertTrue(VideoProfile.isVideo(call.getVideoStateHistory()));
197    }
198
199    /**
200     * Ensure that when an outgoing video call is answered as audio only, the video state history
201     * shows that the call was audio only. This is important for the call log.
202     */
203    @LargeTest
204    public void testOutgoingVideoCallAnsweredAsAudio() throws Exception {
205        IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
206                mConnectionServiceFixtureA, Process.myUserHandle(),
207                VideoProfile.STATE_BIDIRECTIONAL);
208        com.android.server.telecom.Call call = mTelecomSystem.getCallsManager().getCalls()
209                .iterator().next();
210
211        mConnectionServiceFixtureA.mConnectionById.get(ids.mConnectionId).videoState
212                = VideoProfile.STATE_AUDIO_ONLY;
213        mConnectionServiceFixtureA.sendSetVideoState(ids.mConnectionId);
214        mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
215
216        assertFalse(VideoProfile.isVideo(call.getVideoStateHistory()));
217    }
218
219    /**
220     * Verifies that the
221     * {@link android.telecom.InCallService#onCallAudioStateChanged(CallAudioState)} change is
222     * called with an expected route and number of changes.
223     *
224     * @param expectedRoute The expected audio route on the latest change.
225     */
226    private void verifyAudioRoute(int expectedRoute) throws Exception {
227        // Capture all onCallAudioStateChanged callbacks to InCall.
228        CallAudioRouteStateMachine carsm = mTelecomSystem.getCallsManager()
229                .getCallAudioManager().getCallAudioRouteStateMachine();
230        CallAudioModeStateMachine camsm = mTelecomSystem.getCallsManager()
231                .getCallAudioManager().getCallAudioModeStateMachine();
232        waitForHandlerAction(camsm.getHandler(), TEST_TIMEOUT);
233        final boolean[] success = {true};
234        carsm.sendMessage(CallAudioRouteStateMachine.RUN_RUNNABLE, (Runnable) () -> {
235            ArgumentCaptor<CallAudioState> callAudioStateArgumentCaptor = ArgumentCaptor.forClass(
236                    CallAudioState.class);
237            try {
238                verify(mInCallServiceFixtureX.getTestDouble(), atLeastOnce())
239                        .onCallAudioStateChanged(callAudioStateArgumentCaptor.capture());
240            } catch (RemoteException e) {
241                fail("Remote exception in InCallServiceFixture");
242            }
243            List<CallAudioState> changes = callAudioStateArgumentCaptor.getAllValues();
244            assertEquals(expectedRoute, changes.get(changes.size() - 1).getRoute());
245            success[0] = true;
246        });
247        waitForHandlerAction(carsm.getHandler(), TEST_TIMEOUT);
248        assertTrue(success[0]);
249    }
250}
251