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