VideoProviderTest.java revision f1dcfdb99763740e9fd1ab036f6c06a312f197ad
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;
20import org.mockito.Mock;
21import org.mockito.Mockito;
22import org.mockito.internal.exceptions.ExceptionIncludingMockitoWarnings;
23import org.mockito.invocation.InvocationOnMock;
24import org.mockito.stubbing.Answer;
25
26import android.app.AppOpsManager;
27import android.content.Context;
28import android.graphics.SurfaceTexture;
29import android.net.Uri;
30import android.os.Handler;
31import android.os.Looper;
32import android.telecom.Connection.VideoProvider;
33import android.telecom.InCallService;
34import android.telecom.InCallService.VideoCall;
35import android.telecom.VideoCallImpl;
36import android.telecom.VideoProfile;
37import android.telecom.VideoProfile.CameraCapabilities;
38import android.test.suitebuilder.annotation.MediumTest;
39import android.view.Surface;
40
41import com.google.common.base.Predicate;
42
43import java.util.List;
44import java.util.concurrent.CountDownLatch;
45import java.util.concurrent.TimeUnit;
46
47import static android.test.MoreAsserts.assertEquals;
48import static org.mockito.Matchers.any;
49import static org.mockito.Matchers.anyInt;
50import static org.mockito.Matchers.anyLong;
51import static org.mockito.Matchers.anyString;
52import static org.mockito.Matchers.eq;
53import static org.mockito.Mockito.doAnswer;
54import static org.mockito.Mockito.doNothing;
55import static org.mockito.Mockito.doReturn;
56import static org.mockito.Mockito.doThrow;
57import static org.mockito.Mockito.times;
58import static org.mockito.Mockito.timeout;
59import static org.mockito.Mockito.mock;
60import static org.mockito.Mockito.verify;
61import static org.mockito.Mockito.when;
62
63/**
64 * Performs tests of the {@link VideoProvider} and {@link VideoCall} APIs.  Ensures that requests
65 * sent from an InCallService are routed through Telecom to a VideoProvider, and that callbacks are
66 * correctly routed.
67 */
68public class VideoProviderTest extends TelecomSystemTest {
69    private static final int ORIENTATION_0 = 0;
70    private static final int ORIENTATION_90 = 90;
71    private static final float ZOOM_LEVEL = 3.0f;
72
73    @Mock private VideoCall.Callback mVideoCallCallback;
74    private IdPair mCallIds;
75    private InCallService.VideoCall mVideoCall;
76    private VideoCallImpl mVideoCallImpl;
77    private ConnectionServiceFixture.ConnectionInfo mConnectionInfo;
78    private CountDownLatch mVerificationLock;
79    private AppOpsManager mAppOpsManager;
80
81    private Answer mVerification = new Answer() {
82        @Override
83        public Object answer(InvocationOnMock i) {
84            mVerificationLock.countDown();
85            return null;
86        }
87    };
88
89    @Override
90    public void setUp() throws Exception {
91        super.setUp();
92        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
93        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
94
95        mCallIds = startAndMakeActiveOutgoingCall(
96                "650-555-1212",
97                mPhoneAccountA0.getAccountHandle(),
98                mConnectionServiceFixtureA);
99
100        // Set the video provider on the connection.
101        mConnectionServiceFixtureA.sendSetVideoProvider(
102                mConnectionServiceFixtureA.mLatestConnectionId);
103
104        // Provide a mocked VideoCall.Callback to receive callbacks via.
105        mVideoCallCallback = mock(InCallService.VideoCall.Callback.class);
106
107        mVideoCall = mInCallServiceFixtureX.getCall(mCallIds.mCallId).getVideoCallImpl(
108                mInCallServiceComponentNameX.getPackageName());
109        mVideoCallImpl = (VideoCallImpl) mVideoCall;
110        mVideoCall.registerCallback(mVideoCallCallback);
111
112        mConnectionInfo = mConnectionServiceFixtureA.mConnectionById.get(mCallIds.mConnectionId);
113        mVerificationLock = new CountDownLatch(1);
114        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
115
116        doNothing().when(mContext).enforcePermission(anyString(), anyInt(), anyInt(), anyString());
117        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).noteOp(anyInt(), anyInt(),
118                anyString());
119    }
120
121    @Override
122    public void tearDown() throws Exception {
123        super.tearDown();
124    }
125
126    /**
127     * Tests the {@link VideoCall#setCamera(String)}, {@link VideoProvider#onSetCamera(String)},
128     * and {@link VideoCall.Callback#onCameraCapabilitiesChanged(CameraCapabilities)}
129     * APIS.
130     */
131    @MediumTest
132    public void testCameraChange() throws Exception {
133        // Wait until the callback has been received before performing verification.
134        doAnswer(mVerification).when(mVideoCallCallback)
135                .onCameraCapabilitiesChanged(any(CameraCapabilities.class));
136
137        // Make 2 setCamera requests.
138        mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
139        mVideoCall.setCamera(MockVideoProvider.CAMERA_BACK);
140
141        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
142
143        // Capture the video profile reported via the callback.
144        ArgumentCaptor<CameraCapabilities> cameraCapabilitiesCaptor =
145                ArgumentCaptor.forClass(CameraCapabilities.class);
146
147        // Verify that the callback was called twice and capture the callback arguments.
148        verify(mVideoCallCallback, timeout(TEST_TIMEOUT).times(2))
149                .onCameraCapabilitiesChanged(cameraCapabilitiesCaptor.capture());
150
151        assertEquals(2, cameraCapabilitiesCaptor.getAllValues().size());
152
153        List<CameraCapabilities> cameraCapabilities = cameraCapabilitiesCaptor.getAllValues();
154        // Ensure dimensions are as expected.
155        assertEquals(MockVideoProvider.CAMERA_FRONT_DIMENSIONS,
156                cameraCapabilities.get(0).getHeight());
157        assertEquals(MockVideoProvider.CAMERA_BACK_DIMENSIONS,
158                cameraCapabilities.get(1).getHeight());
159    }
160
161    /**
162     * Tests the caller permission check in {@link VideoCall#setCamera(String)} to ensure a camera
163     * change from a non-permitted caller is ignored.
164     */
165    @MediumTest
166    public void testCameraChangePermissionFail() throws Exception {
167        // Wait until the callback has been received before performing verification.
168        doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
169
170        // ensure permission check fails.
171        doThrow(new SecurityException()).when(mContext)
172                .enforcePermission(anyString(), anyInt(), anyInt(), anyString());
173
174        // Make a request to change the camera
175        mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
176        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
177
178        // Capture the session event reported via the callback.
179        ArgumentCaptor<Integer> sessionEventCaptor = ArgumentCaptor.forClass(Integer.class);
180        verify(mVideoCallCallback, timeout(TEST_TIMEOUT)).onCallSessionEvent(
181                sessionEventCaptor.capture());
182
183        assertEquals(VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR,
184                sessionEventCaptor.getValue().intValue());
185    }
186
187    /**
188     * Tests the caller app ops check in {@link VideoCall#setCamera(String)} to ensure a camera
189     * change from a non-permitted caller is ignored.
190     */
191    @MediumTest
192    public void testCameraChangeAppOpsFail() throws Exception {
193        // Wait until the callback has been received before performing verification.
194        doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
195
196        // ensure app ops check fails.
197        doReturn(AppOpsManager.MODE_ERRORED).when(mAppOpsManager).noteOp(anyInt(), anyInt(),
198                anyString());
199
200        // Make a request to change the camera
201        mVideoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
202        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
203
204        // Capture the session event reported via the callback.
205        ArgumentCaptor<Integer> sessionEventCaptor = ArgumentCaptor.forClass(Integer.class);
206        verify(mVideoCallCallback, timeout(TEST_TIMEOUT)).onCallSessionEvent(
207                sessionEventCaptor.capture());
208
209        assertEquals(VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR,
210                sessionEventCaptor.getValue().intValue());
211    }
212
213    /**
214     * Tests the caller permission check in {@link VideoCall#setCamera(String)} to ensure the
215     * caller can null out the camera, even if they do not have camera permission.
216     */
217    @MediumTest
218    public void testCameraChangeNullNoPermission() throws Exception {
219        // Wait until the callback has been received before performing verification.
220        doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
221
222        // ensure permission check fails.
223        doThrow(new SecurityException()).when(mContext)
224                .enforcePermission(anyString(), anyInt(), anyInt(), anyString());
225
226        // Make a request to null the camera; we expect the permission check won't happen.
227        mVideoCall.setCamera(null);
228        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
229
230        // Capture the session event reported via the callback.
231        ArgumentCaptor<Integer> sessionEventCaptor = ArgumentCaptor.forClass(Integer.class);
232        verify(mVideoCallCallback, timeout(TEST_TIMEOUT)).onCallSessionEvent(
233                sessionEventCaptor.capture());
234
235        // See the MockVideoProvider class; for convenience when the camera is nulled we just send
236        // back a "camera ready" event.
237        assertEquals(VideoProvider.SESSION_EVENT_CAMERA_READY,
238                sessionEventCaptor.getValue().intValue());
239    }
240
241    /**
242     * Tests the {@link VideoCall#setPreviewSurface(Surface)} and
243     * {@link VideoProvider#onSetPreviewSurface(Surface)} APIs.
244     */
245    @MediumTest
246    public void testSetPreviewSurface() throws Exception {
247        final Surface surface = new Surface(new SurfaceTexture(1));
248        mVideoCall.setPreviewSurface(surface);
249
250        assertTrueWithTimeout(new Predicate<Void>() {
251            @Override
252            public boolean apply(Void v) {
253                return mConnectionInfo.mockVideoProvider.getPreviewSurface() == surface;
254            }
255        });
256
257        mVideoCall.setPreviewSurface(null);
258
259        assertTrueWithTimeout(new Predicate<Void>() {
260            @Override
261            public boolean apply(Void v) {
262                return mConnectionInfo.mockVideoProvider.getPreviewSurface() == null;
263            }
264        });
265    }
266
267    /**
268     * Tests the {@link VideoCall#setDisplaySurface(Surface)} and
269     * {@link VideoProvider#onSetDisplaySurface(Surface)} APIs.
270     */
271    @MediumTest
272    public void testSetDisplaySurface() throws Exception {
273        final Surface surface = new Surface(new SurfaceTexture(1));
274        mVideoCall.setDisplaySurface(surface);
275
276        assertTrueWithTimeout(new Predicate<Void>() {
277            @Override
278            public boolean apply(Void v) {
279                return mConnectionInfo.mockVideoProvider.getDisplaySurface() == surface;
280            }
281        });
282
283        mVideoCall.setDisplaySurface(null);
284
285        assertTrueWithTimeout(new Predicate<Void>() {
286            @Override
287            public boolean apply(Void v) {
288                return mConnectionInfo.mockVideoProvider.getDisplaySurface() == null;
289            }
290        });
291    }
292
293    /**
294     * Tests the {@link VideoCall#setDeviceOrientation(int)} and
295     * {@link VideoProvider#onSetDeviceOrientation(int)} APIs.
296     */
297    @MediumTest
298    public void testSetDeviceOrientation() throws Exception {
299        mVideoCall.setDeviceOrientation(ORIENTATION_0);
300
301        assertTrueWithTimeout(new Predicate<Void>() {
302            @Override
303            public boolean apply(Void v) {
304                return mConnectionInfo.mockVideoProvider.getDeviceOrientation() == ORIENTATION_0;
305            }
306        });
307
308        mVideoCall.setDeviceOrientation(ORIENTATION_90);
309
310        assertTrueWithTimeout(new Predicate<Void>() {
311            @Override
312            public boolean apply(Void v) {
313                return mConnectionInfo.mockVideoProvider.getDeviceOrientation() == ORIENTATION_90;
314            }
315        });
316    }
317
318    /**
319     * Tests the {@link VideoCall#setZoom(float)} and {@link VideoProvider#onSetZoom(float)} APIs.
320     */
321    @MediumTest
322    public void testSetZoom() throws Exception {
323        mVideoCall.setZoom(ZOOM_LEVEL);
324
325        assertTrueWithTimeout(new Predicate<Void>() {
326            @Override
327            public boolean apply(Void v) {
328                return mConnectionInfo.mockVideoProvider.getZoom() == ZOOM_LEVEL;
329            }
330        });
331    }
332
333    /**
334     * Tests the {@link VideoCall#sendSessionModifyRequest(VideoProfile)},
335     * {@link VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)},
336     * {@link VideoProvider#receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}, and
337     * {@link VideoCall.Callback#onSessionModifyResponseReceived(int, VideoProfile, VideoProfile)}
338     * APIs.
339     *
340     * Emulates a scenario where an InCallService sends a request to upgrade to video, which the
341     * peer accepts as-is.
342     */
343    @MediumTest
344    public void testSessionModifyRequest() throws Exception {
345        VideoProfile requestProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
346
347        // Set the starting video state on the video call impl; normally this would be set based on
348        // the original android.telecom.Call instance.
349        mVideoCallImpl.setVideoState(VideoProfile.STATE_RX_ENABLED);
350
351        doAnswer(mVerification).when(mVideoCallCallback)
352                .onSessionModifyResponseReceived(anyInt(), any(VideoProfile.class),
353                        any(VideoProfile.class));
354
355        // Send the request.
356        mVideoCall.sendSessionModifyRequest(requestProfile);
357
358        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
359
360        // Capture the video profiles from the callback.
361        ArgumentCaptor<VideoProfile> fromVideoProfileCaptor =
362                ArgumentCaptor.forClass(VideoProfile.class);
363        ArgumentCaptor<VideoProfile> toVideoProfileCaptor =
364                ArgumentCaptor.forClass(VideoProfile.class);
365
366        // Verify we got a response and capture the profiles.
367        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
368                .onSessionModifyResponseReceived(eq(VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS),
369                        fromVideoProfileCaptor.capture(), toVideoProfileCaptor.capture());
370
371        assertEquals(VideoProfile.STATE_RX_ENABLED,
372                fromVideoProfileCaptor.getValue().getVideoState());
373        assertEquals(VideoProfile.STATE_BIDIRECTIONAL,
374                toVideoProfileCaptor.getValue().getVideoState());
375    }
376
377    /**
378     * Tests the {@link VideoCall#sendSessionModifyResponse(VideoProfile)},
379     * and {@link VideoProvider#onSendSessionModifyResponse(VideoProfile)} APIs.
380     */
381    @MediumTest
382    public void testSessionModifyResponse() throws Exception {
383        VideoProfile sessionModifyResponse = new VideoProfile(VideoProfile.STATE_TX_ENABLED);
384
385        mVideoCall.sendSessionModifyResponse(sessionModifyResponse);
386
387        assertTrueWithTimeout(new Predicate<Void>() {
388            @Override
389            public boolean apply(Void v) {
390                VideoProfile response = mConnectionInfo.mockVideoProvider
391                        .getSessionModifyResponse();
392                return response != null && response.getVideoState() == VideoProfile.STATE_TX_ENABLED;
393            }
394        });
395    }
396
397    /**
398     * Tests the {@link VideoCall#requestCameraCapabilities()} ()},
399     * {@link VideoProvider#onRequestCameraCapabilities()} ()}, and
400     * {@link VideoCall.Callback#onCameraCapabilitiesChanged(CameraCapabilities)} APIs.
401     */
402    @MediumTest
403    public void testRequestCameraCapabilities() throws Exception {
404        // Wait until the callback has been received before performing verification.
405        doAnswer(mVerification).when(mVideoCallCallback)
406                .onCameraCapabilitiesChanged(any(CameraCapabilities.class));
407
408        mVideoCall.requestCameraCapabilities();
409
410        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
411
412        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
413                .onCameraCapabilitiesChanged(any(CameraCapabilities.class));
414    }
415
416    /**
417     * Tests the {@link VideoCall#setPauseImage(Uri)}, and
418     * {@link VideoProvider#onSetPauseImage(Uri)} APIs.
419     */
420    @MediumTest
421    public void testSetPauseImage() throws Exception {
422        final Uri testUri = Uri.fromParts("file", "test.jpg", null);
423        mVideoCall.setPauseImage(testUri);
424
425        assertTrueWithTimeout(new Predicate<Void>() {
426            @Override
427            public boolean apply(Void v) {
428                Uri pauseImage = mConnectionInfo.mockVideoProvider.getPauseImage();
429                return pauseImage != null && pauseImage.equals(testUri);
430            }
431        });
432    }
433
434    /**
435     * Tests the {@link VideoCall#requestCallDataUsage()},
436     * {@link VideoProvider#onRequestConnectionDataUsage()}, and
437     * {@link VideoCall.Callback#onCallDataUsageChanged(long)} APIs.
438     */
439    @MediumTest
440    public void testRequestDataUsage() throws Exception {
441        // Wait until the callback has been received before performing verification.
442        doAnswer(mVerification).when(mVideoCallCallback)
443                .onCallDataUsageChanged(anyLong());
444
445        mVideoCall.requestCallDataUsage();
446
447        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
448
449        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
450                .onCallDataUsageChanged(eq(MockVideoProvider.DATA_USAGE));
451    }
452
453    /**
454     * Tests the {@link VideoProvider#receiveSessionModifyRequest(VideoProfile)},
455     * {@link VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)} APIs.
456     */
457    @MediumTest
458    public void testReceiveSessionModifyRequest() throws Exception {
459        // Wait until the callback has been received before performing verification.
460        doAnswer(mVerification).when(mVideoCallCallback)
461                .onSessionModifyRequestReceived(any(VideoProfile.class));
462
463        mConnectionInfo.mockVideoProvider.sendMockSessionModifyRequest();
464
465        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
466
467        ArgumentCaptor<VideoProfile> requestProfileCaptor =
468                ArgumentCaptor.forClass(VideoProfile.class);
469        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
470                .onSessionModifyRequestReceived(requestProfileCaptor.capture());
471        assertEquals(VideoProfile.STATE_BIDIRECTIONAL,
472                requestProfileCaptor.getValue().getVideoState());
473    }
474
475
476    /**
477     * Tests the {@link VideoProvider#handleCallSessionEvent(int)}, and
478     * {@link VideoCall.Callback#onCallSessionEvent(int)} APIs.
479     */
480    @MediumTest
481    public void testSessionEvent() throws Exception {
482        // Wait until the callback has been received before performing verification.
483        doAnswer(mVerification).when(mVideoCallCallback)
484                .onCallSessionEvent(anyInt());
485
486        mConnectionInfo.mockVideoProvider.sendMockSessionEvent(
487                VideoProvider.SESSION_EVENT_CAMERA_READY);
488
489        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
490
491        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
492                .onCallSessionEvent(eq(VideoProvider.SESSION_EVENT_CAMERA_READY));
493    }
494
495    /**
496     * Tests the {@link VideoProvider#changePeerDimensions(int, int)} and
497     * {@link VideoCall.Callback#onPeerDimensionsChanged(int, int)} APIs.
498     */
499    @MediumTest
500    public void testPeerDimensionChange() throws Exception {
501        // Wait until the callback has been received before performing verification.
502        doAnswer(mVerification).when(mVideoCallCallback)
503                .onPeerDimensionsChanged(anyInt(), anyInt());
504
505        mConnectionInfo.mockVideoProvider.sendMockPeerDimensions(MockVideoProvider.PEER_DIMENSIONS,
506                MockVideoProvider.PEER_DIMENSIONS);
507
508        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
509
510        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
511                .onPeerDimensionsChanged(eq(MockVideoProvider.PEER_DIMENSIONS),
512                        eq(MockVideoProvider.PEER_DIMENSIONS));
513    }
514
515    /**
516     * Tests the {@link VideoProvider#changeVideoQuality(int)} and
517     * {@link VideoCall.Callback#onVideoQualityChanged(int)} APIs.
518     */
519    @MediumTest
520    public void testVideoQualityChange() throws Exception {
521        // Wait until the callback has been received before performing verification.
522        doAnswer(mVerification).when(mVideoCallCallback)
523                .onVideoQualityChanged(anyInt());
524
525        mConnectionInfo.mockVideoProvider.sendMockVideoQuality(VideoProfile.QUALITY_HIGH);
526
527        mVerificationLock.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
528
529        verify(mVideoCallCallback, timeout(TEST_TIMEOUT))
530                .onVideoQualityChanged(eq(VideoProfile.QUALITY_HIGH));
531    }
532}
533