1 /**
2  * Copyright (C) 2022 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 
17 package com.android.telephony.imsmedia;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.fail;
22 import static org.mockito.Mockito.eq;
23 import static org.mockito.Mockito.times;
24 import static org.mockito.Mockito.verify;
25 
26 import android.graphics.ImageFormat;
27 import android.media.ImageReader;
28 import android.os.Looper;
29 import android.os.Parcel;
30 import android.os.ParcelFileDescriptor;
31 import android.os.RemoteException;
32 import android.telephony.ims.RtpHeaderExtension;
33 import android.telephony.imsmedia.IImsVideoSessionCallback;
34 import android.telephony.imsmedia.ImsMediaSession;
35 import android.telephony.imsmedia.MediaQualityThreshold;
36 import android.telephony.imsmedia.VideoConfig;
37 import android.testing.AndroidTestingRunner;
38 import android.testing.TestableLooper;
39 import android.view.Surface;
40 
41 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
42 
43 import org.junit.After;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 
50 import java.net.DatagramSocket;
51 import java.net.SocketException;
52 import java.util.ArrayList;
53 
54 @RunWith(AndroidTestingRunner.class)
55 @TestableLooper.RunWithLooper
56 public class VideoSessionTest extends ImsMediaTest {
57     private static final int SESSION_ID = 1;
58     private static final int UNUSED = -1;
59     private static final int SUCCESS = ImsMediaSession.RESULT_SUCCESS;
60     private static final int NO_RESOURCES = ImsMediaSession.RESULT_NO_RESOURCES;
61     private static final int RTP = ImsMediaSession.PACKET_TYPE_RTP;
62     private static final int RTCP = ImsMediaSession.PACKET_TYPE_RTCP;
63     private static final int RESOLUTION_WIDTH = 640;
64     private static final int RESOLUTION_HEIGHT = 480;
65     private static final long VIDEO_DATA = 1024;
66     private static final int PACKET_LOSS = 15;
67     private static final Surface PREVIEW_SURFACE = ImageReader.newInstance(
68             RESOLUTION_WIDTH, RESOLUTION_HEIGHT, ImageFormat.JPEG, 1).getSurface();
69     private static final Surface DISPLAY_SURFACE = ImageReader.newInstance(
70             RESOLUTION_WIDTH, RESOLUTION_HEIGHT, ImageFormat.JPEG, 1).getSurface();
71     private VideoSession mVideoSession;
72     private VideoSession.VideoSessionHandler mHandler;
73     private WakeLockManager mWakeLockManager;
74     @Mock
75     private VideoService mVideoService;
76     private VideoListener mVideoListener;
77     @Mock
78     private VideoLocalSession mVideoLocalSession;
79     @Mock
80     private IImsVideoSessionCallback mCallback;
81     private TestableLooper mLooper;
82 
83     @Before
setUp()84     public void setUp() {
85         MockitoAnnotations.initMocks(this);
86         mVideoSession = new VideoSession(SESSION_ID, mCallback,
87                 mVideoService, mVideoLocalSession, Looper.myLooper());
88         mVideoListener = mVideoSession.getVideoListener();
89         mHandler = mVideoSession.getVideoSessionHandler();
90         mTestClass = VideoSessionTest.this;
91         mWakeLockManager = WakeLockManager.getInstance();
92         super.setUp();
93     }
94 
95     @After
tearDown()96     public void tearDown() throws Exception {
97         super.tearDown();
98         mWakeLockManager.cleanup();
99     }
100 
createParcel(int message, int result, VideoConfig config)101     private Parcel createParcel(int message, int result, VideoConfig config) {
102         Parcel parcel = Parcel.obtain();
103         parcel.writeInt(message);
104         parcel.writeInt(result);
105         if (config != null) {
106             config.writeToParcel(parcel, 0);
107         }
108         parcel.setDataPosition(0);
109         return parcel;
110     }
111 
112     @Test
testOpenSession()113     public void testOpenSession() {
114         DatagramSocket rtpSocket = null;
115         DatagramSocket rtcpSocket = null;
116 
117         try {
118             rtpSocket = new DatagramSocket();
119             rtcpSocket = new DatagramSocket();
120         } catch (SocketException e) {
121             fail("SocketException:" + e);
122         }
123 
124         OpenSessionParams params = new OpenSessionParams(
125                 ParcelFileDescriptor.fromDatagramSocket(rtpSocket),
126                 ParcelFileDescriptor.fromDatagramSocket(rtcpSocket),
127                 null, null);
128 
129         mVideoSession.openSession(params);
130         processAllMessages();
131         verify(mVideoService, times(1)).openSession(eq(SESSION_ID), eq(params));
132         assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false);
133     }
134 
135     @Test
testCloseSession()136     public void testCloseSession() {
137         mVideoSession.closeSession();
138         processAllMessages();
139         verify(mVideoService, times(1)).closeSession(eq(SESSION_ID));
140         assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false);
141     }
142 
143     @Test
testModifySession()144     public void testModifySession() {
145         // Modify Session Request
146         VideoConfig config = VideoConfigTest.createVideoConfig();
147         mVideoSession.modifySession(config);
148         processAllMessages();
149         verify(mVideoLocalSession, times(1)).modifySession(eq(config));
150         assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(true);
151 
152         // Modify Session Response - Success
153         mVideoListener.onMessage(
154                 createParcel(VideoSession.EVENT_MODIFY_SESSION_RESPONSE, SUCCESS, config));
155         processAllMessages();
156         try {
157             verify(mCallback, times(1)).onModifySessionResponse(eq(config), eq(SUCCESS));
158         } catch (RemoteException e) {
159             fail("Failed to notify modify session response: " + e);
160         }
161 
162         // Modify Session Response - Failure (NO_RESOURCES)
163         mVideoListener.onMessage(
164                 createParcel(VideoSession.EVENT_MODIFY_SESSION_RESPONSE, NO_RESOURCES, config));
165         processAllMessages();
166         try {
167             verify(mCallback, times(1)).onModifySessionResponse(eq(config), eq(NO_RESOURCES));
168         } catch (RemoteException e) {
169             fail("Failed to notify modify session response: " + e);
170         }
171     }
172 
173     @Test
testSurfaces()174     public void testSurfaces() {
175         Utils.sendMessage(mHandler, VideoSession.CMD_SET_PREVIEW_SURFACE, PREVIEW_SURFACE);
176         processAllMessages();
177         verify(mVideoLocalSession, times(1)).setPreviewSurface(eq(PREVIEW_SURFACE));
178 
179         Utils.sendMessage(mHandler, VideoSession.CMD_SET_DISPLAY_SURFACE, DISPLAY_SURFACE);
180         processAllMessages();
181         verify(mVideoLocalSession, times(1)).setDisplaySurface(eq(DISPLAY_SURFACE));
182     }
183 
184     @Test
testSetMediaQualityThreshold()185     public void testSetMediaQualityThreshold() {
186         // Set Media Quality Threshold
187         MediaQualityThreshold threshold = MediaQualityThresholdTest.createMediaQualityThreshold();
188         mVideoSession.setMediaQualityThreshold(threshold);
189         processAllMessages();
190         verify(mVideoLocalSession, times(1)).setMediaQualityThreshold(eq(threshold));
191     }
192 
193     @Test
testRequestVideoDataUsage()194     public void testRequestVideoDataUsage() {
195         mVideoSession.requestVideoDataUsage();
196         processAllMessages();
197         verify(mVideoLocalSession, times(1)).requestVideoDataUsage();
198     }
199 
200     @Test
testMediaInactivityInd()201     public void testMediaInactivityInd() {
202         // Receive Inactivity - RTP
203         Parcel parcel = Parcel.obtain();
204         parcel.writeInt(VideoSession.EVENT_MEDIA_INACTIVITY_IND);
205         parcel.writeInt(RTP);
206         parcel.setDataPosition(0);
207         mVideoListener.onMessage(parcel);
208         processAllMessages();
209         try {
210             verify(mCallback, times(1)).notifyMediaInactivity(eq(RTP));
211         } catch (RemoteException e) {
212             fail("Failed to notify media inactivity: " + e);
213         }
214 
215         // Receive Inactivity - RTCP
216         Parcel parcel2 = Parcel.obtain();
217         parcel2.writeInt(VideoSession.EVENT_MEDIA_INACTIVITY_IND);
218         parcel2.writeInt(RTCP);
219         parcel2.setDataPosition(0);
220         mVideoListener.onMessage(parcel2);
221         processAllMessages();
222         try {
223             verify(mCallback, times(1)).notifyMediaInactivity(eq(RTCP));
224         } catch (RemoteException e) {
225             fail("Failed to notify media inactivity: " + e);
226         }
227     }
228 
229     @Test
testPeerDimensionChanged()230     public void testPeerDimensionChanged() {
231         // received video frame resolution changed
232         Parcel parcel = Parcel.obtain();
233         parcel.writeInt(VideoSession.EVENT_PEER_DIMENSION_CHANGED);
234         parcel.writeInt(RESOLUTION_WIDTH);
235         parcel.writeInt(RESOLUTION_HEIGHT);
236         parcel.setDataPosition(0);
237         mVideoListener.onMessage(parcel);
238         processAllMessages();
239         try {
240             verify(mCallback, times(1)).onPeerDimensionChanged(
241                     eq(RESOLUTION_WIDTH), eq(RESOLUTION_HEIGHT));
242         } catch (RemoteException e) {
243             fail("Failed to notify peer dimension changed: " + e);
244         }
245     }
246 
247     @Test
testNotifyVideoDataUsage()248     public void testNotifyVideoDataUsage() {
249         Parcel parcel = Parcel.obtain();
250         parcel.writeInt(VideoSession.EVENT_VIDEO_DATA_USAGE_IND);
251         parcel.writeLong(VIDEO_DATA);
252         parcel.setDataPosition(0);
253         mVideoListener.onMessage(parcel);
254         processAllMessages();
255         try {
256             verify(mCallback, times(1)).notifyVideoDataUsage(eq(VIDEO_DATA));
257         } catch (RemoteException e) {
258             fail("Failed to notify video data usage: " + e);
259         }
260     }
261 
262     @Test
testFirstMediaPacketReceivedInd()263     public void testFirstMediaPacketReceivedInd() {
264         // Receive First MediaPacket Received Indication
265         VideoConfig config = VideoConfigTest.createVideoConfig();
266         Utils.sendMessage(mHandler, VideoSession.EVENT_FIRST_MEDIA_PACKET_IND, config);
267         processAllMessages();
268         try {
269             verify(mCallback, times(1)).onFirstMediaPacketReceived(eq(config));
270         } catch (RemoteException e) {
271             fail("Failed to notify first media packet received: " + e);
272         }
273     }
274 
275     @Test
testHeaderExtension()276     public void testHeaderExtension() {
277         // Send RtpHeaderExtension
278         ArrayList extensions = new ArrayList<RtpHeaderExtension>();
279         mVideoSession.sendHeaderExtension(extensions);
280         processAllMessages();
281         verify(mVideoLocalSession, times(1)).sendHeaderExtension(eq(extensions));
282 
283         // Receive RtpHeaderExtension
284         Utils.sendMessage(mHandler, VideoSession.EVENT_RTP_HEADER_EXTENSION_IND, extensions);
285         processAllMessages();
286         try {
287             verify(mCallback, times(1)).onHeaderExtensionReceived(eq(extensions));
288         } catch (RemoteException e) {
289             fail("Failed to notify header extension received: " + e);
290         }
291     }
292 
293     @Test
testPacketLossInd()294     public void testPacketLossInd() {
295         // Receive Packet Loss
296         Utils.sendMessage(mHandler, VideoSession.EVENT_NOTIFY_BITRATE_IND, PACKET_LOSS, UNUSED);
297         processAllMessages();
298         try {
299             verify(mCallback, times(1)).notifyBitrate(eq(PACKET_LOSS));
300         } catch (RemoteException e) {
301             fail("Failed to notify notifyBitrate: " + e);
302         }
303     }
304 
305     @Test
testOpenSessionSuccess()306     public void testOpenSessionSuccess() {
307         mVideoSession.onOpenSessionSuccess(mVideoLocalSession);
308         processAllMessages();
309         try {
310             verify(mCallback, times(1)).onOpenSessionSuccess(mVideoSession);
311             assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false);
312         } catch (RemoteException e) {
313             fail("Failed to notify onOpenSessionSuccess: " + e);
314         }
315     }
316 
317     @Test
testOpenSessionFailure()318     public void testOpenSessionFailure() {
319         mVideoSession.onOpenSessionFailure(ImsMediaSession.RESULT_INVALID_PARAM);
320         processAllMessages();
321         try {
322             verify(mCallback, times(1)).onOpenSessionFailure(ImsMediaSession.RESULT_INVALID_PARAM);
323             assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false);
324         } catch (RemoteException e) {
325             fail("Failed to notify onOpenSessionFailure: " + e);
326         }
327     }
328 
329     @Test
testSessionClosed()330     public void testSessionClosed() {
331         mVideoSession.onSessionClosed();
332         processAllMessages();
333         try {
334             verify(mCallback, times(1)).onSessionClosed();
335             assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false);
336         } catch (RemoteException e) {
337             fail("Failed to notify onSessionClosed: " + e);
338         }
339     }
340 }
341