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.os.Looper; 27 import android.os.Parcel; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.telephony.CallQuality; 31 import android.telephony.ims.RtpHeaderExtension; 32 import android.telephony.imsmedia.AudioConfig; 33 import android.telephony.imsmedia.IImsAudioSessionCallback; 34 import android.telephony.imsmedia.ImsMediaSession; 35 import android.telephony.imsmedia.MediaQualityStatus; 36 import android.telephony.imsmedia.MediaQualityThreshold; 37 import android.telephony.imsmedia.RtpReceptionStats; 38 import android.testing.AndroidTestingRunner; 39 import android.testing.TestableLooper; 40 41 import com.android.telephony.imsmedia.AudioService; 42 import com.android.telephony.imsmedia.AudioSession; 43 import com.android.telephony.imsmedia.Utils; 44 import com.android.telephony.imsmedia.Utils.OpenSessionParams; 45 import com.android.telephony.imsmedia.tests.RtpReceptionStatsTest; 46 47 import org.junit.After; 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.Mock; 52 import org.mockito.MockitoAnnotations; 53 54 import java.net.DatagramSocket; 55 import java.net.SocketException; 56 import java.util.ArrayList; 57 58 @RunWith(AndroidTestingRunner.class) 59 @TestableLooper.RunWithLooper 60 public class AudioSessionTest extends ImsMediaTest { 61 private static final int SESSION_ID = 1; 62 private static final int DTMF_DURATION = 140; 63 private static final int UNUSED = -1; 64 private static final int SUCCESS = ImsMediaSession.RESULT_SUCCESS; 65 private static final int NO_RESOURCES = ImsMediaSession.RESULT_NO_RESOURCES; 66 private static final int PACKET_LOSS = 15; 67 private static final int JITTER = 200; 68 private static final char DTMF_DIGIT = '7'; 69 private static final int RECEPTION_DURATION = 10000; 70 private static final int DELAY_ADJUSTMENT = 100; 71 private AudioSession audioSession; 72 private AudioSession.AudioSessionHandler handler; 73 private WakeLockManager mWakeLockManager; 74 @Mock 75 private AudioService audioService; 76 private AudioListener audioListener; 77 @Mock 78 private AudioLocalSession audioLocalSession; 79 @Mock 80 private IImsAudioSessionCallback callback; 81 82 @Before setUp()83 public void setUp() { 84 MockitoAnnotations.initMocks(this); 85 audioSession = new AudioSession(SESSION_ID, callback, 86 audioService, audioLocalSession, null, Looper.myLooper()); 87 audioListener = audioSession.getAudioListener(); 88 handler = audioSession.getAudioSessionHandler(); 89 mTestClass = AudioSessionTest.this; 90 mWakeLockManager = WakeLockManager.getInstance(); 91 super.setUp(); 92 } 93 94 @After tearDown()95 public void tearDown() throws Exception { 96 super.tearDown(); 97 mWakeLockManager.cleanup(); 98 } 99 createParcel(int message, int result, AudioConfig config)100 private Parcel createParcel(int message, int result, AudioConfig config) { 101 Parcel parcel = Parcel.obtain(); 102 parcel.writeInt(message); 103 parcel.writeInt(result); 104 if (config != null) { 105 config.writeToParcel(parcel, 0); 106 } 107 parcel.setDataPosition(0); 108 return parcel; 109 } 110 111 @Test testOpenSession()112 public void testOpenSession() { 113 DatagramSocket rtpSocket = null; 114 DatagramSocket rtcpSocket = null; 115 116 try { 117 rtpSocket = new DatagramSocket(); 118 rtcpSocket = new DatagramSocket(); 119 } catch (SocketException e) { 120 fail("SocketException:" + e); 121 } 122 123 OpenSessionParams params = new OpenSessionParams( 124 ParcelFileDescriptor.fromDatagramSocket(rtpSocket), 125 ParcelFileDescriptor.fromDatagramSocket(rtcpSocket), 126 null, null); 127 128 audioSession.openSession(params); 129 processAllMessages(); 130 verify(audioService, times(1)).openSession(eq(SESSION_ID), eq(params)); 131 assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false); 132 } 133 134 @Test testCloseSession()135 public void testCloseSession() { 136 audioSession.closeSession(); 137 processAllMessages(); 138 verify(audioService, times(1)).closeSession(eq(SESSION_ID)); 139 assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false); 140 } 141 142 @Test testModifySession()143 public void testModifySession() { 144 // Modify Session Request 145 AudioConfig config = AudioConfigTest.createAudioConfig(); 146 audioSession.modifySession(config); 147 processAllMessages(); 148 verify(audioLocalSession, times(1)).modifySession(eq(config)); 149 assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(true); 150 151 // Modify Session Response - Success 152 audioListener.onMessage( 153 createParcel(AudioSession.EVENT_MODIFY_SESSION_RESPONSE, SUCCESS, config)); 154 processAllMessages(); 155 try { 156 verify(callback, times(1)).onModifySessionResponse(eq(config), eq(SUCCESS)); 157 } catch(RemoteException e) { 158 fail("Failed to notify modifySessionResponse: " + e); 159 } 160 161 // Modify Session Response - Failure (NO_RESOURCES) 162 audioListener.onMessage( 163 createParcel(AudioSession.EVENT_MODIFY_SESSION_RESPONSE, NO_RESOURCES, config)); 164 processAllMessages(); 165 try { 166 verify(callback, times(1)).onModifySessionResponse(eq(config), eq(NO_RESOURCES)); 167 } catch(RemoteException e) { 168 fail("Failed to notify modifySessionResponse: " + e); 169 } 170 } 171 172 @Test testAddConfig()173 public void testAddConfig() { 174 // Add Config Request 175 AudioConfig config = AudioConfigTest.createAudioConfig(); 176 audioSession.addConfig(config); 177 processAllMessages(); 178 verify(audioLocalSession, times(1)).addConfig(eq(config)); 179 180 // Add Config Response - Success 181 audioListener.onMessage( 182 createParcel(AudioSession.EVENT_ADD_CONFIG_RESPONSE, SUCCESS, config)); 183 processAllMessages(); 184 try { 185 verify(callback, times(1)).onAddConfigResponse(eq(config), eq(SUCCESS)); 186 } catch(RemoteException e) { 187 fail("Failed to notify addConfigResponse: " + e); 188 } 189 190 // Add Config Response - Failure (NO_RESOURCES) 191 audioListener.onMessage( 192 createParcel(AudioSession.EVENT_ADD_CONFIG_RESPONSE, NO_RESOURCES, config)); 193 processAllMessages(); 194 try { 195 verify(callback, times(1)).onAddConfigResponse(eq(config), eq(NO_RESOURCES)); 196 } catch(RemoteException e) { 197 fail("Failed to notify addConfigResponse: " + e); 198 } 199 } 200 201 @Test testDeleteConfig()202 public void testDeleteConfig() { 203 // Delete Config Request 204 AudioConfig config = AudioConfigTest.createAudioConfig(); 205 audioSession.deleteConfig(config); 206 processAllMessages(); 207 verify(audioLocalSession, times(1)).deleteConfig(eq(config)); 208 } 209 210 @Test testConfirmConfig()211 public void testConfirmConfig() { 212 // Confirm Config Request 213 AudioConfig config = AudioConfigTest.createAudioConfig(); 214 audioSession.confirmConfig(config); 215 processAllMessages(); 216 verify(audioLocalSession, times(1)).confirmConfig(eq(config)); 217 218 // Confirm Config Response - Success 219 audioListener.onMessage( 220 createParcel(AudioSession.EVENT_CONFIRM_CONFIG_RESPONSE, SUCCESS, config)); 221 processAllMessages(); 222 try { 223 verify(callback, times(1)).onConfirmConfigResponse(eq(config), eq(SUCCESS)); 224 } catch(RemoteException e) { 225 fail("Failed to notify confirmConfigResponse: " + e); 226 } 227 228 // Confirm Config Response - Failure (NO_RESOURCES) 229 audioListener.onMessage( 230 createParcel(AudioSession.EVENT_CONFIRM_CONFIG_RESPONSE, NO_RESOURCES, config)); 231 processAllMessages(); 232 try { 233 verify(callback, times(1)).onConfirmConfigResponse(eq(config), eq(NO_RESOURCES)); 234 } catch(RemoteException e) { 235 fail("Failed to notify confirmConfigResponse: " + e); 236 } 237 } 238 239 @Test testSendDtmf()240 public void testSendDtmf() { 241 audioSession.sendDtmf(DTMF_DIGIT, DTMF_DURATION); 242 processAllMessages(); 243 verify(audioLocalSession, times(1)).sendDtmf(eq(DTMF_DIGIT), eq(DTMF_DURATION)); 244 } 245 246 @Test testStartDtmf()247 public void testStartDtmf() { 248 audioSession.startDtmf(DTMF_DIGIT); 249 processAllMessages(); 250 verify(audioLocalSession, times(1)).sendDtmf(eq(DTMF_DIGIT), eq(DTMF_DURATION)); 251 } 252 253 @Test testSetMediaQualityThreshold()254 public void testSetMediaQualityThreshold() { 255 // Set Media Quality Threshold 256 MediaQualityThreshold threshold = MediaQualityThresholdTest.createMediaQualityThreshold(); 257 audioSession.setMediaQualityThreshold(threshold); 258 processAllMessages(); 259 verify(audioLocalSession, times(1)).setMediaQualityThreshold(eq(threshold)); 260 } 261 262 @Test testRequestRtpReceptionStats()263 public void testRequestRtpReceptionStats() { 264 // Query rtp reception stats 265 audioSession.requestRtpReceptionStats(RECEPTION_DURATION); 266 processAllMessages(); 267 verify(audioLocalSession, times(1)).requestRtpReceptionStats(eq(RECEPTION_DURATION)); 268 } 269 270 @Test testAdjustDelay()271 public void testAdjustDelay() { 272 // Apply delay 273 audioSession.adjustDelay(DELAY_ADJUSTMENT); 274 processAllMessages(); 275 verify(audioLocalSession, times(1)).adjustDelay(eq(DELAY_ADJUSTMENT)); 276 } 277 278 @Test testFirstMediaPacketReceivedInd()279 public void testFirstMediaPacketReceivedInd() { 280 // Receive First MediaPacket Received Indication 281 AudioConfig config = AudioConfigTest.createAudioConfig(); 282 Utils.sendMessage(handler, AudioSession.EVENT_FIRST_MEDIA_PACKET_IND, config); 283 processAllMessages(); 284 try { 285 verify(callback, times(1)).onFirstMediaPacketReceived(eq(config)); 286 } catch(RemoteException e) { 287 fail("Failed to notify onFirstMediaPacketReceived: " + e); 288 } 289 } 290 291 @Test testHeaderExtension()292 public void testHeaderExtension() { 293 // Send RtpHeaderExtension 294 ArrayList extensions = new ArrayList<RtpHeaderExtension>(); 295 audioSession.sendHeaderExtension(extensions); 296 processAllMessages(); 297 verify(audioLocalSession, times(1)).sendHeaderExtension(eq(extensions)); 298 299 // Receive RtpHeaderExtension 300 Utils.sendMessage(handler, AudioSession.EVENT_RTP_HEADER_EXTENSION_IND, extensions); 301 processAllMessages(); 302 try { 303 verify(callback, times(1)).onHeaderExtensionReceived(eq(extensions)); 304 } catch(RemoteException e) { 305 fail("Failed to notify onHeaderExtensionReceived: " + e); 306 } 307 } 308 309 @Test testNotifyMediaQualityStatus()310 public void testNotifyMediaQualityStatus() { 311 // Receive MediaQualityStatus 312 MediaQualityStatus status = MediaQualityStatusTest.createMediaQualityStatus(); 313 Utils.sendMessage(handler, AudioSession.EVENT_MEDIA_QUALITY_STATUS_IND, status); 314 processAllMessages(); 315 try { 316 verify(callback, times(1)).notifyMediaQualityStatus(eq(status)); 317 } catch (RemoteException e) { 318 fail("Failed to notify notifyMediaInactivity: " + e); 319 } 320 } 321 322 @Test testTriggerAnbrQuery()323 public void testTriggerAnbrQuery() { 324 // Receive triggerAnbrQuery for ANBR 325 AudioConfig config = AudioConfigTest.createAudioConfig(); 326 Utils.sendMessage(handler, AudioSession.EVENT_TRIGGER_ANBR_QUERY_IND, config); 327 processAllMessages(); 328 try { 329 verify(callback, times(1)).triggerAnbrQuery(eq(config)); 330 } catch (RemoteException e) { 331 fail("Failed to notify triggerAnbrQuery: " + e); 332 } 333 } 334 335 @Test testDtmfReceived()336 public void testDtmfReceived() { 337 // Receive onDtmfReceived 338 Utils.sendMessage(handler, AudioSession.EVENT_DTMF_RECEIVED_IND, DTMF_DIGIT, DTMF_DURATION); 339 processAllMessages(); 340 try { 341 verify(callback, times(1)).onDtmfReceived(eq(DTMF_DIGIT), eq(DTMF_DURATION)); 342 } catch (RemoteException e) { 343 fail("Failed to notify onDtmfReceived: " + e); 344 } 345 } 346 347 @Test testCallQualityChangedInd()348 public void testCallQualityChangedInd() { 349 // Receive Call Quality Changed Indication 350 CallQuality callQuality = CallQualityTest.createCallQuality(); 351 Utils.sendMessage(handler, AudioSession.EVENT_CALL_QUALITY_CHANGE_IND, callQuality); 352 processAllMessages(); 353 try { 354 verify(callback, times(1)).onCallQualityChanged(eq(callQuality)); 355 } catch (RemoteException e) { 356 fail("Failed to notify onCallQualityChanged: " + e); 357 } 358 } 359 360 @Test testNotifyRtpReceptionStats()361 public void testNotifyRtpReceptionStats() { 362 // Receive Rtp reception statistics notification 363 RtpReceptionStats stats = RtpReceptionStatsTest.createRtpReceptionStats(); 364 Utils.sendMessage(handler, AudioSession.EVENT_NOTIFY_RECEPTION_STATS, stats); 365 processAllMessages(); 366 try { 367 verify(callback, times(1)).notifyRtpReceptionStats(eq(stats)); 368 } catch (RemoteException e) { 369 fail("Failed to notify RtpReceptionStats: " + e); 370 } 371 } 372 373 @Test testOpenSessionSuccess()374 public void testOpenSessionSuccess() { 375 audioSession.onOpenSessionSuccess(audioLocalSession); 376 processAllMessages(); 377 try { 378 verify(callback, times(1)).onOpenSessionSuccess(audioSession); 379 assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false); 380 } catch (RemoteException e) { 381 fail("Failed to notify onOpenSessionSuccess: " + e); 382 } 383 } 384 385 @Test testOpenSessionFailure()386 public void testOpenSessionFailure() { 387 audioSession.onOpenSessionFailure(ImsMediaSession.RESULT_INVALID_PARAM); 388 processAllMessages(); 389 try { 390 verify(callback, times(1)).onOpenSessionFailure(ImsMediaSession.RESULT_INVALID_PARAM); 391 assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false); 392 } catch (RemoteException e) { 393 fail("Failed to notify onOpenSessionFailure: " + e); 394 } 395 } 396 397 @Test testSessionClosed()398 public void testSessionClosed() { 399 audioSession.onSessionClosed(); 400 processAllMessages(); 401 try { 402 verify(callback, times(1)).onSessionClosed(); 403 assertThat(mWakeLockManager.mWakeLock.isHeld()).isEqualTo(false); 404 } catch (RemoteException e) { 405 fail("Failed to notify onSessionClosed: " + e); 406 } 407 } 408 } 409