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