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