• 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 package android.virtualdevice.cts;
17 
18 import static android.Manifest.permission.ACTIVITY_EMBEDDING;
19 import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
20 import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
21 import static android.Manifest.permission.CAPTURE_AUDIO_OUTPUT;
22 import static android.Manifest.permission.CREATE_VIRTUAL_DEVICE;
23 import static android.Manifest.permission.MODIFY_AUDIO_ROUTING;
24 import static android.Manifest.permission.REAL_GET_TASKS;
25 import static android.Manifest.permission.WAKE_LOCK;
26 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
27 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
28 import static android.media.AudioFormat.CHANNEL_IN_MONO;
29 import static android.media.AudioFormat.ENCODING_PCM_16BIT;
30 import static android.media.AudioFormat.ENCODING_PCM_FLOAT;
31 import static android.media.AudioRecord.READ_BLOCKING;
32 import static android.media.AudioRecord.RECORDSTATE_RECORDING;
33 import static android.media.AudioRecord.RECORDSTATE_STOPPED;
34 import static android.media.AudioTrack.PLAYSTATE_PLAYING;
35 import static android.media.AudioTrack.PLAYSTATE_STOPPED;
36 import static android.media.AudioTrack.WRITE_BLOCKING;
37 import static android.media.AudioTrack.WRITE_NON_BLOCKING;
38 import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_LAST_RECORDED_NONZERO_VALUE;
39 import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_POWER_SPECTRUM_AT_FREQUENCY;
40 import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_POWER_SPECTRUM_NOT_FREQUENCY;
41 import static android.virtualdevice.cts.common.AudioHelper.ACTION_PLAY_AUDIO;
42 import static android.virtualdevice.cts.common.AudioHelper.ACTION_RECORD_AUDIO;
43 import static android.virtualdevice.cts.common.AudioHelper.AMPLITUDE;
44 import static android.virtualdevice.cts.common.AudioHelper.BUFFER_SIZE_IN_BYTES;
45 import static android.virtualdevice.cts.common.AudioHelper.BYTE_ARRAY;
46 import static android.virtualdevice.cts.common.AudioHelper.BYTE_BUFFER;
47 import static android.virtualdevice.cts.common.AudioHelper.BYTE_VALUE;
48 import static android.virtualdevice.cts.common.AudioHelper.CHANNEL_COUNT;
49 import static android.virtualdevice.cts.common.AudioHelper.EXTRA_AUDIO_DATA_TYPE;
50 import static android.virtualdevice.cts.common.AudioHelper.FLOAT_ARRAY;
51 import static android.virtualdevice.cts.common.AudioHelper.FLOAT_VALUE;
52 import static android.virtualdevice.cts.common.AudioHelper.FREQUENCY;
53 import static android.virtualdevice.cts.common.AudioHelper.NUMBER_OF_SAMPLES;
54 import static android.virtualdevice.cts.common.AudioHelper.SAMPLE_RATE;
55 import static android.virtualdevice.cts.common.AudioHelper.SHORT_ARRAY;
56 import static android.virtualdevice.cts.common.AudioHelper.SHORT_VALUE;
57 import static android.virtualdevice.cts.util.TestAppHelper.MAIN_ACTIVITY_COMPONENT;
58 import static android.virtualdevice.cts.util.VirtualDeviceTestUtils.createActivityOptions;
59 
60 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
61 
62 import static com.google.common.truth.Truth.assertThat;
63 
64 import static org.junit.Assume.assumeFalse;
65 import static org.junit.Assume.assumeTrue;
66 import static org.mockito.ArgumentMatchers.any;
67 import static org.mockito.Mockito.timeout;
68 import static org.mockito.Mockito.verify;
69 
70 import android.companion.virtual.VirtualDeviceManager;
71 import android.companion.virtual.VirtualDeviceManager.VirtualDevice;
72 import android.companion.virtual.VirtualDeviceParams;
73 import android.companion.virtual.audio.AudioCapture;
74 import android.companion.virtual.audio.AudioInjection;
75 import android.companion.virtual.audio.VirtualAudioDevice;
76 import android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback;
77 import android.content.Context;
78 import android.content.Intent;
79 import android.content.pm.PackageManager;
80 import android.hardware.display.VirtualDisplay;
81 import android.media.AudioFormat;
82 import android.platform.test.annotations.AppModeFull;
83 import android.virtualdevice.cts.common.ActivityResultReceiver;
84 import android.virtualdevice.cts.common.AudioHelper;
85 import android.virtualdevice.cts.util.FakeAssociationRule;
86 
87 import androidx.test.ext.junit.runners.AndroidJUnit4;
88 import androidx.test.platform.app.InstrumentationRegistry;
89 
90 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
91 
92 import org.junit.After;
93 import org.junit.Before;
94 import org.junit.Rule;
95 import org.junit.Test;
96 import org.junit.runner.RunWith;
97 import org.mockito.ArgumentCaptor;
98 import org.mockito.Captor;
99 import org.mockito.Mock;
100 import org.mockito.MockitoAnnotations;
101 
102 import java.nio.ByteBuffer;
103 import java.nio.ByteOrder;
104 
105 /**
106  * Tests for injection and capturing of audio from streamed apps
107  */
108 @RunWith(AndroidJUnit4.class)
109 @AppModeFull(reason = "VirtualDeviceManager cannot be accessed by instant apps")
110 public class VirtualAudioTest {
111     /**
112      * Captured signal should be mostly single frequency and power of that frequency should be
113      * over this much of total power.
114      */
115     public static final double POWER_THRESHOLD_FOR_PRESENT = 0.4f;
116 
117     /**
118      * The other signals should have very weak power and should not exceed this value
119      */
120     public static final double POWER_THRESHOLD_FOR_ABSENT = 0.02f;
121 
122     private static final VirtualDeviceParams DEFAULT_VIRTUAL_DEVICE_PARAMS =
123             new VirtualDeviceParams.Builder().build();
124 
125     @Rule
126     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
127             InstrumentationRegistry.getInstrumentation().getUiAutomation(),
128             ACTIVITY_EMBEDDING,
129             ADD_ALWAYS_UNLOCKED_DISPLAY,
130             ADD_TRUSTED_DISPLAY,
131             CREATE_VIRTUAL_DEVICE,
132             REAL_GET_TASKS,
133             WAKE_LOCK,
134             MODIFY_AUDIO_ROUTING,
135             CAPTURE_AUDIO_OUTPUT);
136     @Rule
137     public FakeAssociationRule mFakeAssociationRule = new FakeAssociationRule();
138 
139     private VirtualDevice mVirtualDevice;
140     private VirtualDisplay mVirtualDisplay;
141     private VirtualAudioDevice mVirtualAudioDevice;
142 
143     @Mock
144     private VirtualDisplay.Callback mVirtualDisplayCallback;
145     @Mock
146     private AudioConfigurationChangeCallback mAudioConfigurationChangeCallback;
147     @Mock
148     private ActivityResultReceiver.Callback mActivityResultCallback;
149     @Captor
150     private ArgumentCaptor<Intent> mIntentCaptor;
151 
152     @Before
setUp()153     public void setUp() throws Exception {
154         MockitoAnnotations.initMocks(this);
155         Context context = getApplicationContext();
156         PackageManager packageManager = context.getPackageManager();
157         assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP));
158         assumeTrue(packageManager.hasSystemFeature(
159                 PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS));
160         assumeFalse("Skipping test: not supported on automotive", isAutomotive());
161         // TODO(b/261155110): Re-enable tests once freeform mode is supported in Virtual Display.
162         assumeFalse("Skipping test: VirtualDisplay window policy doesn't support freeform.",
163                 packageManager.hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT));
164 
165         VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
166         mVirtualDevice = vdm.createVirtualDevice(
167                 mFakeAssociationRule.getAssociationInfo().getId(),
168                 DEFAULT_VIRTUAL_DEVICE_PARAMS);
169         mVirtualDisplay = mVirtualDevice.createVirtualDisplay(
170                 /* width= */ 100,
171                 /* height= */ 100,
172                 /* densityDpi= */ 240,
173                 /* surface= */ null,
174                 /* flags= */ VIRTUAL_DISPLAY_FLAG_TRUSTED,
175                 Runnable::run,
176                 mVirtualDisplayCallback);
177     }
178 
179     @After
tearDown()180     public void tearDown() {
181         if (mVirtualDevice != null) {
182             mVirtualDevice.close();
183         }
184         if (mVirtualDisplay != null) {
185             mVirtualDisplay.release();
186         }
187         if (mVirtualAudioDevice != null) {
188             mVirtualAudioDevice.close();
189         }
190     }
191 
192     @Test
audioCapture_createCorrectly()193     public void audioCapture_createCorrectly() {
194         mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
195                 mVirtualDisplay, /* executor= */ null, /* callback= */ null);
196         AudioFormat audioFormat = createCaptureFormat(ENCODING_PCM_16BIT);
197         AudioCapture audioCapture = mVirtualAudioDevice.startAudioCapture(audioFormat);
198         assertThat(audioCapture).isNotNull();
199         assertThat(audioCapture.getFormat()).isEqualTo(audioFormat);
200         assertThat(mVirtualAudioDevice.getAudioCapture()).isEqualTo(audioCapture);
201 
202         audioCapture.startRecording();
203         assertThat(audioCapture.getRecordingState()).isEqualTo(RECORDSTATE_RECORDING);
204         audioCapture.stop();
205         assertThat(audioCapture.getRecordingState()).isEqualTo(RECORDSTATE_STOPPED);
206     }
207 
208     @Test
audioInjection_createCorrectly()209     public void audioInjection_createCorrectly() {
210         mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
211                 mVirtualDisplay, /* executor= */ null, /* callback= */ null);
212         AudioFormat audioFormat = createInjectionFormat(ENCODING_PCM_16BIT);
213         AudioInjection audioInjection = mVirtualAudioDevice.startAudioInjection(audioFormat);
214         assertThat(audioInjection).isNotNull();
215         assertThat(audioInjection.getFormat()).isEqualTo(audioFormat);
216         assertThat(mVirtualAudioDevice.getAudioInjection()).isEqualTo(audioInjection);
217 
218         audioInjection.play();
219         assertThat(audioInjection.getPlayState()).isEqualTo(PLAYSTATE_PLAYING);
220         audioInjection.stop();
221         assertThat(audioInjection.getPlayState()).isEqualTo(PLAYSTATE_STOPPED);
222     }
223 
224     @Test
audioCapture_receivesAudioConfigurationChangeCallback()225     public void audioCapture_receivesAudioConfigurationChangeCallback() {
226         mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
227                 mVirtualDisplay, /* executor= */ null, mAudioConfigurationChangeCallback);
228         AudioFormat audioFormat = createCaptureFormat(ENCODING_PCM_16BIT);
229         mVirtualAudioDevice.startAudioCapture(audioFormat);
230 
231         ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
232                 getApplicationContext());
233         activityResultReceiver.register(mActivityResultCallback);
234         InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
235                 createPlayAudioIntent(BYTE_BUFFER),
236                 createActivityOptions(mVirtualDisplay));
237         verify(mAudioConfigurationChangeCallback, timeout(5000).atLeastOnce())
238                 .onPlaybackConfigChanged(any());
239         verify(mActivityResultCallback, timeout(5000)).onActivityResult(
240                 mIntentCaptor.capture());
241         activityResultReceiver.unregister();
242     }
243 
244     @Test
audioInjection_receivesAudioConfigurationChangeCallback()245     public void audioInjection_receivesAudioConfigurationChangeCallback() {
246         mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
247                 mVirtualDisplay, /* executor= */ null, mAudioConfigurationChangeCallback);
248         AudioFormat audioFormat = createInjectionFormat(ENCODING_PCM_16BIT);
249         AudioInjection audioInjection = mVirtualAudioDevice.startAudioInjection(audioFormat);
250 
251         ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
252                 getApplicationContext());
253         activityResultReceiver.register(mActivityResultCallback);
254         InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
255                 createAudioRecordIntent(BYTE_BUFFER),
256                 createActivityOptions(mVirtualDisplay));
257 
258         ByteBuffer byteBuffer = AudioHelper.createAudioData(
259                 SAMPLE_RATE, NUMBER_OF_SAMPLES, CHANNEL_COUNT, FREQUENCY, AMPLITUDE);
260         int remaining = byteBuffer.remaining();
261         while (remaining > 0) {
262             remaining -= audioInjection.write(byteBuffer, byteBuffer.remaining(), WRITE_BLOCKING);
263         }
264 
265         verify(mAudioConfigurationChangeCallback, timeout(5000).atLeastOnce())
266                 .onRecordingConfigChanged(any());
267         verify(mActivityResultCallback, timeout(5000)).onActivityResult(
268                 mIntentCaptor.capture());
269         activityResultReceiver.unregister();
270     }
271 
272     @Test
audioCapture_readByteBuffer_shouldCaptureAppPlaybackFrequency()273     public void audioCapture_readByteBuffer_shouldCaptureAppPlaybackFrequency() {
274         runAudioCaptureTest(BYTE_BUFFER, /* readMode= */ -1);
275     }
276 
277     @Test
audioCapture_readByteBufferBlocking_shouldCaptureAppPlaybackFrequency()278     public void audioCapture_readByteBufferBlocking_shouldCaptureAppPlaybackFrequency() {
279         runAudioCaptureTest(BYTE_BUFFER, /* readMode= */ READ_BLOCKING);
280     }
281 
282     @Test
audioCapture_readByteArray_shouldCaptureAppPlaybackData()283     public void audioCapture_readByteArray_shouldCaptureAppPlaybackData() {
284         runAudioCaptureTest(BYTE_ARRAY, /* readMode= */ -1);
285     }
286 
287     @Test
audioCapture_readByteArrayBlocking_shouldCaptureAppPlaybackData()288     public void audioCapture_readByteArrayBlocking_shouldCaptureAppPlaybackData() {
289         runAudioCaptureTest(BYTE_ARRAY, /* readMode= */ READ_BLOCKING);
290     }
291 
292     @Test
audioCapture_readShortArray_shouldCaptureAppPlaybackData()293     public void audioCapture_readShortArray_shouldCaptureAppPlaybackData() {
294         runAudioCaptureTest(SHORT_ARRAY, /* readMode= */ -1);
295     }
296 
297     @Test
audioCapture_readShortArrayBlocking_shouldCaptureAppPlaybackData()298     public void audioCapture_readShortArrayBlocking_shouldCaptureAppPlaybackData() {
299         runAudioCaptureTest(SHORT_ARRAY, /* readMode= */ READ_BLOCKING);
300     }
301 
302     @Test
audioCapture_readFloatArray_shouldCaptureAppPlaybackData()303     public void audioCapture_readFloatArray_shouldCaptureAppPlaybackData() {
304         runAudioCaptureTest(FLOAT_ARRAY, /* readMode= */ READ_BLOCKING);
305     }
306 
307     @Test
audioInjection_writeByteBuffer_appShouldRecordInjectedFrequency()308     public void audioInjection_writeByteBuffer_appShouldRecordInjectedFrequency() {
309         runAudioInjectionTest(BYTE_BUFFER, /* writeMode= */
310                 WRITE_BLOCKING, /* timestamp= */ 0);
311     }
312 
313     @Test
audioInjection_writeByteBufferWithTimestamp_appShouldRecordInjectedFrequency()314     public void audioInjection_writeByteBufferWithTimestamp_appShouldRecordInjectedFrequency() {
315         runAudioInjectionTest(BYTE_BUFFER, /* writeMode= */
316                 WRITE_BLOCKING, /* timestamp= */ 50);
317     }
318 
319     @Test
audioInjection_writeByteArray_appShouldRecordInjectedData()320     public void audioInjection_writeByteArray_appShouldRecordInjectedData() {
321         runAudioInjectionTest(BYTE_ARRAY, /* writeMode= */ -1, /* timestamp= */ 0);
322     }
323 
324     @Test
audioInjection_writeByteArrayBlocking_appShouldRecordInjectedData()325     public void audioInjection_writeByteArrayBlocking_appShouldRecordInjectedData() {
326         runAudioInjectionTest(BYTE_ARRAY, /* writeMode= */ WRITE_BLOCKING, /* timestamp= */
327                 0);
328     }
329 
330     @Test
audioInjection_writeShortArray_appShouldRecordInjectedData()331     public void audioInjection_writeShortArray_appShouldRecordInjectedData() {
332         runAudioInjectionTest(SHORT_ARRAY, /* writeMode= */ -1, /* timestamp= */ 0);
333     }
334 
335     @Test
audioInjection_writeShortArrayBlocking_appShouldRecordInjectedData()336     public void audioInjection_writeShortArrayBlocking_appShouldRecordInjectedData() {
337         runAudioInjectionTest(SHORT_ARRAY, /* writeMode= */
338                 WRITE_BLOCKING, /* timestamp= */ 0);
339     }
340 
341     @Test
audioInjection_writeFloatArray_appShouldRecordInjectedData()342     public void audioInjection_writeFloatArray_appShouldRecordInjectedData() {
343         runAudioInjectionTest(FLOAT_ARRAY, /* writeMode= */
344                 WRITE_BLOCKING, /* timestamp= */ 0);
345     }
346 
isAutomotive()347     private boolean isAutomotive() {
348         return getApplicationContext().getPackageManager()
349                 .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
350     }
351 
runAudioCaptureTest(@udioHelper.DataType int dataType, int readMode)352     private void runAudioCaptureTest(@AudioHelper.DataType int dataType, int readMode) {
353         mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
354                 mVirtualDisplay, /* executor= */ null, /* callback= */ null);
355         int encoding = dataType == FLOAT_ARRAY ? ENCODING_PCM_FLOAT : ENCODING_PCM_16BIT;
356         AudioCapture audioCapture = mVirtualAudioDevice.startAudioCapture(
357                 createCaptureFormat(encoding));
358 
359         ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
360                 getApplicationContext());
361         activityResultReceiver.register(mActivityResultCallback);
362         InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
363                 createPlayAudioIntent(dataType),
364                 createActivityOptions(mVirtualDisplay));
365 
366         AudioHelper.CapturedAudio capturedAudio = null;
367         switch (dataType) {
368             case BYTE_BUFFER:
369                 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE_IN_BYTES).order(
370                         ByteOrder.nativeOrder());
371                 capturedAudio = new AudioHelper.CapturedAudio(audioCapture, byteBuffer, readMode);
372                 assertThat(capturedAudio.getPowerSpectrum(FREQUENCY + 100))
373                         .isLessThan(POWER_THRESHOLD_FOR_ABSENT);
374                 assertThat(capturedAudio.getPowerSpectrum(FREQUENCY))
375                         .isGreaterThan(POWER_THRESHOLD_FOR_PRESENT);
376                 break;
377             case BYTE_ARRAY:
378                 byte[] byteArray = new byte[BUFFER_SIZE_IN_BYTES];
379                 capturedAudio = new AudioHelper.CapturedAudio(audioCapture, byteArray, readMode);
380                 assertThat(capturedAudio.getByteValue()).isEqualTo(BYTE_VALUE);
381                 break;
382             case SHORT_ARRAY:
383                 short[] shortArray = new short[BUFFER_SIZE_IN_BYTES / 2];
384                 capturedAudio = new AudioHelper.CapturedAudio(audioCapture, shortArray, readMode);
385                 assertThat(capturedAudio.getShortValue()).isEqualTo(SHORT_VALUE);
386                 break;
387             case FLOAT_ARRAY:
388                 float[] floatArray = new float[BUFFER_SIZE_IN_BYTES / 4];
389                 capturedAudio = new AudioHelper.CapturedAudio(audioCapture, floatArray, readMode);
390                 float roundOffError = Math.abs(capturedAudio.getFloatValue() - FLOAT_VALUE);
391                 assertThat(roundOffError).isLessThan(0.001f);
392                 break;
393         }
394 
395         verify(mActivityResultCallback, timeout(5000)).onActivityResult(
396                 mIntentCaptor.capture());
397         activityResultReceiver.unregister();
398     }
399 
runAudioInjectionTest(@udioHelper.DataType int dataType, int writeMode, long timestamp)400     private void runAudioInjectionTest(@AudioHelper.DataType int dataType, int writeMode,
401             long timestamp) {
402         mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
403                 mVirtualDisplay, /* executor= */ null, /* callback= */ null);
404         int encoding = dataType == FLOAT_ARRAY ? ENCODING_PCM_FLOAT : ENCODING_PCM_16BIT;
405         AudioInjection audioInjection = mVirtualAudioDevice.startAudioInjection(
406                 createInjectionFormat(encoding));
407 
408         ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
409                 getApplicationContext());
410         activityResultReceiver.register(mActivityResultCallback);
411         InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
412                 createAudioRecordIntent(dataType),
413                 createActivityOptions(mVirtualDisplay));
414 
415         int remaining;
416         switch (dataType) {
417             case BYTE_BUFFER:
418                 ByteBuffer byteBuffer = AudioHelper.createAudioData(
419                         SAMPLE_RATE, NUMBER_OF_SAMPLES, CHANNEL_COUNT, FREQUENCY, AMPLITUDE);
420                 remaining = byteBuffer.remaining();
421                 while (remaining > 0) {
422                     if (timestamp != 0) {
423                         remaining -= audioInjection.write(byteBuffer, byteBuffer.remaining(),
424                                 writeMode, timestamp);
425                     } else {
426                         remaining -= audioInjection.write(byteBuffer, byteBuffer.remaining(),
427                                 writeMode);
428                     }
429                 }
430                 break;
431             case BYTE_ARRAY:
432                 byte[] byteArray = new byte[NUMBER_OF_SAMPLES];
433                 for (int i = 0; i < byteArray.length; i++) {
434                     byteArray[i] = BYTE_VALUE;
435                 }
436                 remaining = byteArray.length;
437                 while (remaining > 0) {
438                     if (writeMode == WRITE_BLOCKING || writeMode == WRITE_NON_BLOCKING) {
439                         remaining -= audioInjection.write(byteArray, 0, byteArray.length,
440                                 writeMode);
441                     } else {
442                         remaining -= audioInjection.write(byteArray, 0, byteArray.length);
443                     }
444                 }
445                 break;
446             case SHORT_ARRAY:
447                 short[] shortArray = new short[NUMBER_OF_SAMPLES];
448                 for (int i = 0; i < shortArray.length; i++) {
449                     shortArray[i] = SHORT_VALUE;
450                 }
451                 remaining = shortArray.length;
452                 while (remaining > 0) {
453                     if (writeMode == WRITE_BLOCKING || writeMode == WRITE_NON_BLOCKING) {
454                         remaining -= audioInjection.write(shortArray, 0, shortArray.length,
455                                 writeMode);
456                     } else {
457                         remaining -= audioInjection.write(shortArray, 0, shortArray.length);
458                     }
459                 }
460                 break;
461             case FLOAT_ARRAY:
462                 float[] floatArray = new float[NUMBER_OF_SAMPLES];
463                 for (int i = 0; i < floatArray.length; i++) {
464                     floatArray[i] = FLOAT_VALUE;
465                 }
466                 remaining = floatArray.length;
467                 while (remaining > 0) {
468                     remaining -= audioInjection.write(floatArray, 0, floatArray.length, writeMode);
469                 }
470                 break;
471         }
472 
473         verify(mActivityResultCallback, timeout(5000)).onActivityResult(
474                 mIntentCaptor.capture());
475         Intent intent = mIntentCaptor.getValue();
476         assertThat(intent).isNotNull();
477         activityResultReceiver.unregister();
478 
479         switch (dataType) {
480             case BYTE_BUFFER:
481                 double powerSpectrumAtFrequency = intent.getDoubleExtra(
482                         EXTRA_POWER_SPECTRUM_AT_FREQUENCY,
483                         0);
484                 double powerSpectrumNotFrequency = intent.getDoubleExtra(
485                         EXTRA_POWER_SPECTRUM_NOT_FREQUENCY,
486                         0);
487                 assertThat(powerSpectrumNotFrequency).isLessThan(POWER_THRESHOLD_FOR_ABSENT);
488                 assertThat(powerSpectrumAtFrequency).isGreaterThan(POWER_THRESHOLD_FOR_PRESENT);
489                 break;
490             case BYTE_ARRAY:
491                 byte byteValue = intent.getByteExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE,
492                         Byte.MIN_VALUE);
493                 assertThat(byteValue).isEqualTo(BYTE_VALUE);
494                 break;
495             case SHORT_ARRAY:
496                 short shortValue = intent.getShortExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE,
497                         Short.MIN_VALUE);
498                 assertThat(shortValue).isEqualTo(SHORT_VALUE);
499                 break;
500             case FLOAT_ARRAY:
501                 float floatValue = intent.getFloatExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE, 0);
502                 float roundOffError = Math.abs(floatValue - FLOAT_VALUE);
503                 assertThat(roundOffError).isLessThan(0.001f);
504                 break;
505         }
506     }
507 
createCaptureFormat(int encoding)508     private static AudioFormat createCaptureFormat(int encoding) {
509         return new AudioFormat.Builder()
510                 .setSampleRate(SAMPLE_RATE)
511                 .setEncoding(encoding)
512                 .setChannelMask(CHANNEL_IN_MONO)
513                 .build();
514     }
515 
createInjectionFormat(int encoding)516     private static AudioFormat createInjectionFormat(int encoding) {
517         return new AudioFormat.Builder()
518                 .setSampleRate(SAMPLE_RATE)
519                 .setEncoding(encoding)
520                 .setChannelMask(CHANNEL_IN_MONO)
521                 .build();
522     }
523 
createPlayAudioIntent(@udioHelper.DataType int dataType)524     private static Intent createPlayAudioIntent(@AudioHelper.DataType int dataType) {
525         return new Intent(ACTION_PLAY_AUDIO)
526                 .putExtra(EXTRA_AUDIO_DATA_TYPE, dataType)
527                 .setComponent(MAIN_ACTIVITY_COMPONENT)
528                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
529     }
530 
createAudioRecordIntent(@udioHelper.DataType int dataType)531     private static Intent createAudioRecordIntent(@AudioHelper.DataType int dataType) {
532         return new Intent(ACTION_RECORD_AUDIO)
533                 .putExtra(EXTRA_AUDIO_DATA_TYPE, dataType)
534                 .setComponent(MAIN_ACTIVITY_COMPONENT)
535                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
536     }
537 }
538