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