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