• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.AudioFormat.CHANNEL_OUT_MONO;
20 import static android.media.AudioFormat.CHANNEL_OUT_STEREO;
21 import static android.media.AudioFormat.ENCODING_DEFAULT;
22 import static android.media.AudioFormat.ENCODING_PCM_16BIT;
23 import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_INJECTOR;
24 import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS;
25 import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID;
26 import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID;
27 
28 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertThrows;
30 
31 import android.media.AudioAttributes;
32 import android.media.AudioFormat;
33 import android.media.AudioSystem;
34 import android.media.audiopolicy.AudioMix;
35 import android.media.audiopolicy.AudioMixingRule;
36 import android.media.audiopolicy.AudioPolicyConfig;
37 import android.os.Parcel;
38 
39 import androidx.test.ext.junit.runners.AndroidJUnit4;
40 
41 import com.android.compatibility.common.util.FrameworkSpecificTest;
42 
43 import com.google.common.testing.EqualsTester;
44 
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 @FrameworkSpecificTest
52 @RunWith(AndroidJUnit4.class)
53 public class AudioMixTest {
54 
55     private static final AudioFormat OUTPUT_FORMAT_STEREO_44KHZ_PCM =
56             new AudioFormat.Builder()
57                     .setSampleRate(44000)
58                     .setChannelMask(CHANNEL_OUT_STEREO)
59                     .setEncoding(ENCODING_PCM_16BIT).build();
60 
61     private static final AudioFormat OUTPUT_FORMAT_STEREO_48KHZ_DEFAULT =
62             new AudioFormat.Builder()
63                     .setSampleRate(48000)
64                     .setChannelMask(CHANNEL_OUT_STEREO)
65                     .setEncoding(ENCODING_DEFAULT).build();
66     private static final AudioFormat OUTPUT_FORMAT_MONO_16KHZ_PCM =
67             new AudioFormat.Builder()
68                     .setSampleRate(16000)
69                     .setChannelMask(CHANNEL_OUT_MONO)
70                     .setEncoding(ENCODING_PCM_16BIT).build();
71     private static final AudioFormat INPUT_FORMAT_MONO_16KHZ_PCM =
72             new AudioFormat.Builder()
73                     .setSampleRate(16000)
74                     .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
75                     .setEncoding(ENCODING_PCM_16BIT).build();
76 
77     private static final AudioAttributes AUDIO_ATTRIBUTES_MEDIA_MUSIC =
78             new AudioAttributes.Builder()
79                     .setUsage(AudioAttributes.USAGE_MEDIA)
80                     .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
81                     .build();
82 
83     @Test
testEquals()84     public void testEquals() {
85         final EqualsTester equalsTester = new EqualsTester();
86 
87         // --- Equality group 1
88         final AudioMix playbackAudioMixWithSessionId42AndUid123 =
89                 new AudioMix.Builder(new AudioMixingRule.Builder()
90                         .setTargetMixRole(MIX_ROLE_PLAYERS)
91                         .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
92                         .addMixRule(RULE_MATCH_UID, 123).build())
93                         .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM)
94                         .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
95         equalsTester.addEqualityGroup(
96                 playbackAudioMixWithSessionId42AndUid123,
97                 writeToAndFromParcel(playbackAudioMixWithSessionId42AndUid123));
98 
99         // --- Equality group 2
100         final AudioMix recordingAudioMixWithSessionId42AndUid123 =
101                 new AudioMix.Builder(new AudioMixingRule.Builder()
102                         .setTargetMixRole(MIX_ROLE_INJECTOR)
103                         .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
104                         .addMixRule(RULE_MATCH_UID, 123).build())
105                         .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
106                         .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
107         equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123,
108                 writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123));
109 
110         // --- Equality group 3
111         final AudioMix recordingAudioMixWithSessionId42AndUid123Render =
112                 new AudioMix.Builder(new AudioMixingRule.Builder()
113                         .setTargetMixRole(MIX_ROLE_PLAYERS)
114                         .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
115                         .addMixRule(RULE_MATCH_UID, 123).build())
116                         .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
117                         .setRouteFlags(
118                                 AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER).build();
119         equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123Render,
120                 writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123Render));
121 
122         // --- Equality group 4
123         final AudioMix playbackAudioMixWithUid123 =
124                 new AudioMix.Builder(new AudioMixingRule.Builder()
125                         .setTargetMixRole(MIX_ROLE_PLAYERS)
126                         .addMixRule(RULE_MATCH_UID, 123).build())
127                         .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
128                         .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
129         equalsTester.addEqualityGroup(playbackAudioMixWithUid123,
130                 writeToAndFromParcel(playbackAudioMixWithUid123));
131 
132         // --- Equality group 5
133         final AudioMix playbackAudioMixWithUid42 =
134                 new AudioMix.Builder(new AudioMixingRule.Builder()
135                         .setTargetMixRole(MIX_ROLE_PLAYERS)
136                         .addMixRule(RULE_MATCH_UID, 42).build())
137                         .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
138                         .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
139         equalsTester.addEqualityGroup(playbackAudioMixWithUid42,
140                 writeToAndFromParcel(playbackAudioMixWithUid42));
141 
142         // -- Equality group 6
143         final AudioMix playbackAudioMixWithMediaUsage = new AudioMix.Builder(
144                 new AudioMixingRule.Builder()
145                         .excludeRule(AUDIO_ATTRIBUTES_MEDIA_MUSIC,
146                                 AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE)
147                         .build())
148                 .setDevice(AudioSystem.DEVICE_BIT_DEFAULT, "device-address")
149                 .setFormat(OUTPUT_FORMAT_STEREO_48KHZ_DEFAULT)
150                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
151                 .build();
152         equalsTester.addEqualityGroup(playbackAudioMixWithMediaUsage,
153                 writeToAndFromParcel(playbackAudioMixWithMediaUsage));
154 
155         equalsTester.testEquals();
156     }
157 
158     @Test
buildRenderToRemoteSubmix_success()159     public void buildRenderToRemoteSubmix_success() {
160         final String deviceAddress = "address";
161         final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
162                 .setTargetMixRole(MIX_ROLE_PLAYERS)
163                 .addMixRule(RULE_MATCH_UID, 42).build())
164                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
165                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
166                 .setDevice(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, /*address=*/deviceAddress).build();
167 
168         assertEquals(deviceAddress, audioMix.getRegistration());
169         assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
170         assertEquals(AudioMix.ROUTE_FLAG_RENDER, audioMix.getRouteFlags());
171     }
172 
173     @Test
buildLoopbackAndRenderToRemoteSubmix_success()174     public void buildLoopbackAndRenderToRemoteSubmix_success() {
175         final String deviceAddress = "address";
176         final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
177                 .setTargetMixRole(MIX_ROLE_PLAYERS)
178                 .addMixRule(RULE_MATCH_UID, 42).build())
179                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
180                 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK_RENDER)
181                 .setDevice(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, /*address=*/deviceAddress).build();
182 
183         assertEquals(deviceAddress, audioMix.getRegistration());
184         assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
185         assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK_RENDER, audioMix.getRouteFlags());
186     }
187 
188     @Test
buildRenderToSpeaker_success()189     public void buildRenderToSpeaker_success() {
190         final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
191                 .setTargetMixRole(MIX_ROLE_PLAYERS)
192                 .addMixRule(RULE_MATCH_UID, 42).build())
193                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
194                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
195                 .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build();
196 
197         assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
198         assertEquals(AudioMix.ROUTE_FLAG_RENDER, audioMix.getRouteFlags());
199     }
200 
201     @Test
buildLoopbackForPlayerMix_success()202     public void buildLoopbackForPlayerMix_success() {
203         final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
204                 .setTargetMixRole(MIX_ROLE_PLAYERS)
205                 .addMixRule(RULE_MATCH_UID, 42).build())
206                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
207                 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
208 
209         assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
210         assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK, audioMix.getRouteFlags());
211     }
212 
213     @Test
buildLoopbackForInjectorMix_success()214     public void buildLoopbackForInjectorMix_success() {
215         final AudioMix audioMix = new AudioMix.Builder(new AudioMixingRule.Builder()
216                 .setTargetMixRole(MIX_ROLE_INJECTOR)
217                 .addMixRule(RULE_MATCH_UID, 42).build())
218                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
219                 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
220 
221         assertEquals(OUTPUT_FORMAT_MONO_16KHZ_PCM, audioMix.getFormat());
222         assertEquals(AudioMix.ROUTE_FLAG_LOOP_BACK, audioMix.getRouteFlags());
223     }
224 
225     @Test
buildLoopbackWithIncompatibleDevice_throws()226     public void buildLoopbackWithIncompatibleDevice_throws() {
227         assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
228                 new AudioMixingRule.Builder()
229                         .setTargetMixRole(MIX_ROLE_PLAYERS)
230                         .addMixRule(RULE_MATCH_UID, 42).build())
231                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
232                 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK)
233                 .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build());
234     }
235 
236     @Test
buildRenderWithoutDevice_throws()237     public void buildRenderWithoutDevice_throws() {
238         assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
239                 new AudioMixingRule.Builder()
240                         .setTargetMixRole(MIX_ROLE_PLAYERS)
241                         .addMixRule(RULE_MATCH_UID, 42).build())
242                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
243                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER).build());
244     }
245 
246     @Test
buildRenderWithInputDevice_throws()247     public void buildRenderWithInputDevice_throws() {
248         assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
249                 new AudioMixingRule.Builder()
250                         .setTargetMixRole(MIX_ROLE_PLAYERS)
251                         .addMixRule(RULE_MATCH_UID, 42).build())
252                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
253                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
254                 .setDevice(AudioSystem.DEVICE_IN_BUILTIN_MIC, /*address=*/"").build());
255     }
256 
257     @Test
buildRenderWithInjectorMix_throws()258     public void buildRenderWithInjectorMix_throws() {
259         assertThrows(IllegalArgumentException.class, () -> new AudioMix.Builder(
260                 new AudioMixingRule.Builder()
261                         .setTargetMixRole(MIX_ROLE_INJECTOR)
262                         .addMixRule(RULE_MATCH_UID, 42).build())
263                 .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
264                 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
265                 .setDevice(AudioSystem.DEVICE_OUT_SPEAKER, /*address=*/"").build());
266     }
267 
268 
269 
writeToAndFromParcel(AudioMix audioMix)270     private static AudioMix writeToAndFromParcel(AudioMix audioMix) {
271         AudioPolicyConfig apc = new AudioPolicyConfig(new ArrayList<>(List.of(audioMix)));
272         Parcel parcel = Parcel.obtain();
273         apc.writeToParcel(parcel, /*flags=*/0);
274         parcel.setDataPosition(0);
275         AudioMix unmarshalledMix =
276                 AudioPolicyConfig.CREATOR.createFromParcel(parcel).getMixes().get(0);
277         parcel.recycle();
278         return unmarshalledMix;
279     }
280 }
281