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