1 /* <lambda>null2 * Copyright (C) 2022 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.pm.PackageManager 20 import android.media.AudioAttributes 21 import android.media.AudioFormat 22 import android.media.AudioManager 23 import android.media.AudioProfile 24 import android.media.AudioTrack 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.platform.app.InstrumentationRegistry 27 import com.android.compatibility.common.util.NonMainlineTest 28 import org.junit.Assert.fail 29 import org.junit.Assume.assumeTrue 30 import org.junit.Before 31 import org.junit.Test 32 import org.junit.runner.RunWith 33 34 @NonMainlineTest 35 @RunWith(AndroidJUnit4::class) 36 class DirectAudioProfilesForAttributesTest { 37 38 private lateinit var audioManager: AudioManager 39 40 @Before 41 fun setup() { 42 val context = InstrumentationRegistry.getInstrumentation().context 43 assumeTrue( 44 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT) 45 ) 46 47 audioManager = context.getSystemService(AudioManager::class.java) 48 } 49 50 /** 51 * Test that only AudioProfiles returned in getDirectProfilesForAttributes can create direct 52 * AudioTracks 53 */ 54 @Test 55 fun testCreateDirectAudioTracksOnlyForGetDirectProfilesForAttributes() { 56 for (usage in AudioAttributes.getSdkUsages()) { 57 val audioAttributes = AudioAttributes.Builder() 58 .setUsage(usage) 59 .build() 60 val directProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes) 61 62 // All compressed format (non pcm) profiles can create direct AudioTracks. 63 // getDirectProfilesForAttributes does not include profiles supporting 64 // compressed playback in offload mode, so it is expected that all creation 65 // succeeds even if the audio track is not explicitly created in offload mode. 66 val compressedProfiles = directProfiles.filterOutPcmFormats() 67 for (directProfile in compressedProfiles) { 68 checkCreateAudioTracks(audioAttributes, directProfile, true) 69 } 70 } 71 } 72 73 // Returns true if all the AudioTracks with all combinations of parameters that can be derived 74 // from the passed audio profile can be created with the expected result. 75 // Doesn't start the tracks. 76 private fun checkCreateAudioTracks( 77 audioAttributes: AudioAttributes, 78 audioProfile: AudioProfile, 79 expectedCreationSuccess: Boolean 80 ) { 81 if (audioProfile.format == AudioFormat.ENCODING_INVALID) { 82 fail("Found INVALID audio format in audio profile ($audioProfile) " + 83 "when trying to create audio tracks with it!") 84 } 85 for (audioFormat in audioProfile.getAllAudioFormats()) { 86 try { 87 AudioTrack.Builder() 88 .setAudioAttributes(audioAttributes) 89 .setAudioFormat(audioFormat) 90 .build() 91 .release() 92 // allow a short time to free the AudioTrack resources 93 Thread.sleep(100) 94 if (!expectedCreationSuccess) { 95 fail( 96 "Created AudioTrack for attributes ($audioAttributes) and " + 97 "audio format ($audioFormat)!" 98 ) 99 } 100 } catch (e: Exception) { 101 if (expectedCreationSuccess) { 102 fail( 103 "Failed to create AudioTrack for attributes ($audioAttributes) and " + 104 "audio format ($audioFormat) with exception ($e)!" 105 ) 106 } 107 } 108 } 109 } 110 111 // Utils 112 private fun AudioProfile.getAllAudioFormats() = 113 sampleRates.map { sampleRate -> 114 channelMasks.map { channelMask -> 115 AudioFormat.Builder() 116 .setEncoding(format) 117 .setSampleRate(sampleRate) 118 .setChannelMask(channelMask) 119 .build() 120 }.plus( 121 channelIndexMasks.map { channelIndexMask -> 122 AudioFormat.Builder() 123 .setEncoding(format) 124 .setSampleRate(sampleRate) 125 .setChannelIndexMask(channelIndexMask) 126 .build() 127 } 128 ) 129 }.flatten() 130 131 private fun List<AudioProfile>.filterOutPcmFormats() = filter { it.format !in pcmFormats } 132 133 companion object { 134 private val pcmFormats = listOf( 135 AudioFormat.ENCODING_PCM_8BIT, 136 AudioFormat.ENCODING_PCM_16BIT, 137 AudioFormat.ENCODING_PCM_FLOAT, 138 AudioFormat.ENCODING_PCM_24BIT_PACKED, 139 AudioFormat.ENCODING_PCM_32BIT 140 ) 141 } 142 } 143