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