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 mediaPlayer.pause(); 634 //Wait for state to change before setPreferDevice 635 SystemClock.sleep(200); 636 assertTrue(mediaPlayer.setPreferredDevice(null)); 637 mediaPlayer.start(); 638 assertTrue(mediaPlayer.isPlaying()); 639 640 // test each device 641 AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 642 for (int index = 0; index < deviceList.length; index++) { 643 if (deviceList[index].getType() == AudioDeviceInfo.TYPE_TELEPHONY) { 644 // Device with type as TYPE_TELEPHONY requires a privileged permission. 645 continue; 646 } 647 mediaPlayer.pause(); 648 //Wait for state to change before setPreferDevice 649 SystemClock.sleep(200); 650 assertTrue(mediaPlayer.setPreferredDevice(deviceList[index])); 651 mediaPlayer.start(); 652 assertTrue(mediaPlayer.isPlaying()); 653 assertTrue(mediaPlayer.getPreferredDevice() == deviceList[index]); 654 } 655 656 // Check defaults again 657 mediaPlayer.pause(); 658 //Wait for state to change before setPreferDevice 659 SystemClock.sleep(200); 660 assertTrue(mediaPlayer.setPreferredDevice(null)); 661 mediaPlayer.start(); 662 assertTrue(mediaPlayer.isPlaying()); 663 assertNull(mediaPlayer.getPreferredDevice()); 664 665 mediaPlayer.stop(); 666 mediaPlayer.release(); 667 } 668 test_mediaPlayer_getRoutedDevice()669 public void test_mediaPlayer_getRoutedDevice() throws Exception { 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 assertTrue(mediaPlayer.isPlaying()); 677 678 assertHasNonNullRoutedDevice(mediaPlayer); 679 680 mediaPlayer.stop(); 681 mediaPlayer.release(); 682 } 683 test_MediaPlayer_RoutingListener()684 public void test_MediaPlayer_RoutingListener() { 685 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) { 686 // Can't do it so skip this test 687 return; 688 } 689 690 MediaPlayer mediaPlayer = allocMediaPlayer(); 691 692 // null listener 693 mediaPlayer.addOnRoutingChangedListener(null, null); 694 695 AudioRoutingListener listener = new AudioRoutingListener(); 696 AudioRoutingListener someOtherListener = new AudioRoutingListener(); 697 698 // add a listener 699 mediaPlayer.addOnRoutingChangedListener(listener, null); 700 701 // remove listeners 702 // remove a listener we didn't add 703 mediaPlayer.removeOnRoutingChangedListener(someOtherListener); 704 // remove a valid listener 705 mediaPlayer.removeOnRoutingChangedListener(listener); 706 707 Looper myLooper = prepareIfNeededLooper(); 708 709 mediaPlayer.addOnRoutingChangedListener(listener, new Handler()); 710 mediaPlayer.removeOnRoutingChangedListener(listener); 711 712 mediaPlayer.stop(); 713 mediaPlayer.release(); 714 if (myLooper != null) { 715 myLooper.quit(); 716 } 717 } 718 test_MediaPlayer_RoutingChangedCallback()719 public void test_MediaPlayer_RoutingChangedCallback() throws Exception { 720 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) { 721 // Can't do it so skip this test 722 return; 723 } 724 725 AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 726 if (devices.length < 2) { 727 // In this case, we cannot switch output device, that may cause the test fail. 728 return; 729 } 730 731 AudioRoutingListener listener = new AudioRoutingListener(); 732 MediaPlayer mediaPlayer = allocMediaPlayer(null, false); 733 mediaPlayer.addOnRoutingChangedListener(listener, null); 734 mediaPlayer.start(); 735 try { 736 // Wait a second so that the player 737 Thread.sleep(WAIT_PLAYBACK_START_TIME_MS); 738 } catch (Exception e) { 739 } 740 741 AudioDeviceInfo routedDevice = mediaPlayer.getRoutedDevice(); 742 assertTrue("Routed device should not be null", routedDevice != null); 743 744 // Reset the routing listener as the listener is called to notify the routed device 745 // when the playback starts. 746 listener.await(WAIT_ROUTING_CHANGE_TIME_MS); 747 assertTrue("Routing changed callback has not been called when starting playback", 748 listener.isRoutingListenerCalled()); 749 listener.reset(); 750 751 listener.setCallExpected(false); 752 for (AudioDeviceInfo device : devices) { 753 if (routedDevice.getId() != device.getId() && 754 device.getType() != AudioDeviceInfo.TYPE_TELEPHONY) { 755 mediaPlayer.pause(); 756 //Wait for state to change before setPreferDevice 757 SystemClock.sleep(200); 758 mediaPlayer.setPreferredDevice(device); 759 mediaPlayer.start(); 760 assertTrue(mediaPlayer.isPlaying()); 761 listener.setCallExpected(true); 762 listener.await(WAIT_ROUTING_CHANGE_TIME_MS); 763 break; 764 } 765 } 766 767 mediaPlayer.removeOnRoutingChangedListener(listener); 768 mediaPlayer.stop(); 769 mediaPlayer.release(); 770 771 if (listener.isCallExpected()) { 772 assertTrue("Routing changed callback has not been called", 773 listener.isRoutingListenerCalled()); 774 } 775 } 776 test_mediaPlayer_incallMusicRoutingPermissions()777 public void test_mediaPlayer_incallMusicRoutingPermissions() { 778 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) { 779 // Can't do it so skip this test 780 return; 781 } 782 783 // only apps with MODIFY_PHONE_STATE permission can route playback 784 // to the uplink stream during a phone call, so this test makes sure that 785 // audio is re-routed to default device when the permission is missing 786 787 AudioDeviceInfo telephonyDevice = getTelephonyDeviceAndSetInCommunicationMode(); 788 if (telephonyDevice == null) { 789 // Can't do it so skip this test 790 return; 791 } 792 793 MediaPlayer mediaPlayer = null; 794 795 try { 796 mediaPlayer = allocMediaPlayer(telephonyDevice, false); 797 assertEquals(AudioDeviceInfo.TYPE_TELEPHONY, mediaPlayer.getPreferredDevice().getType()); 798 mediaPlayer.start(); 799 // Sleep for 1s to ensure the underlying AudioTrack is created and started 800 SystemClock.sleep(1000); 801 telephonyDevice = mediaPlayer.getRoutedDevice(); 802 // 3 behaviors are accepted when permission to play to telephony device is rejected: 803 // - indicate a null routed device 804 // - fallback to another device for playback 805 // - stop playback in error. 806 assertTrue(telephonyDevice == null 807 || telephonyDevice.getType() != AudioDeviceInfo.TYPE_TELEPHONY 808 || !mediaPlayer.isPlaying()); 809 } finally { 810 if (mediaPlayer != null) { 811 mediaPlayer.stop(); 812 mediaPlayer.release(); 813 } 814 mAudioManager.setMode(AudioManager.MODE_NORMAL); 815 } 816 } 817 allocMediaRecorder()818 private MediaRecorder allocMediaRecorder() throws Exception { 819 final String outputPath = new File(mContext.getExternalFilesDir(null), 820 "record.out").getAbsolutePath(); 821 mOutFile = new File(outputPath); 822 MediaRecorder mediaRecorder = new MediaRecorder(); 823 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 824 assertEquals(0, mediaRecorder.getMaxAmplitude()); 825 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 826 mediaRecorder.setOutputFile(outputPath); 827 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); 828 mediaRecorder.setAudioChannels(AudioFormat.CHANNEL_OUT_DEFAULT); 829 mediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ); 830 mediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS); 831 mediaRecorder.setMaxFileSize(MAX_FILE_SIZE_BYTE); 832 mediaRecorder.prepare(); 833 mediaRecorder.start(); 834 // Sleep a while to ensure the underlying AudioRecord is initialized. 835 Thread.sleep(1000); 836 return mediaRecorder; 837 } 838 test_mediaRecorder_preferredDevice()839 public void test_mediaRecorder_preferredDevice() throws Exception { 840 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE) 841 || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) { 842 MediaUtils.skipTest("no audio codecs or microphone"); 843 return; 844 } 845 846 MediaRecorder mediaRecorder = allocMediaRecorder(); 847 848 // None selected (new MediaPlayer), so check for default 849 assertNull(mediaRecorder.getPreferredDevice()); 850 851 // resets to default 852 assertTrue(mediaRecorder.setPreferredDevice(null)); 853 854 // test each device 855 AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS); 856 for (int index = 0; index < deviceList.length; index++) { 857 if (!AVAILABLE_INPUT_DEVICES_TYPE.contains(deviceList[index].getType())) { 858 // Only try to set devices whose type is contained in predefined set as preferred 859 // device in case of permission denied when switching input device. 860 continue; 861 } 862 assertTrue(mediaRecorder.setPreferredDevice(deviceList[index])); 863 assertTrue(mediaRecorder.getPreferredDevice() == deviceList[index]); 864 } 865 866 // Check defaults again 867 assertTrue(mediaRecorder.setPreferredDevice(null)); 868 assertNull(mediaRecorder.getPreferredDevice()); 869 Thread.sleep(RECORD_TIME_MS); 870 871 mediaRecorder.stop(); 872 mediaRecorder.release(); 873 } 874 test_mediaRecorder_getRoutedDeviceId()875 public void test_mediaRecorder_getRoutedDeviceId() throws Exception { 876 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE) 877 || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) { 878 MediaUtils.skipTest("no audio codecs or microphone"); 879 return; 880 } 881 882 MediaRecorder mediaRecorder = allocMediaRecorder(); 883 884 AudioDeviceInfo routedDevice = mediaRecorder.getRoutedDevice(); 885 assertNotNull(routedDevice); // we probably can't say anything more than this 886 Thread.sleep(RECORD_TIME_MS); 887 888 mediaRecorder.stop(); 889 mediaRecorder.release(); 890 } 891 test_mediaRecorder_RoutingListener()892 public void test_mediaRecorder_RoutingListener() throws Exception { 893 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE) 894 || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) { 895 MediaUtils.skipTest("no audio codecs or microphone"); 896 return; 897 } 898 899 MediaRecorder mediaRecorder = allocMediaRecorder(); 900 901 // null listener 902 mediaRecorder.addOnRoutingChangedListener(null, null); 903 904 AudioRoutingListener listener = new AudioRoutingListener(); 905 AudioRoutingListener someOtherListener = new AudioRoutingListener(); 906 907 // add a listener 908 mediaRecorder.addOnRoutingChangedListener(listener, null); 909 910 // remove listeners we didn't add 911 mediaRecorder.removeOnRoutingChangedListener(someOtherListener); 912 // remove a valid listener 913 mediaRecorder.removeOnRoutingChangedListener(listener); 914 915 Looper myLooper = prepareIfNeededLooper(); 916 mediaRecorder.addOnRoutingChangedListener(listener, new Handler()); 917 mediaRecorder.removeOnRoutingChangedListener(listener); 918 919 Thread.sleep(RECORD_TIME_MS); 920 921 mediaRecorder.stop(); 922 mediaRecorder.release(); 923 if (myLooper != null) { 924 myLooper.quit(); 925 } 926 } 927 } 928