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