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