1 /* 2 * Copyright (C) 2019 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 static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL; 20 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE; 21 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM; 22 23 import static org.hamcrest.Matchers.greaterThan; 24 import static org.hamcrest.Matchers.lessThan; 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertNotSame; 28 import static org.junit.Assert.assertThat; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 import static org.testng.Assert.assertThrows; 32 33 import static java.util.stream.Collectors.toSet; 34 35 import android.media.AudioAttributes; 36 import android.media.AudioAttributes.AttributeUsage; 37 import android.media.AudioAttributes.CapturePolicy; 38 import android.media.AudioDeviceInfo; 39 import android.media.AudioFormat; 40 import android.media.AudioManager; 41 import android.media.AudioPlaybackCaptureConfiguration; 42 import android.media.AudioRecord; 43 import android.media.MediaPlayer; 44 import android.media.cts.MediaProjectionActivity; 45 import android.media.projection.MediaProjection; 46 import android.os.Handler; 47 import android.os.Looper; 48 import android.platform.test.annotations.AppModeNonSdkSandbox; 49 import android.platform.test.annotations.Presubmit; 50 51 import androidx.test.rule.ActivityTestRule; 52 53 import com.android.compatibility.common.util.NonMainlineTest; 54 55 import org.junit.Before; 56 import org.junit.Rule; 57 import org.junit.Test; 58 59 import java.nio.ByteBuffer; 60 import java.nio.ShortBuffer; 61 import java.util.Arrays; 62 import java.util.Set; 63 import java.util.Stack; 64 import java.util.concurrent.CountDownLatch; 65 import java.util.concurrent.TimeUnit; 66 67 /** 68 * Test audio playback capture through MediaProjection. 69 * 70 * The tests do the following: 71 * - retrieve a MediaProjection through MediaProjectionActivity 72 * - play some audio 73 * - use that MediaProjection to record the audio playing 74 * - check that some audio was recorded. 75 * 76 * Currently the test that some audio was recorded just check that at least one sample is non 0. 77 * A better check needs to be used, eg: compare the power spectrum. 78 */ 79 @NonMainlineTest 80 @AppModeNonSdkSandbox(reason = "The sandbox cannot retrieve MediaProjection.") 81 public class AudioPlaybackCaptureTest { 82 private static final String TAG = "AudioPlaybackCaptureTest"; 83 private static final int SAMPLE_RATE = 44100; 84 private static final int BUFFER_SIZE = SAMPLE_RATE * 2; // 1s at 44.1k/s 16bit mono 85 86 private AudioManager mAudioManager; 87 private boolean mPlaybackBeforeCapture; 88 private int mUid; //< UID of this test 89 private MediaProjectionActivity mActivity; 90 private MediaProjection mMediaProjection; 91 @Rule 92 public ActivityTestRule<MediaProjectionActivity> mActivityRule = 93 new ActivityTestRule<>(MediaProjectionActivity.class); 94 95 private static class APCTestConfig { 96 public @AttributeUsage int[] matchingUsages; 97 public @AttributeUsage int[] excludeUsages; 98 public int[] matchingUids; 99 public int[] excludeUids; build(MediaProjection projection)100 private AudioPlaybackCaptureConfiguration build(MediaProjection projection) 101 throws Exception { 102 AudioPlaybackCaptureConfiguration.Builder apccBuilder = 103 new AudioPlaybackCaptureConfiguration.Builder(projection); 104 105 if (matchingUsages != null) { 106 for (int usage : matchingUsages) { 107 apccBuilder.addMatchingUsage(usage); 108 } 109 } 110 if (excludeUsages != null) { 111 for (int usage : excludeUsages) { 112 apccBuilder.excludeUsage(usage); 113 } 114 } 115 if (matchingUids != null) { 116 for (int uid : matchingUids) { 117 apccBuilder.addMatchingUid(uid); 118 } 119 } 120 if (excludeUids != null) { 121 for (int uid : excludeUids) { 122 apccBuilder.excludeUid(uid); 123 } 124 } 125 AudioPlaybackCaptureConfiguration config = apccBuilder.build(); 126 assertCorreclyBuilt(config); 127 return config; 128 } 129 assertCorreclyBuilt(AudioPlaybackCaptureConfiguration config)130 private void assertCorreclyBuilt(AudioPlaybackCaptureConfiguration config) { 131 assertEquals("matchingUsages", 132 arraytoSet(matchingUsages), arraytoSet(config.getMatchingUsages())); 133 assertEquals("excludeUsages", 134 arraytoSet(excludeUsages), arraytoSet(config.getExcludeUsages())); 135 assertEquals("matchingUids", 136 arraytoSet(matchingUids), arraytoSet(config.getMatchingUids())); 137 assertEquals("excludeUids", 138 arraytoSet(excludeUids), arraytoSet(config.getExcludeUids())); 139 } 140 arraytoSet(int[] array)141 private static Set<Integer> arraytoSet(int[] array) { 142 return array == null ? Set.of() : Arrays.stream(array).boxed().collect(toSet()); 143 } 144 }; 145 private APCTestConfig mAPCTestConfig; 146 147 @Before setup()148 public void setup() throws Exception { 149 mPlaybackBeforeCapture = false; 150 mAPCTestConfig = new APCTestConfig(); 151 mActivity = mActivityRule.getActivity(); 152 mAudioManager = mActivity.getSystemService(AudioManager.class); 153 mUid = mActivity.getApplicationInfo().uid; 154 mMediaProjection = mActivity.waitForMediaProjection(); 155 } 156 createDefaultPlaybackCaptureRecord()157 private AudioRecord createDefaultPlaybackCaptureRecord() throws Exception { 158 return createPlaybackCaptureRecord( 159 new AudioFormat.Builder() 160 .setEncoding(AudioFormat.ENCODING_PCM_16BIT) 161 .setSampleRate(SAMPLE_RATE) 162 .setChannelMask(AudioFormat.CHANNEL_IN_MONO) 163 .build()); 164 } 165 createPlaybackCaptureRecord(AudioFormat audioFormat)166 private AudioRecord createPlaybackCaptureRecord(AudioFormat audioFormat) throws Exception { 167 AudioPlaybackCaptureConfiguration apcConfig = mAPCTestConfig.build(mMediaProjection); 168 169 AudioRecord audioRecord = new AudioRecord.Builder() 170 .setAudioPlaybackCaptureConfig(apcConfig) 171 .setAudioFormat(audioFormat) 172 .build(); 173 assertEquals("AudioRecord failed to initialized", AudioRecord.STATE_INITIALIZED, 174 audioRecord.getState()); 175 return audioRecord; 176 } 177 createMediaPlayer(@apturePolicy int capturePolicy, int resid, @AttributeUsage int usage)178 private MediaPlayer createMediaPlayer(@CapturePolicy int capturePolicy, int resid, 179 @AttributeUsage int usage) { 180 MediaPlayer mediaPlayer = MediaPlayer.create( 181 mActivity, 182 resid, 183 new AudioAttributes.Builder() 184 .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) 185 .setUsage(usage) 186 .setAllowedCapturePolicy(capturePolicy) 187 .build(), 188 mAudioManager.generateAudioSessionId()); 189 mediaPlayer.setLooping(true); 190 return mediaPlayer; 191 } 192 readToBuffer(AudioRecord audioRecord, int bufferSize)193 private static ByteBuffer readToBuffer(AudioRecord audioRecord, int bufferSize) 194 throws Exception { 195 assertEquals("AudioRecord is not recording", AudioRecord.RECORDSTATE_RECORDING, 196 audioRecord.getRecordingState()); 197 ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize); 198 int retry = 100; 199 boolean silence = true; 200 while (silence && buffer.hasRemaining()) { 201 assertNotSame(buffer.remaining() + "/" + bufferSize + "remaining", 0, retry--); 202 int written = audioRecord.read(buffer, buffer.remaining()); 203 assertThat("audioRecord did not read frames", written, greaterThan(0)); 204 for (int i = 0; i < written; i++) { 205 silence &= buffer.get() == 0; 206 } 207 } 208 buffer.rewind(); 209 return buffer; 210 } 211 onlySilence(ShortBuffer buffer)212 private static boolean onlySilence(ShortBuffer buffer) { 213 while (buffer.hasRemaining()) { 214 if (buffer.get() != 0) { 215 return false; 216 } 217 } 218 return true; 219 } 220 testPlaybackCapture(@apturePolicy int capturePolicy, @AttributeUsage int playbackUsage, boolean dataPresent)221 public void testPlaybackCapture(@CapturePolicy int capturePolicy, 222 @AttributeUsage int playbackUsage, 223 boolean dataPresent) throws Exception { 224 MediaPlayer mediaPlayer = createMediaPlayer(capturePolicy, R.raw.testwav_16bit_44100hz, 225 playbackUsage); 226 try { 227 if (mPlaybackBeforeCapture) { 228 mediaPlayer.start(); 229 // Make sure the player is actually playing, thus forcing a rerouting 230 Thread.sleep(100); 231 } 232 233 AudioRecord audioRecord = createDefaultPlaybackCaptureRecord(); 234 235 try { 236 audioRecord.startRecording(); 237 mediaPlayer.start(); 238 ByteBuffer rawBuffer = readToBuffer(audioRecord, BUFFER_SIZE); 239 audioRecord.stop(); // Force an reroute 240 mediaPlayer.stop(); 241 assertEquals(AudioRecord.RECORDSTATE_STOPPED, audioRecord.getRecordingState()); 242 if (dataPresent) { 243 assertFalse("Expected data, but only silence was recorded", 244 onlySilence(rawBuffer.asShortBuffer())); 245 } else { 246 assertTrue("Expected silence, but some data was recorded", 247 onlySilence(rawBuffer.asShortBuffer())); 248 } 249 } finally { 250 audioRecord.release(); 251 } 252 } finally { 253 mediaPlayer.release(); 254 } 255 } 256 testPlaybackCapture(boolean allowCapture, @AttributeUsage int playbackUsage, boolean dataPresent)257 public void testPlaybackCapture(boolean allowCapture, 258 @AttributeUsage int playbackUsage, 259 boolean dataPresent) throws Exception { 260 if (allowCapture) { 261 testPlaybackCapture(ALLOW_CAPTURE_BY_ALL, playbackUsage, dataPresent); 262 } else { 263 testPlaybackCapture(ALLOW_CAPTURE_BY_SYSTEM, playbackUsage, dataPresent); 264 testPlaybackCapture(ALLOW_CAPTURE_BY_NONE, playbackUsage, dataPresent); 265 266 try { 267 mAudioManager.setAllowedCapturePolicy(ALLOW_CAPTURE_BY_SYSTEM); 268 testPlaybackCapture(ALLOW_CAPTURE_BY_ALL, playbackUsage, dataPresent); 269 mAudioManager.setAllowedCapturePolicy(ALLOW_CAPTURE_BY_NONE); 270 testPlaybackCapture(ALLOW_CAPTURE_BY_ALL, playbackUsage, dataPresent); 271 } finally { 272 // Do not impact followup test is case of failure 273 mAudioManager.setAllowedCapturePolicy(ALLOW_CAPTURE_BY_ALL); 274 } 275 } 276 } 277 278 private static final boolean OPT_IN = true; 279 private static final boolean OPT_OUT = false; 280 281 private static final boolean EXPECT_DATA = true; 282 private static final boolean EXPECT_SILENCE = false; 283 284 // We have explicit tests per usage testCaptureMatchingAllowedUsage* 285 private static final @AttributeUsage int[] ALLOWED_USAGES = new int[]{ 286 AudioAttributes.USAGE_UNKNOWN, 287 AudioAttributes.USAGE_MEDIA, 288 AudioAttributes.USAGE_GAME 289 }; 290 private static final @AttributeUsage int[] FORBIDEN_USAGES = new int[]{ 291 AudioAttributes.USAGE_ALARM, 292 AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, 293 AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, 294 AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, 295 AudioAttributes.USAGE_ASSISTANT, 296 AudioAttributes.USAGE_NOTIFICATION, 297 }; 298 299 @Presubmit 300 @Test testPlaybackCaptureFast()301 public void testPlaybackCaptureFast() throws Exception { 302 mAPCTestConfig.matchingUsages = new int[]{ AudioAttributes.USAGE_MEDIA }; 303 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_MEDIA, EXPECT_DATA); 304 testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_MEDIA, EXPECT_SILENCE); 305 } 306 307 @Test testPlaybackCaptureRerouting()308 public void testPlaybackCaptureRerouting() throws Exception { 309 mPlaybackBeforeCapture = true; 310 mAPCTestConfig.matchingUsages = new int[]{ AudioAttributes.USAGE_MEDIA }; 311 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_MEDIA, EXPECT_DATA); 312 } 313 314 @Test(expected = IllegalArgumentException.class) testMatchNothing()315 public void testMatchNothing() throws Exception { 316 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE); 317 } 318 319 @Test(expected = IllegalStateException.class) testCombineUsages()320 public void testCombineUsages() throws Exception { 321 mAPCTestConfig.matchingUsages = new int[]{ AudioAttributes.USAGE_UNKNOWN }; 322 mAPCTestConfig.excludeUsages = new int[]{ AudioAttributes.USAGE_MEDIA }; 323 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE); 324 } 325 326 @Test(expected = IllegalStateException.class) testCombineUid()327 public void testCombineUid() throws Exception { 328 mAPCTestConfig.matchingUids = new int[]{ mUid }; 329 mAPCTestConfig.excludeUids = new int[]{ 0 }; 330 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE); 331 } 332 333 // Allowed usage tests done individually to isolate failure and keep test duration < 30s. 334 @Test testCaptureMatchingAllowedUsageUnknown()335 public void testCaptureMatchingAllowedUsageUnknown() throws Exception { 336 doTestCaptureMatchingAllowedUsage(AudioAttributes.USAGE_UNKNOWN); 337 } 338 339 @Test testCaptureMatchingAllowedUsageMedia()340 public void testCaptureMatchingAllowedUsageMedia() throws Exception { 341 doTestCaptureMatchingAllowedUsage(AudioAttributes.USAGE_MEDIA); 342 } 343 344 @Test testCaptureMatchingAllowedUsageGame()345 public void testCaptureMatchingAllowedUsageGame() throws Exception { 346 doTestCaptureMatchingAllowedUsage(AudioAttributes.USAGE_GAME); 347 } 348 doTestCaptureMatchingAllowedUsage(int usage)349 private void doTestCaptureMatchingAllowedUsage(int usage) throws Exception { 350 mAPCTestConfig.matchingUsages = new int[]{ usage }; 351 testPlaybackCapture(OPT_IN, usage, EXPECT_DATA); 352 testPlaybackCapture(OPT_OUT, usage, EXPECT_SILENCE); 353 354 mAPCTestConfig.matchingUsages = ALLOWED_USAGES; 355 testPlaybackCapture(OPT_IN, usage, EXPECT_DATA); 356 testPlaybackCapture(OPT_OUT, usage, EXPECT_SILENCE); 357 } 358 359 @Test testCaptureMatchingForbidenUsage()360 public void testCaptureMatchingForbidenUsage() throws Exception { 361 for (int usage : FORBIDEN_USAGES) { 362 mAPCTestConfig.matchingUsages = new int[]{ usage }; 363 testPlaybackCapture(OPT_IN, usage, EXPECT_SILENCE); 364 365 mAPCTestConfig.matchingUsages = ALLOWED_USAGES; 366 testPlaybackCapture(OPT_IN, usage, EXPECT_SILENCE); 367 } 368 } 369 370 @Test testCaptureExcludeUsage()371 public void testCaptureExcludeUsage() throws Exception { 372 for (int usage : ALLOWED_USAGES) { 373 mAPCTestConfig.excludeUsages = new int[]{ usage }; 374 testPlaybackCapture(OPT_IN, usage, EXPECT_SILENCE); 375 376 mAPCTestConfig.excludeUsages = ALLOWED_USAGES; 377 testPlaybackCapture(OPT_IN, usage, EXPECT_SILENCE); 378 379 mAPCTestConfig.excludeUsages = FORBIDEN_USAGES; 380 testPlaybackCapture(OPT_IN, usage, EXPECT_DATA); 381 } 382 } 383 384 @Test testCaptureMatchingUid()385 public void testCaptureMatchingUid() throws Exception { 386 mAPCTestConfig.matchingUids = new int[]{ mUid }; 387 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA); 388 testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_GAME, EXPECT_SILENCE); 389 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE); 390 391 mAPCTestConfig.matchingUids = new int[]{ 0 }; 392 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE); 393 } 394 395 @Test testCaptureExcludeUid()396 public void testCaptureExcludeUid() throws Exception { 397 mAPCTestConfig.excludeUids = new int[]{ 0 }; 398 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_DATA); 399 testPlaybackCapture(OPT_OUT, AudioAttributes.USAGE_UNKNOWN, EXPECT_SILENCE); 400 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_VOICE_COMMUNICATION, EXPECT_SILENCE); 401 402 mAPCTestConfig.excludeUids = new int[]{ mUid }; 403 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_GAME, EXPECT_SILENCE); 404 } 405 406 @Test(expected = UnsupportedOperationException.class) testStoppedMediaProjection()407 public void testStoppedMediaProjection() throws Exception { 408 mMediaProjection.stop(); 409 mAPCTestConfig.matchingUsages = new int[]{ AudioAttributes.USAGE_MEDIA }; 410 testPlaybackCapture(OPT_IN, AudioAttributes.USAGE_MEDIA, EXPECT_DATA); 411 } 412 413 @Test testStopMediaProjectionDuringCapture()414 public void testStopMediaProjectionDuringCapture() throws Exception { 415 final int STOP_TIMEOUT_MS = 1000; 416 417 mAPCTestConfig.matchingUsages = new int[]{ AudioAttributes.USAGE_MEDIA }; 418 419 MediaPlayer mediaPlayer = createMediaPlayer(ALLOW_CAPTURE_BY_ALL, 420 R.raw.testwav_16bit_44100hz, 421 AudioAttributes.USAGE_MEDIA); 422 mediaPlayer.start(); 423 424 AudioRecord audioRecord = createDefaultPlaybackCaptureRecord(); 425 audioRecord.startRecording(); 426 ByteBuffer rawBuffer = readToBuffer(audioRecord, BUFFER_SIZE); 427 assertFalse("Expected data, but only silence was recorded", 428 onlySilence(rawBuffer.asShortBuffer())); 429 430 final int nativeBufferSize = audioRecord.getBufferSizeInFrames() 431 * audioRecord.getChannelCount(); 432 433 // Stop the media projection 434 CountDownLatch stopCDL = new CountDownLatch(1); 435 mMediaProjection.registerCallback(new MediaProjection.Callback() { 436 public void onStop() { 437 stopCDL.countDown(); 438 } 439 }, new Handler(Looper.getMainLooper())); 440 mMediaProjection.stop(); 441 assertTrue("Could not stop the MediaProjection in " + STOP_TIMEOUT_MS + "ms", 442 stopCDL.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 443 444 // With the remote submix disabled, no new samples should feed the track buffer. 445 // As a result, read() should fail after at most the total buffer size read. 446 // Even if the projection is stopped, the policy unregisteration is async, 447 // so double that to be on the conservative side. 448 final int MAX_READ_SIZE = 8 * nativeBufferSize; 449 int readSize = 0; 450 ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE); 451 int status; 452 while ((status = audioRecord.read(buffer, BUFFER_SIZE)) > 0) { 453 readSize += status; 454 assertThat("audioRecord did not stop, current state is " 455 + audioRecord.getRecordingState(), readSize, lessThan(MAX_READ_SIZE)); 456 } 457 audioRecord.stop(); 458 audioRecord.startRecording(); 459 460 // Check that the audioRecord can no longer receive audio 461 assertThat("Can still record after policy unregistration", 462 audioRecord.read(buffer, BUFFER_SIZE), lessThan(0)); 463 464 audioRecord.release(); 465 mediaPlayer.stop(); 466 mediaPlayer.release(); 467 } 468 469 470 @Test testPlaybackCaptureDoS()471 public void testPlaybackCaptureDoS() throws Exception { 472 final int UPPER_BOUND_TO_CONCURENT_PLAYBACK_CAPTURE = 1000; 473 final int MIN_NB_OF_CONCURENT_PLAYBACK_CAPTURE = 5; 474 475 mAPCTestConfig.matchingUsages = new int[]{ AudioAttributes.USAGE_MEDIA }; 476 477 Stack<AudioRecord> audioRecords = new Stack<>(); 478 MediaPlayer mediaPlayer = createMediaPlayer(ALLOW_CAPTURE_BY_ALL, 479 R.raw.testwav_16bit_44100hz, 480 AudioAttributes.USAGE_MEDIA); 481 try { 482 mediaPlayer.start(); 483 484 // Lets create as many audio playback capture as we can 485 try { 486 for (int i = 0; i < UPPER_BOUND_TO_CONCURENT_PLAYBACK_CAPTURE; i++) { 487 audioRecords.push(createDefaultPlaybackCaptureRecord()); 488 } 489 fail("Playback capture never failed even with " + audioRecords.size() 490 + " concurrent ones. Are errors silently dropped ?"); 491 } catch (Exception e) { 492 assertThat("Number of supported concurrent playback capture", audioRecords.size(), 493 greaterThan(MIN_NB_OF_CONCURENT_PLAYBACK_CAPTURE)); 494 } 495 496 // Should not be able to create a new audio playback capture record", 497 assertThrows(Exception.class, this::createDefaultPlaybackCaptureRecord); 498 499 // Check that all record can all be started 500 for (AudioRecord audioRecord : audioRecords) { 501 audioRecord.startRecording(); 502 assertEquals(AudioRecord.RECORDSTATE_RECORDING, audioRecord.getRecordingState()); 503 } 504 505 // Check that they all record audio. Since for a system under load it could 506 // take some time to establish the capture pipeline, allow for retries. 507 final int kDataAcquisitionAttempts = 3; 508 Stack<String> failedRecords = new Stack<>(); 509 for (AudioRecord audioRecord : audioRecords) { 510 boolean noData = true; 511 for (int attempt = 0; attempt < kDataAcquisitionAttempts; attempt++) { 512 ByteBuffer rawBuffer = readToBuffer(audioRecord, BUFFER_SIZE); 513 if (!onlySilence(rawBuffer.asShortBuffer())) { 514 noData = false; 515 break; 516 } 517 } 518 if (noData) { 519 AudioDeviceInfo device = audioRecord.getRoutedDevice(); 520 if (device != null) { 521 failedRecords.push(device.getAddress()); 522 } else { 523 failedRecords.push("null device"); 524 } 525 } 526 } 527 if (!failedRecords.empty()) { 528 fail("Expected data, but only silence was recorded for " + failedRecords); 529 } 530 531 // Stopping one AR must allow creating a new one 532 audioRecords.peek().stop(); 533 audioRecords.pop().release(); 534 final long SLEEP_AFTER_STOP_FOR_INACTIVITY_MS = 1000; 535 Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS); 536 audioRecords.push(createDefaultPlaybackCaptureRecord()); 537 538 // That new one must still be able to capture 539 audioRecords.peek().startRecording(); 540 ByteBuffer rawBuffer = readToBuffer(audioRecords.peek(), BUFFER_SIZE); 541 assertFalse("Expected data, but only silence was recorded", 542 onlySilence(rawBuffer.asShortBuffer())); 543 544 // cleanup 545 mediaPlayer.stop(); 546 } finally { 547 mediaPlayer.release(); 548 try { 549 for (AudioRecord audioRecord : audioRecords) { 550 audioRecord.stop(); 551 } 552 } finally { 553 for (AudioRecord audioRecord : audioRecords) { 554 audioRecord.release(); 555 } 556 } 557 } 558 } 559 560 } 561