• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.media.audiotestharness.instrumentation;
18 
19 import android.media.AudioAttributes;
20 import android.media.MediaPlayer;
21 import android.test.ActivityInstrumentationTestCase2;
22 import android.test.suitebuilder.annotation.LargeTest;
23 import android.util.Log;
24 
25 import com.android.media.audiotestharness.client.core.AudioCaptureStream;
26 import com.android.media.audiotestharness.client.core.AudioTestHarnessClient;
27 import com.android.media.audiotestharness.client.grpc.GrpcAudioTestHarnessClient;
28 
29 import com.google.common.io.Files;
30 
31 import java.io.File;
32 import java.io.IOException;
33 import java.nio.ByteBuffer;
34 import java.nio.ByteOrder;
35 
36 /** Sample test that demonstrates the capture functionality of the Audio Test Harness */
37 public class AudioTestHarnessCaptureTest extends ActivityInstrumentationTestCase2<MainActivity> {
38     private static final String TAG = AudioTestHarnessCaptureTest.class.getSimpleName();
39 
40     /** On device path to the file that should be played back during the test. */
41     private static final String TEST_FILE = "/system/product/media/audio/ringtones/Lollipop.ogg";
42 
43     /** Duration that the file should play. */
44     private static final int TEST_DURATION = 3;
45 
46     /** Amplitude value at which we consider a test PASSED. */
47     private static final int TEST_AMPLITUDE_THRESHOLD = 250;
48 
49     private static final int NUM_CHANNELS_MONO = 1;
50     private static final int BITS_PER_SAMPLE_16BIT = 16;
51 
AudioTestHarnessCaptureTest()52     public AudioTestHarnessCaptureTest() {
53         super(MainActivity.class);
54     }
55 
56     @Override
setUp()57     protected void setUp() throws Exception {
58         super.setUp();
59 
60         // Launch the activity which is required for connections to the Audio Test Harness to go
61         // through. Without this, connections result in ECONNREFUSED.
62         getActivity();
63     }
64 
65     /**
66      * Tests that Media Player properly outputs audio through the device speaker by checking that
67      * the microphone in the box picks up audio playback.
68      */
69     @LargeTest
testPlayAudioFile_outputsAudio()70     public void testPlayAudioFile_outputsAudio() throws Exception {
71 
72         // Create a new Audio Test Harness client, and start a capture session with the harness.
73         try (AudioTestHarnessClient audioTestHarnessClient =
74                 GrpcAudioTestHarnessClient.builder().build()) {
75             try (AudioCaptureStream audioCaptureStream = audioTestHarnessClient.startCapture()) {
76 
77                 // Playback the test file (Lollipop.ogg) and capture three seconds of audio
78                 // from the harness, ensuring that it is written as a test artifact.
79                 MediaPlayer mediaPlayer = startAudioFilePlayback(TEST_FILE);
80 
81                 // Create a buffer for three seconds of audio captured from the device.
82                 int numSamplesRequired =
83                         Math.round(3 * audioCaptureStream.getAudioFormat().getSampleRate());
84                 short[] samples = new short[numSamplesRequired];
85 
86                 // Capture three seconds of audio from the stream.
87                 int samplesRead = 0;
88                 while (samplesRead < numSamplesRequired) {
89                     samplesRead +=
90                             audioCaptureStream.read(
91                                     samples, samplesRead, samples.length - samplesRead);
92                 }
93 
94                 // Write file to storage and cleanup resources.
95                 writeSamplesToAppStorage("testPlayAudioFile_outputsAudio.pcm", samples);
96                 mediaPlayer.release();
97 
98                 // Verify that the amplitude was far above ambient and thus audio playback
99                 // was heard by the microphone.
100                 int maxAmplitude = Math.abs(samples[0]);
101                 for (int i = 1; i < samples.length; i++) {
102                     maxAmplitude = Math.max(maxAmplitude, Math.abs(samples[i]));
103                 }
104 
105                 Log.v(TAG, String.format("Maximum Amplitude of Capture: %d", maxAmplitude));
106                 assertTrue(
107                         "Could not detect audio playback", maxAmplitude > TEST_AMPLITUDE_THRESHOLD);
108             }
109         }
110     }
111 
112     /**
113      * Plays audio file for given amount of time.
114      *
115      * <p>Instantiates a MediaPlayer and plays the passed in audioFile for audioPlayDuration
116      * milliseconds. If the player fails to instantiate or any exception happened during the play,
117      * the test will fail.
118      *
119      * @return the MediaPlayer instance.
120      */
startAudioFilePlayback(String audioFile)121     private static MediaPlayer startAudioFilePlayback(String audioFile) {
122         Log.v(TAG, String.format("Playing audio file: %s", audioFile));
123         MediaPlayer mp = new MediaPlayer();
124         try {
125             mp.setAudioAttributes(
126                     new AudioAttributes.Builder()
127                             .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
128                             .setUsage(AudioAttributes.USAGE_MEDIA)
129                             .build());
130             mp.setDataSource(audioFile);
131             mp.prepare();
132             int duration = mp.getDuration();
133             if (duration <= 0) {
134                 Log.e(TAG, "Failed to grab duration from audio file.");
135                 fail("AudioFileWithNegativeDuration");
136             }
137             mp.start();
138         } catch (IOException e) {
139             Log.e(
140                     TAG,
141                     String.format("Exception happened while playing audio file: %s", audioFile),
142                     e);
143             fail("FailedToPlayAudioFile");
144         }
145 
146         return mp;
147     }
148 
149     /**
150      * Writes a provided short[] to app storage with a given filename. These files are picked up as
151      * test artifacts after the test completes. Any existing files are overwritten.
152      *
153      * @throws IOException if any errors occur while writing the file.
154      */
writeSamplesToAppStorage(String filename, short[] toWrite)155     private void writeSamplesToAppStorage(String filename, short[] toWrite) throws IOException {
156         File outputFile = new File(getActivity().getFilesDir(), filename);
157 
158         if (outputFile.exists()) {
159             outputFile.delete();
160         }
161         outputFile.createNewFile();
162 
163         // Convert our samples into a raw PCM file written little endian.
164         ByteBuffer buf = ByteBuffer.allocate(toWrite.length * 2).order(ByteOrder.LITTLE_ENDIAN);
165         for (short value : toWrite) {
166             buf.putShort(value);
167         }
168 
169         Files.write(buf.array(), outputFile);
170 
171         Log.v(TAG, String.format("Wrote file (%s) of size (%d)", outputFile, toWrite.length));
172     }
173 }
174