1 /*
2  * Copyright (C) 2015 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 android.media.cts;
18 
19 import android.content.Context;
20 import android.content.pm.PackageManager;
21 import android.content.res.AssetFileDescriptor;
22 
23 import android.media.AudioAttributes;
24 import android.media.AudioDeviceInfo;
25 import android.media.AudioFormat;
26 import android.media.AudioManager;
27 import android.media.AudioRecord;
28 import android.media.AudioRouting;
29 import android.media.AudioTrack;
30 import android.media.MediaPlayer;
31 import android.media.MediaFormat;
32 import android.media.MediaRecorder;
33 import android.media.cts.TestUtils.Monitor;
34 
35 import android.net.Uri;
36 import android.os.Environment;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.SystemClock;
40 import android.os.ParcelFileDescriptor;
41 import android.os.PowerManager;
42 
43 import android.platform.test.annotations.AppModeFull;
44 import android.test.AndroidTestCase;
45 
46 import android.util.Log;
47 
48 import com.android.compatibility.common.util.MediaUtils;
49 
50 import java.io.File;
51 import java.lang.Runnable;
52 import java.util.Arrays;
53 import java.util.HashSet;
54 import java.util.Set;
55 import java.util.concurrent.CountDownLatch;
56 import java.util.concurrent.ExecutorService;
57 import java.util.concurrent.Executors;
58 import java.util.concurrent.TimeUnit;
59 
60 /**
61  * AudioTrack / AudioRecord / MediaPlayer / MediaRecorder preferred device
62  * and routing listener tests.
63  * The routing tests are mostly here to exercise the routing code, as an actual test would require
64  * adding / removing an audio device for the listeners to be called.
65  * The routing listener code is designed to run for two versions of the routing code:
66  *  - the deprecated AudioTrack.OnRoutingChangedListener and AudioRecord.OnRoutingChangedListener
67  *  - the N AudioRouting.OnRoutingChangedListener
68  */
69 @AppModeFull(reason = "TODO: evaluate and port to instant")
70 public class RoutingTest extends AndroidTestCase {
71     private static final String TAG = "RoutingTest";
72     private static final long WAIT_ROUTING_CHANGE_TIME_MS = 3000;
73     private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
74     private static final int AUDIO_SAMPLE_RATE_HZ = 8000;
75     private static final long MAX_FILE_SIZE_BYTE = 5000;
76     private static final int RECORD_TIME_MS = 3000;
77     private static final long WAIT_PLAYBACK_START_TIME_MS = 1000;
78     private static final Set<Integer> AVAILABLE_INPUT_DEVICES_TYPE = new HashSet<>(
79         Arrays.asList(AudioDeviceInfo.TYPE_BUILTIN_MIC));
80     static final String mInpPrefix = WorkDir.getMediaDirString();
81 
82     private AudioManager mAudioManager;
83     private File mOutFile;
84 
85     @Override
setUp()86     protected void setUp() throws Exception {
87         super.setUp();
88 
89         // get the AudioManager
90         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
91         assertNotNull(mAudioManager);
92     }
93 
94     @Override
tearDown()95     protected void tearDown() throws Exception {
96         if (mOutFile != null && mOutFile.exists()) {
97             mOutFile.delete();
98         }
99         super.tearDown();
100     }
101 
allocAudioTrack()102     private AudioTrack allocAudioTrack() {
103         int bufferSize =
104                 AudioTrack.getMinBufferSize(
105                     41000,
106                     AudioFormat.CHANNEL_OUT_STEREO,
107                     AudioFormat.ENCODING_PCM_16BIT);
108         AudioTrack audioTrack =
109             new AudioTrack(
110                 AudioManager.STREAM_MUSIC,
111                 41000,
112                 AudioFormat.CHANNEL_OUT_STEREO,
113                 AudioFormat.ENCODING_PCM_16BIT,
114                 bufferSize,
115                 AudioTrack.MODE_STREAM);
116         return audioTrack;
117     }
118 
test_audioTrack_preferredDevice()119     public void test_audioTrack_preferredDevice() {
120         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
121             // Can't do it so skip this test
122             return;
123         }
124 
125         AudioTrack audioTrack = allocAudioTrack();
126         assertNotNull(audioTrack);
127 
128         // None selected (new AudioTrack), so check for default
129         assertNull(audioTrack.getPreferredDevice());
130 
131         // resets to default
132         assertTrue(audioTrack.setPreferredDevice(null));
133 
134         // test each device
135         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
136         for (int index = 0; index < deviceList.length; index++) {
137             if (deviceList[index].getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
138                 // Device with type as TYPE_TELEPHONY requires a privileged permission.
139                 continue;
140             }
141             assertTrue(audioTrack.setPreferredDevice(deviceList[index]));
142             assertTrue(audioTrack.getPreferredDevice() == deviceList[index]);
143         }
144 
145         // Check defaults again
146         assertTrue(audioTrack.setPreferredDevice(null));
147         assertNull(audioTrack.getPreferredDevice());
148 
149         audioTrack.release();
150     }
151 
test_audioTrack_incallMusicRoutingPermissions()152     public void test_audioTrack_incallMusicRoutingPermissions() {
153         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
154             // Can't do it so skip this test
155             return;
156         }
157 
158         // only apps with MODIFY_PHONE_STATE permission can route playback
159         // to the uplink stream during a phone call, so this test makes sure that
160         // audio is re-routed to default device when the permission is missing
161 
162         AudioDeviceInfo telephonyDevice = getTelephonyDeviceAndSetInCommunicationMode();
163         if (telephonyDevice == null) {
164             // Can't do it so skip this test
165             return;
166         }
167 
168         AudioTrack audioTrack = null;
169 
170         try {
171             audioTrack = allocAudioTrack();
172             assertNotNull(audioTrack);
173 
174             audioTrack.setPreferredDevice(telephonyDevice);
175             assertEquals(AudioDeviceInfo.TYPE_TELEPHONY, audioTrack.getPreferredDevice().getType());
176 
177             audioTrack.play();
178             assertTrue(audioTrack.getRoutedDevice().getType() != AudioDeviceInfo.TYPE_TELEPHONY);
179 
180         } finally {
181             if (audioTrack != null) {
182                 audioTrack.stop();
183                 audioTrack.release();
184             }
185             mAudioManager.setMode(AudioManager.MODE_NORMAL);
186         }
187     }
188 
getTelephonyDeviceAndSetInCommunicationMode()189     private AudioDeviceInfo getTelephonyDeviceAndSetInCommunicationMode() {
190         // get the output device for telephony
191         AudioDeviceInfo telephonyDevice = null;
192         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
193         for (int index = 0; index < deviceList.length; index++) {
194             if (deviceList[index].getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
195                 telephonyDevice = deviceList[index];
196             }
197         }
198 
199         if (telephonyDevice == null) {
200             return null;
201         }
202 
203         // simulate an in call state using MODE_IN_COMMUNICATION since
204         // AudioManager.setMode requires MODIFY_PHONE_STATE permission
205         // for setMode with MODE_IN_CALL.
206         mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
207         assertEquals(AudioManager.MODE_IN_COMMUNICATION, mAudioManager.getMode());
208 
209         return telephonyDevice;
210     }
211 
212     /*
213      * tests if the Looper for the current thread has been prepared,
214      * If not, it makes one, prepares it and returns it.
215      * If this returns non-null, the caller is reponsible for calling quit()
216      * on the returned Looper.
217      */
prepareIfNeededLooper()218     private Looper prepareIfNeededLooper() {
219         // non-null Handler
220         Looper myLooper = null;
221         if (Looper.myLooper() == null) {
222             Looper.prepare();
223             myLooper = Looper.myLooper();
224             assertNotNull(myLooper);
225         }
226         return myLooper;
227     }
228 
229     private class AudioTrackRoutingListener implements AudioTrack.OnRoutingChangedListener,
230             AudioRouting.OnRoutingChangedListener
231     {
onRoutingChanged(AudioTrack audioTrack)232         public void onRoutingChanged(AudioTrack audioTrack) {}
onRoutingChanged(AudioRouting audioRouting)233         public void onRoutingChanged(AudioRouting audioRouting) {}
234     }
235 
236 
test_audioTrack_RoutingListener()237     public void test_audioTrack_RoutingListener() {
238         test_audioTrack_RoutingListener(false /*usesAudioRouting*/);
239     }
240 
test_audioTrack_audioRouting_RoutingListener()241     public void test_audioTrack_audioRouting_RoutingListener() {
242         test_audioTrack_RoutingListener(true /*usesAudioRouting*/);
243     }
244 
test_audioTrack_RoutingListener(boolean usesAudioRouting)245     private void test_audioTrack_RoutingListener(boolean usesAudioRouting) {
246         AudioTrack audioTrack = allocAudioTrack();
247 
248         // null listener
249         if (usesAudioRouting) {
250             audioTrack.addOnRoutingChangedListener(
251                     (AudioRouting.OnRoutingChangedListener) null, null);
252         } else {
253             audioTrack.addOnRoutingChangedListener(
254                     (AudioTrack.OnRoutingChangedListener) null, null);
255         }
256 
257         AudioTrackRoutingListener listener = new AudioTrackRoutingListener();
258         AudioTrackRoutingListener someOtherListener = new AudioTrackRoutingListener();
259 
260         // add a listener
261         if (usesAudioRouting) {
262             audioTrack.addOnRoutingChangedListener(
263                     (AudioRouting.OnRoutingChangedListener) listener, null);
264         } else {
265             audioTrack.addOnRoutingChangedListener(listener, null);
266         }
267 
268         // remove listeners
269         if (usesAudioRouting) {
270             // remove a listener we didn't add
271             audioTrack.removeOnRoutingChangedListener(
272                     (AudioRouting.OnRoutingChangedListener) someOtherListener);
273             // remove a valid listener
274             audioTrack.removeOnRoutingChangedListener(
275                     (AudioRouting.OnRoutingChangedListener) listener);
276         } else {
277             // remove a listener we didn't add
278             audioTrack.removeOnRoutingChangedListener(
279                     (AudioTrack.OnRoutingChangedListener) someOtherListener);
280             // remove a valid listener
281             audioTrack.removeOnRoutingChangedListener(
282                     (AudioTrack.OnRoutingChangedListener) listener);
283         }
284 
285         Looper myLooper = prepareIfNeededLooper();
286 
287         if (usesAudioRouting) {
288             audioTrack.addOnRoutingChangedListener(
289                     (AudioRouting.OnRoutingChangedListener) listener, new Handler());
290             audioTrack.removeOnRoutingChangedListener(
291                     (AudioRouting.OnRoutingChangedListener) listener);
292         } else {
293             audioTrack.addOnRoutingChangedListener(
294                     (AudioTrack.OnRoutingChangedListener) listener, new Handler());
295             audioTrack.removeOnRoutingChangedListener(
296                     (AudioTrack.OnRoutingChangedListener) listener);
297         }
298 
299         audioTrack.release();
300         if (myLooper != null) {
301             myLooper.quit();
302         }
303    }
304 
allocAudioRecord()305     private AudioRecord allocAudioRecord() {
306         int bufferSize =
307                 AudioRecord.getMinBufferSize(
308                     41000,
309                     AudioFormat.CHANNEL_OUT_DEFAULT,
310                     AudioFormat.ENCODING_PCM_16BIT);
311         AudioRecord audioRecord =
312             new AudioRecord(
313                 MediaRecorder.AudioSource.DEFAULT,
314                 41000, AudioFormat.CHANNEL_OUT_DEFAULT,
315                 AudioFormat.ENCODING_PCM_16BIT,
316                 bufferSize);
317         return audioRecord;
318     }
319 
320     private class AudioRecordRoutingListener implements AudioRecord.OnRoutingChangedListener,
321             AudioRouting.OnRoutingChangedListener
322     {
onRoutingChanged(AudioRecord audioRecord)323         public void onRoutingChanged(AudioRecord audioRecord) {}
onRoutingChanged(AudioRouting audioRouting)324         public void onRoutingChanged(AudioRouting audioRouting) {}
325     }
326 
test_audioRecord_RoutingListener()327     public void test_audioRecord_RoutingListener() {
328         test_audioRecord_RoutingListener(false /*usesAudioRouting*/);
329     }
330 
test_audioRecord_audioRouting_RoutingListener()331     public void test_audioRecord_audioRouting_RoutingListener() {
332         test_audioRecord_RoutingListener(true /*usesAudioRouting*/);
333     }
334 
test_audioRecord_RoutingListener(boolean usesAudioRouting)335     private void test_audioRecord_RoutingListener(boolean usesAudioRouting) {
336         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
337             // Can't do it so skip this test
338             return;
339         }
340         AudioRecord audioRecord = allocAudioRecord();
341 
342         // null listener
343         if (usesAudioRouting) {
344             audioRecord.addOnRoutingChangedListener(
345                     (AudioRouting.OnRoutingChangedListener) null, null);
346         } else {
347             audioRecord.addOnRoutingChangedListener(
348                     (AudioRecord.OnRoutingChangedListener) null, null);
349         }
350 
351         AudioRecordRoutingListener listener = new AudioRecordRoutingListener();
352         AudioRecordRoutingListener someOtherListener = new AudioRecordRoutingListener();
353 
354         // add a listener
355         if (usesAudioRouting) {
356             audioRecord.addOnRoutingChangedListener(
357                     (AudioRouting.OnRoutingChangedListener) listener, null);
358         } else {
359             audioRecord.addOnRoutingChangedListener(
360                     (AudioRecord.OnRoutingChangedListener) listener, null);
361         }
362 
363         // remove listeners
364         if (usesAudioRouting) {
365             // remove a listener we didn't add
366             audioRecord.removeOnRoutingChangedListener(
367                     (AudioRouting.OnRoutingChangedListener) someOtherListener);
368             // remove a valid listener
369             audioRecord.removeOnRoutingChangedListener(
370                     (AudioRouting.OnRoutingChangedListener) listener);
371         } else {
372             // remove a listener we didn't add
373             audioRecord.removeOnRoutingChangedListener(
374                     (AudioRecord.OnRoutingChangedListener) someOtherListener);
375             // remove a valid listener
376             audioRecord.removeOnRoutingChangedListener(
377                     (AudioRecord.OnRoutingChangedListener) listener);
378         }
379 
380         Looper myLooper = prepareIfNeededLooper();
381         if (usesAudioRouting) {
382             audioRecord.addOnRoutingChangedListener(
383                     (AudioRouting.OnRoutingChangedListener) listener, new Handler());
384             audioRecord.removeOnRoutingChangedListener(
385                     (AudioRouting.OnRoutingChangedListener) listener);
386         } else {
387             audioRecord.addOnRoutingChangedListener(
388                     (AudioRecord.OnRoutingChangedListener) listener, new Handler());
389             audioRecord.removeOnRoutingChangedListener(
390                     (AudioRecord.OnRoutingChangedListener) listener);
391         }
392 
393         audioRecord.release();
394         if (myLooper != null) {
395             myLooper.quit();
396         }
397     }
398 
test_audioRecord_preferredDevice()399     public void test_audioRecord_preferredDevice() {
400         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
401             // Can't do it so skip this test
402             return;
403         }
404 
405         AudioRecord audioRecord = allocAudioRecord();
406         assertNotNull(audioRecord);
407 
408         // None selected (new AudioRecord), so check for default
409         assertNull(audioRecord.getPreferredDevice());
410 
411         // resets to default
412         assertTrue(audioRecord.setPreferredDevice(null));
413 
414         // test each device
415         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
416         for (int index = 0; index < deviceList.length; index++) {
417             assertTrue(audioRecord.setPreferredDevice(deviceList[index]));
418             assertTrue(audioRecord.getPreferredDevice() == deviceList[index]);
419         }
420 
421         // Check defaults again
422         assertTrue(audioRecord.setPreferredDevice(null));
423         assertNull(audioRecord.getPreferredDevice());
424 
425         audioRecord.release();
426     }
427 
428     private class AudioTrackFiller implements Runnable {
429         AudioTrack mAudioTrack;
430         int mBufferSize;
431 
432         boolean mPlaying;
433 
434         short[] mAudioData;
435 
AudioTrackFiller(AudioTrack audioTrack, int bufferSize)436         public AudioTrackFiller(AudioTrack audioTrack, int bufferSize) {
437             mAudioTrack = audioTrack;
438             mBufferSize = bufferSize;
439             mPlaying = false;
440 
441             // setup audio data (silence will suffice)
442             mAudioData = new short[mBufferSize];
443             for (int index = 0; index < mBufferSize; index++) {
444                 mAudioData[index] = 0;
445             }
446         }
447 
start()448         public void start() { mPlaying = true; }
stop()449         public void stop() { mPlaying = false; }
450 
451         @Override
run()452         public void run() {
453             while (mAudioTrack != null && mPlaying) {
454                 mAudioTrack.write(mAudioData, 0, mBufferSize);
455             }
456         }
457     }
458 
test_audioTrack_getRoutedDevice()459     public void test_audioTrack_getRoutedDevice() throws Exception {
460         if (!DeviceUtils.hasOutputDevice(mAudioManager)) {
461             Log.i(TAG, "No output devices. Test skipped");
462             return; // nothing to test here
463         }
464 
465         int bufferSize =
466                 AudioTrack.getMinBufferSize(
467                     41000,
468                     AudioFormat.CHANNEL_OUT_STEREO,
469                     AudioFormat.ENCODING_PCM_16BIT);
470         AudioTrack audioTrack =
471             new AudioTrack(
472                 AudioManager.STREAM_MUSIC,
473                 41000,
474                 AudioFormat.CHANNEL_OUT_STEREO,
475                 AudioFormat.ENCODING_PCM_16BIT,
476                 bufferSize,
477                 AudioTrack.MODE_STREAM);
478 
479         AudioTrackFiller filler = new AudioTrackFiller(audioTrack, bufferSize);
480         filler.start();
481 
482         audioTrack.play();
483 
484         Thread fillerThread = new Thread(filler);
485         fillerThread.start();
486 
487         assertHasNonNullRoutedDevice(audioTrack);
488 
489         filler.stop();
490         audioTrack.stop();
491         audioTrack.release();
492     }
493 
assertHasNonNullRoutedDevice(AudioRouting router)494     private void assertHasNonNullRoutedDevice(AudioRouting router) throws Exception {
495         AudioDeviceInfo routedDevice = null;
496         // Give a chance for playback or recording to start so routing can be established
497         final long timeouts[] = { 100, 200, 300, 500, 1000};
498         int attempt = 0;
499         long totalWait = 0;
500         do {
501             totalWait += timeouts[attempt];
502             try { Thread.sleep(timeouts[attempt++]); } catch (InterruptedException ex) {}
503             routedDevice = router.getRoutedDevice();
504             if (routedDevice == null && (attempt > 2 || totalWait >= 1000)) {
505                 Log.w(TAG, "Routing still not reported after " + totalWait + "ms");
506             }
507         } while (routedDevice == null && attempt < timeouts.length);
508         assertNotNull(routedDevice); // we probably can't say anything more than this
509     }
510 
511     private class AudioRecordPuller implements Runnable {
512         AudioRecord mAudioRecord;
513         int mBufferSize;
514 
515         boolean mRecording;
516 
517         short[] mAudioData;
518 
AudioRecordPuller(AudioRecord audioRecord, int bufferSize)519         public AudioRecordPuller(AudioRecord audioRecord, int bufferSize) {
520             mAudioRecord = audioRecord;
521             mBufferSize = bufferSize;
522             mRecording = false;
523         }
524 
start()525         public void start() { mRecording = true; }
stop()526         public void stop() { mRecording = false; }
527 
528         @Override
run()529         public void run() {
530             while (mAudioRecord != null && mRecording) {
531                 mAudioRecord.read(mAudioData, 0, mBufferSize);
532            }
533         }
534     }
535 
test_audioRecord_getRoutedDevice()536     public void test_audioRecord_getRoutedDevice() throws Exception {
537         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
538             return;
539         }
540 
541         if (!DeviceUtils.hasInputDevice(mAudioManager)) {
542             Log.i(TAG, "No input devices. Test skipped");
543             return; // nothing to test here
544         }
545 
546         int bufferSize =
547                 AudioRecord.getMinBufferSize(
548                     41000,
549                     AudioFormat.CHANNEL_OUT_DEFAULT,
550                     AudioFormat.ENCODING_PCM_16BIT);
551         AudioRecord audioRecord =
552             new AudioRecord(
553                 MediaRecorder.AudioSource.DEFAULT,
554                 41000, AudioFormat.CHANNEL_OUT_DEFAULT,
555                 AudioFormat.ENCODING_PCM_16BIT,
556                 bufferSize);
557 
558         AudioRecordPuller puller = new AudioRecordPuller(audioRecord, bufferSize);
559         puller.start();
560 
561         audioRecord.startRecording();
562 
563         Thread pullerThread = new Thread(puller);
564         pullerThread.start();
565 
566         assertHasNonNullRoutedDevice(audioRecord);
567 
568         puller.stop();
569         audioRecord.stop();
570         audioRecord.release();
571     }
572 
573     static class AudioRoutingListener implements AudioRouting.OnRoutingChangedListener
574     {
575         private boolean mCalled;
576         private boolean mCallExpected;
577         private CountDownLatch mCountDownLatch;
578 
AudioRoutingListener()579         AudioRoutingListener() {
580             reset();
581         }
582 
onRoutingChanged(AudioRouting audioRouting)583         public void onRoutingChanged(AudioRouting audioRouting) {
584             mCalled = true;
585             mCountDownLatch.countDown();
586         }
587 
await(long timeoutMs)588         void await(long timeoutMs) {
589             try {
590                 mCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
591             } catch (InterruptedException e) {
592             }
593         }
594 
setCallExpected(boolean flag)595         void setCallExpected(boolean flag) {
596             mCallExpected = flag;
597         }
598 
isCallExpected()599         boolean isCallExpected() {
600             return mCallExpected;
601         }
602 
isRoutingListenerCalled()603         boolean isRoutingListenerCalled() {
604             return mCalled;
605         }
606 
reset()607         void reset() {
608             mCountDownLatch = new CountDownLatch(1);
609             mCalled = false;
610             mCallExpected = true;
611         }
612     }
613 
allocMediaPlayer()614     private MediaPlayer allocMediaPlayer() {
615         return allocMediaPlayer(null, true);
616     }
617 
allocMediaPlayer(AudioDeviceInfo device, boolean start)618     private MediaPlayer allocMediaPlayer(AudioDeviceInfo device, boolean start) {
619         final String res = "testmp3_2.mp3";
620         Preconditions.assertTestFileExists(mInpPrefix + res);
621         MediaPlayer mediaPlayer = MediaPlayer.create(mContext, Uri
622                 .fromFile(new File(mInpPrefix + res)));
623         mediaPlayer.setAudioAttributes(
624                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
625         if (device != null) {
626             mediaPlayer.setPreferredDevice(device);
627         }
628         if (start) {
629             mediaPlayer.start();
630         }
631         return mediaPlayer;
632     }
633 
test_mediaPlayer_preferredDevice()634     public void test_mediaPlayer_preferredDevice() {
635         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
636             // Can't do it so skip this test
637             return;
638         }
639 
640         MediaPlayer mediaPlayer = allocMediaPlayer();
641         assertTrue(mediaPlayer.isPlaying());
642 
643         // None selected (new MediaPlayer), so check for default
644         assertNull(mediaPlayer.getPreferredDevice());
645 
646         // resets to default
647         assertTrue(mediaPlayer.setPreferredDevice(null));
648 
649         // test each device
650         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
651         for (int index = 0; index < deviceList.length; index++) {
652             if (deviceList[index].getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
653                 // Device with type as TYPE_TELEPHONY requires a privileged permission.
654                 continue;
655             }
656             assertTrue(mediaPlayer.setPreferredDevice(deviceList[index]));
657             assertTrue(mediaPlayer.getPreferredDevice() == deviceList[index]);
658         }
659 
660         // Check defaults again
661         assertTrue(mediaPlayer.setPreferredDevice(null));
662         assertNull(mediaPlayer.getPreferredDevice());
663 
664         mediaPlayer.stop();
665         mediaPlayer.release();
666     }
667 
test_mediaPlayer_getRoutedDevice()668     public void test_mediaPlayer_getRoutedDevice() throws Exception {
669         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
670             // Can't do it so skip this test
671             return;
672         }
673 
674         MediaPlayer mediaPlayer = allocMediaPlayer();
675         assertTrue(mediaPlayer.isPlaying());
676 
677         assertHasNonNullRoutedDevice(mediaPlayer);
678 
679         mediaPlayer.stop();
680         mediaPlayer.release();
681     }
682 
test_MediaPlayer_RoutingListener()683     public void test_MediaPlayer_RoutingListener() {
684         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
685             // Can't do it so skip this test
686             return;
687         }
688 
689         MediaPlayer mediaPlayer = allocMediaPlayer();
690 
691         // null listener
692         mediaPlayer.addOnRoutingChangedListener(null, null);
693 
694         AudioRoutingListener listener = new AudioRoutingListener();
695         AudioRoutingListener someOtherListener = new AudioRoutingListener();
696 
697         // add a listener
698         mediaPlayer.addOnRoutingChangedListener(listener, null);
699 
700         // remove listeners
701         // remove a listener we didn't add
702         mediaPlayer.removeOnRoutingChangedListener(someOtherListener);
703         // remove a valid listener
704         mediaPlayer.removeOnRoutingChangedListener(listener);
705 
706         Looper myLooper = prepareIfNeededLooper();
707 
708         mediaPlayer.addOnRoutingChangedListener(listener, new Handler());
709         mediaPlayer.removeOnRoutingChangedListener(listener);
710 
711         mediaPlayer.stop();
712         mediaPlayer.release();
713         if (myLooper != null) {
714             myLooper.quit();
715         }
716     }
717 
test_MediaPlayer_RoutingChangedCallback()718     public void test_MediaPlayer_RoutingChangedCallback() throws Exception {
719         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
720             // Can't do it so skip this test
721             return;
722         }
723 
724         AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
725         if (devices.length < 2) {
726             // In this case, we cannot switch output device, that may cause the test fail.
727             return;
728         }
729 
730         AudioRoutingListener listener = new AudioRoutingListener();
731         MediaPlayer mediaPlayer = allocMediaPlayer(null, false);
732         mediaPlayer.addOnRoutingChangedListener(listener, null);
733         mediaPlayer.start();
734         try {
735             // Wait a second so that the player
736             Thread.sleep(WAIT_PLAYBACK_START_TIME_MS);
737         } catch (Exception e) {
738         }
739 
740         AudioDeviceInfo routedDevice = mediaPlayer.getRoutedDevice();
741         assertTrue("Routed device should not be null", routedDevice != null);
742 
743         // Reset the routing listener as the listener is called to notify the routed device
744         // when the playback starts.
745         listener.await(WAIT_ROUTING_CHANGE_TIME_MS);
746         assertTrue("Routing changed callback has not been called when starting playback",
747                 listener.isRoutingListenerCalled());
748         listener.reset();
749 
750         listener.setCallExpected(false);
751         for (AudioDeviceInfo device : devices) {
752             if (routedDevice.getId() != device.getId() &&
753                     device.getType() != AudioDeviceInfo.TYPE_TELEPHONY) {
754                 mediaPlayer.setPreferredDevice(device);
755                 listener.setCallExpected(true);
756                 listener.await(WAIT_ROUTING_CHANGE_TIME_MS);
757                 break;
758             }
759         }
760 
761         mediaPlayer.removeOnRoutingChangedListener(listener);
762         mediaPlayer.stop();
763         mediaPlayer.release();
764 
765         if (listener.isCallExpected()) {
766             assertTrue("Routing changed callback has not been called",
767                     listener.isRoutingListenerCalled());
768         }
769     }
770 
test_mediaPlayer_incallMusicRoutingPermissions()771     public void test_mediaPlayer_incallMusicRoutingPermissions() {
772         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
773             // Can't do it so skip this test
774             return;
775         }
776 
777         // only apps with MODIFY_PHONE_STATE permission can route playback
778         // to the uplink stream during a phone call, so this test makes sure that
779         // audio is re-routed to default device when the permission is missing
780 
781         AudioDeviceInfo telephonyDevice = getTelephonyDeviceAndSetInCommunicationMode();
782         if (telephonyDevice == null) {
783             // Can't do it so skip this test
784             return;
785         }
786 
787         MediaPlayer mediaPlayer = null;
788 
789         try {
790             mediaPlayer = allocMediaPlayer(telephonyDevice, false);
791             assertEquals(AudioDeviceInfo.TYPE_TELEPHONY, mediaPlayer.getPreferredDevice().getType());
792             mediaPlayer.start();
793             // Sleep for 1s to ensure the underlying AudioTrack is created and started
794             SystemClock.sleep(1000);
795             telephonyDevice = mediaPlayer.getRoutedDevice();
796             // 3 behaviors are accepted when permission to play to telephony device is rejected:
797             // - indicate a null routed device
798             // - fallback to another device for playback
799             // - stop playback in error.
800             assertTrue(telephonyDevice == null
801                     || telephonyDevice.getType() != AudioDeviceInfo.TYPE_TELEPHONY
802                     || !mediaPlayer.isPlaying());
803         } finally {
804             if (mediaPlayer != null) {
805                 mediaPlayer.stop();
806                 mediaPlayer.release();
807             }
808             mAudioManager.setMode(AudioManager.MODE_NORMAL);
809         }
810     }
811 
allocMediaRecorder()812     private MediaRecorder allocMediaRecorder() throws Exception {
813         final String outputPath = new File(Environment.getExternalStorageDirectory(),
814             "record.out").getAbsolutePath();
815         mOutFile = new File(outputPath);
816         MediaRecorder mediaRecorder = new MediaRecorder();
817         mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
818         assertEquals(0, mediaRecorder.getMaxAmplitude());
819         mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
820         mediaRecorder.setOutputFile(outputPath);
821         mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
822         mediaRecorder.setAudioChannels(AudioFormat.CHANNEL_OUT_DEFAULT);
823         mediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
824         mediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS);
825         mediaRecorder.setMaxFileSize(MAX_FILE_SIZE_BYTE);
826         mediaRecorder.prepare();
827         mediaRecorder.start();
828         // Sleep a while to ensure the underlying AudioRecord is initialized.
829         Thread.sleep(1000);
830         return mediaRecorder;
831     }
832 
test_mediaRecorder_preferredDevice()833     public void test_mediaRecorder_preferredDevice() throws Exception {
834         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
835                 || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
836             MediaUtils.skipTest("no audio codecs or microphone");
837             return;
838         }
839 
840         MediaRecorder mediaRecorder = allocMediaRecorder();
841 
842         // None selected (new MediaPlayer), so check for default
843         assertNull(mediaRecorder.getPreferredDevice());
844 
845         // resets to default
846         assertTrue(mediaRecorder.setPreferredDevice(null));
847 
848         // test each device
849         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
850         for (int index = 0; index < deviceList.length; index++) {
851             if (!AVAILABLE_INPUT_DEVICES_TYPE.contains(deviceList[index].getType())) {
852                 // Only try to set devices whose type is contained in predefined set as preferred
853                 // device in case of permission denied when switching input device.
854                 continue;
855             }
856             assertTrue(mediaRecorder.setPreferredDevice(deviceList[index]));
857             assertTrue(mediaRecorder.getPreferredDevice() == deviceList[index]);
858         }
859 
860         // Check defaults again
861         assertTrue(mediaRecorder.setPreferredDevice(null));
862         assertNull(mediaRecorder.getPreferredDevice());
863         Thread.sleep(RECORD_TIME_MS);
864 
865         mediaRecorder.stop();
866         mediaRecorder.release();
867     }
868 
test_mediaRecorder_getRoutedDeviceId()869     public void test_mediaRecorder_getRoutedDeviceId() throws Exception {
870         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
871             || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
872             MediaUtils.skipTest("no audio codecs or microphone");
873             return;
874         }
875 
876         MediaRecorder mediaRecorder = allocMediaRecorder();
877 
878         AudioDeviceInfo routedDevice = mediaRecorder.getRoutedDevice();
879         assertNotNull(routedDevice); // we probably can't say anything more than this
880         Thread.sleep(RECORD_TIME_MS);
881 
882         mediaRecorder.stop();
883         mediaRecorder.release();
884     }
885 
test_mediaRecorder_RoutingListener()886     public void test_mediaRecorder_RoutingListener() throws Exception {
887         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
888             || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
889             MediaUtils.skipTest("no audio codecs or microphone");
890             return;
891         }
892 
893         MediaRecorder mediaRecorder = allocMediaRecorder();
894 
895         // null listener
896         mediaRecorder.addOnRoutingChangedListener(null, null);
897 
898         AudioRoutingListener listener = new AudioRoutingListener();
899         AudioRoutingListener someOtherListener = new AudioRoutingListener();
900 
901         // add a listener
902         mediaRecorder.addOnRoutingChangedListener(listener, null);
903 
904         // remove listeners we didn't add
905         mediaRecorder.removeOnRoutingChangedListener(someOtherListener);
906         // remove a valid listener
907         mediaRecorder.removeOnRoutingChangedListener(listener);
908 
909         Looper myLooper = prepareIfNeededLooper();
910         mediaRecorder.addOnRoutingChangedListener(listener, new Handler());
911         mediaRecorder.removeOnRoutingChangedListener(listener);
912 
913         Thread.sleep(RECORD_TIME_MS);
914 
915         mediaRecorder.stop();
916         mediaRecorder.release();
917         if (myLooper != null) {
918             myLooper.quit();
919         }
920     }
921 }
922