• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 junit.framework.Assert.assertEquals;
20 
21 import static org.junit.Assert.fail;
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.Mockito.doReturn;
24 import static org.mockito.Mockito.eq;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 
29 import android.hardware.radio.ims.media.IImsMedia;
30 import android.hardware.radio.ims.media.IImsMediaSession;
31 import android.hardware.radio.ims.media.RtpConfig;
32 import android.hardware.radio.ims.media.RtpError;
33 import android.os.Looper;
34 import android.os.ParcelFileDescriptor;
35 import android.os.RemoteException;
36 import android.telephony.CallQuality;
37 import android.telephony.ims.RtpHeaderExtension;
38 import android.telephony.imsmedia.AudioConfig;
39 import android.telephony.imsmedia.IImsAudioSessionCallback;
40 import android.telephony.imsmedia.ImsMediaSession;
41 import android.telephony.imsmedia.MediaQualityStatus;
42 import android.telephony.imsmedia.MediaQualityThreshold;
43 import android.testing.AndroidTestingRunner;
44 import android.testing.TestableLooper;
45 
46 import com.android.telephony.imsmedia.AudioSession;
47 import com.android.telephony.imsmedia.Utils;
48 import com.android.telephony.imsmedia.Utils.OpenSessionParams;
49 
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.mockito.ArgumentCaptor;
55 import org.mockito.Mock;
56 import org.mockito.MockitoAnnotations;
57 
58 import java.net.DatagramSocket;
59 import java.net.SocketException;
60 import java.util.ArrayList;
61 import java.util.List;
62 import java.util.stream.Collectors;
63 
64 @RunWith(AndroidTestingRunner.class)
65 @TestableLooper.RunWithLooper
66 public class AudioOffloadTest extends ImsMediaTest {
67     private static final int SESSION_ID = 1;
68     private static final int DTMF_DURATION = 120;
69     private static final int NO_RESOURCES = ImsMediaSession.RESULT_NO_RESOURCES;
70     private static final int NO_MEMORY = ImsMediaSession.RESULT_NO_MEMORY;
71     private static final int SUCCESS = ImsMediaSession.RESULT_SUCCESS;
72     private static final int PACKET_LOSS = 15;
73     private static final int JITTER = 200;
74     private static final char DTMF_DIGIT = '7';
75     private AudioSession audioSession;
76     private AudioOffloadListener offloadListener;
77     private AudioSession.AudioSessionHandler handler;
78     @Mock
79     private IImsAudioSessionCallback callback;
80     @Mock
81     private IImsMedia imsMedia;
82     @Mock
83     private IImsMediaSession imsMediaSession;
84     @Mock
85     private AudioOffloadService offloadService;
86 
87     @Before
setUp()88     public void setUp() {
89         MockitoAnnotations.initMocks(this);
90         offloadService = spy(AudioOffloadService.getInstance());
91         doReturn(imsMedia).when(offloadService).getIImsMedia();
92         audioSession = new AudioSession(SESSION_ID, callback, null, null, offloadService,
93                 Looper.myLooper());
94         handler = audioSession.getAudioSessionHandler();
95         audioSession.setAudioOffload(true);
96         offloadListener = audioSession.getOffloadListener();
97         audioSession.onOpenSessionSuccess(imsMediaSession);
98         mTestClass = AudioOffloadTest.this;
99         super.setUp();
100     }
101 
102     @After
tearDown()103     public void tearDown() throws Exception {
104         super.tearDown();
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         audioSession.openSession(params);
124         processAllMessages();
125 
126         verify(offloadService, times(1)).openSession(eq(SESSION_ID), eq(params));
127         try {
128             verify(imsMedia, times(1)).openSession(eq(SESSION_ID), any(), eq(null));
129         } catch (RemoteException e) {
130             fail("Failed to invoke openSession:" + e);
131         }
132 
133         rtpSocket.close();
134         rtcpSocket.close();
135     }
136 
137     @Test
testCloseSession()138     public void testCloseSession() {
139         audioSession.closeSession();
140         processAllMessages();
141         verify(offloadService, times(1)).closeSession(eq(SESSION_ID));
142     }
143 
144     @Test
testModifySession()145     public void testModifySession() {
146         final AudioConfig inputAudioConfig = AudioConfigTest.createAudioConfig();
147         RtpConfig outputRtpConfig = null;
148 
149         // Modify Session Request
150         audioSession.modifySession(inputAudioConfig);
151         processAllMessages();
152         try {
153             ArgumentCaptor<RtpConfig> argumentCaptor = ArgumentCaptor.forClass(RtpConfig.class);
154             verify(imsMediaSession, times(1)).modifySession(argumentCaptor.capture());
155             // Get the HAL RtpConfig
156             outputRtpConfig = argumentCaptor.getValue();
157             // Covert it back to AudioConfig
158             final AudioConfig outputAudioConfig = Utils.convertToAudioConfig(outputRtpConfig);
159             // Ensure both are same
160             assertEquals(inputAudioConfig, outputAudioConfig);
161         } catch (RemoteException e) {
162             fail("Failed to invoke modifySession: " + e);
163         }
164 
165         // Modify Session Response - SUCCESS
166         offloadListener.onModifySessionResponse(outputRtpConfig, RtpError.NONE);
167         processAllMessages();
168         try {
169             verify(callback, times(1)).onModifySessionResponse(eq(inputAudioConfig), eq(SUCCESS));
170         } catch (RemoteException e) {
171             fail("Failed to notify modifySessionResponse: " + e);
172         }
173 
174         // Modify Session Response - FAILURE
175         offloadListener.onModifySessionResponse(outputRtpConfig, RtpError.NO_RESOURCES);
176         processAllMessages();
177         try {
178             verify(callback, times(1)).onModifySessionResponse(
179                     eq(inputAudioConfig), eq(NO_RESOURCES));
180         } catch (RemoteException e) {
181             fail("Failed to notify modifySessionResponse: " + e);
182         }
183     }
184 
185     @Test
testAddConfig()186     public void testAddConfig() {
187         final AudioConfig inputAudioConfig = AudioConfigTest.createAudioConfig();
188         RtpConfig outputRtpConfig = null;
189 
190         // Add Config Request
191         audioSession.addConfig(inputAudioConfig);
192         processAllMessages();
193         try {
194             ArgumentCaptor<RtpConfig> argumentCaptor = ArgumentCaptor.forClass(RtpConfig.class);
195             verify(imsMediaSession, times(1)).modifySession(argumentCaptor.capture());
196             // Get the HAL RtpConfig
197             outputRtpConfig = argumentCaptor.getValue();
198             // Covert it back to AudioConfig
199             final AudioConfig outputAudioConfig = Utils.convertToAudioConfig(outputRtpConfig);
200             // Ensure both are same
201             assertEquals(inputAudioConfig, outputAudioConfig);
202         } catch (RemoteException e) {
203             fail("Failed to invoke addConfig: " + e);
204         }
205 
206         // Add Config Response - SUCCESS
207         offloadListener.onModifySessionResponse(outputRtpConfig, RtpError.NONE);
208         processAllMessages();
209         try {
210             verify(callback, times(1)).onModifySessionResponse(eq(inputAudioConfig), eq(SUCCESS));
211         } catch (RemoteException e) {
212             fail("Failed to notify addConfigResponse: " + e);
213         }
214 
215         // Add Config Response - FAILURE
216         offloadListener.onModifySessionResponse(outputRtpConfig, RtpError.NO_MEMORY);
217         processAllMessages();
218         try {
219             verify(callback, times(1)).onModifySessionResponse(eq(inputAudioConfig), eq(NO_MEMORY));
220         } catch (RemoteException e) {
221             fail("Failed to notify addConfigResponse: " + e);
222         }
223     }
224 
225     @Test
testsendDtmf()226     public void testsendDtmf() {
227         audioSession.sendDtmf(DTMF_DIGIT, DTMF_DURATION);
228         processAllMessages();
229         try {
230             verify(imsMediaSession, times(1)).sendDtmf(eq(DTMF_DIGIT), eq(DTMF_DURATION));
231         } catch (RemoteException e) {
232             fail("Failed to invoke sendDtmf: " + e);
233         }
234     }
235 
236     @Test
testStartDtmf()237     public void testStartDtmf() {
238         audioSession.startDtmf(DTMF_DIGIT);
239         processAllMessages();
240         try {
241             verify(imsMediaSession, times(1)).startDtmf(eq(DTMF_DIGIT));
242         } catch (RemoteException e) {
243             fail("Failed to invoke startDtmf: " + e);
244         }
245     }
246 
247     @Test
testStopDtmf()248     public void testStopDtmf() {
249         audioSession.stopDtmf();
250         processAllMessages();
251         try {
252             verify(imsMediaSession, times(1)).stopDtmf();
253         } catch (RemoteException e) {
254             fail("Failed to invoke stopDtmf: " + e);
255         }
256 
257     }
258 
259     @Test
testSetMediaQualityThreshold()260     public void testSetMediaQualityThreshold() {
261         // Set Media Quality Threshold
262         MediaQualityThreshold threshold =
263                 MediaQualityThresholdTest.createMediaQualityThresholdForHal();
264         audioSession.setMediaQualityThreshold(threshold);
265         processAllMessages();
266         try {
267             ArgumentCaptor<android.hardware.radio.ims.media.MediaQualityThreshold> argumentCaptor =
268                     ArgumentCaptor.forClass(
269                         android.hardware.radio.ims.media.MediaQualityThreshold.class);
270             verify(imsMediaSession, times(1)).setMediaQualityThreshold(argumentCaptor.capture());
271             // Get the HAL MediaQualityThreshold
272             final android.hardware.radio.ims.media.MediaQualityThreshold
273                     halThreshold = argumentCaptor.getValue();
274             // Covert it back to {@link MediaQualityThreshold}
275             final MediaQualityThreshold expectedThreshold =
276                     Utils.convertMediaQualityThreshold(halThreshold);
277             // Ensure both are same
278             assertEquals(threshold, expectedThreshold);
279         } catch (RemoteException e) {
280             fail("Failed to invoke setMediaQualityThreshold: " + e);
281         }
282 
283     }
284 
285     @Test
testMediaQualityStatusInd()286     public void testMediaQualityStatusInd() {
287         // Receive MediaQualityStatus
288         final MediaQualityStatus outputStatus =
289                 MediaQualityStatusTest.createMediaQualityStatus();
290         final android.hardware.radio.ims.media.MediaQualityStatus inputStatus =
291                 Utils.convertToHalMediaQualityStatus(outputStatus);
292         offloadListener.notifyMediaQualityStatus(inputStatus);
293         processAllMessages();
294         try {
295             verify(callback, times(1)).notifyMediaQualityStatus(eq(outputStatus));
296         } catch (RemoteException e) {
297             fail("Failed to notify media quality status: " + e);
298         }
299     }
300 
301     @Test
testFirstMediaPacketReceivedInd()302     public void testFirstMediaPacketReceivedInd() {
303         final AudioConfig outputAudioConfig = AudioConfigTest.createAudioConfig();
304         final RtpConfig inputRtpConfig = Utils.convertToRtpConfig(outputAudioConfig);
305 
306         // Receive First MediaPacket Received Indication
307         offloadListener.onFirstMediaPacketReceived(inputRtpConfig);
308         processAllMessages();
309         try {
310             verify(callback, times(1)).onFirstMediaPacketReceived(eq(outputAudioConfig));
311         } catch (RemoteException e) {
312             fail("Failed to notify onFirstMediaPacketReceived: " + e);
313         }
314     }
315 
316     @Test
testHeaderExtension()317     public void testHeaderExtension() {
318         final byte[] arr1 = {1, 2, 3, 4};
319         final byte[] arr2 = {4, 2, 3, 4, 6};
320         final ArrayList inputExtensions = new ArrayList<RtpHeaderExtension>();
321         inputExtensions.add(new RtpHeaderExtension(7, arr1));
322         inputExtensions.add(new RtpHeaderExtension(8, arr2));
323 
324         List<android.hardware.radio.ims.media.RtpHeaderExtension> halExtensions = null;
325 
326         // Send RtpHeaderExtension
327         audioSession.sendHeaderExtension(inputExtensions);
328         processAllMessages();
329         try {
330             ArgumentCaptor<List<android.hardware.radio.ims.media.RtpHeaderExtension>>
331                     argumentCaptor = ArgumentCaptor.forClass(List.class);
332             verify(imsMediaSession, times(1)).sendHeaderExtension(argumentCaptor.capture());
333             // Get the HAL RtpHeaderExtension list
334             halExtensions = argumentCaptor.getValue();
335             // Covert it back to {@link RtpHeaderExtension} list
336             final List<RtpHeaderExtension> outputExtensions =
337                     halExtensions.stream().map(Utils::convertRtpHeaderExtension)
338                     .collect(Collectors.toList());
339             // Ensure both are same
340             assertEquals(inputExtensions, outputExtensions);
341         } catch (RemoteException e) {
342             fail("Failed to invoke sendHeaderExtension: " + e);
343         }
344 
345         // Receive HAL RtpHeaderExtension
346         offloadListener.onHeaderExtensionReceived(halExtensions);
347         processAllMessages();
348         try {
349             verify(callback, times(1)).onHeaderExtensionReceived(eq(inputExtensions));
350         } catch (RemoteException e) {
351             fail("Failed to notify onHeaderExtensionReceived: " + e);
352         }
353     }
354 
355     @Test
testTriggerAnbrQuery()356     public void testTriggerAnbrQuery() {
357         final AudioConfig outputAudioConfig = AudioConfigTest.createAudioConfig();
358         final RtpConfig inputRtpConfig = Utils.convertToRtpConfig(outputAudioConfig);
359 
360         // Receive triggerAnbrQuery for ANBR
361         offloadListener.triggerAnbrQuery(inputRtpConfig);
362         processAllMessages();
363         try {
364             verify(callback, times(1)).triggerAnbrQuery(eq(outputAudioConfig));
365         } catch (RemoteException e) {
366             fail("Failed to notify triggerAnbrQuery: " + e);
367         }
368     }
369 
370     @Test
testDtmfReceived()371     public void testDtmfReceived() {
372         // Receive DTMF Received
373         offloadListener.onDtmfReceived(DTMF_DIGIT, DTMF_DURATION);
374         processAllMessages();
375         try {
376             verify(callback, times(1)).onDtmfReceived(eq(DTMF_DIGIT), eq(DTMF_DURATION));
377         } catch (RemoteException e) {
378             fail("Failed to notify onDtmfReceived: " + e);
379         }
380     }
381 
382     @Test
testCallQualityChangedInd()383     public void testCallQualityChangedInd() {
384         final android.hardware.radio.ims.media.CallQuality inputCallQuality =
385                 CallQualityTest.createHalCallQuality();
386         final CallQuality outputCallQuality = Utils.convertCallQuality(inputCallQuality);
387 
388         // Receive Call Quality Changed Indication
389         offloadListener.onCallQualityChanged(inputCallQuality);
390         processAllMessages();
391         try {
392             verify(callback, times(1)).onCallQualityChanged(eq(outputCallQuality));
393         } catch (RemoteException e) {
394             fail("Failed to notify onCallQualityChanged: " + e);
395         }
396     }
397 }
398