1 /*
2  * Copyright (C) 2020 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 com.android.audiopolicytest;
18 
19 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
20 
21 import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES;
22 import static com.android.audiopolicytest.AudioVolumeTestUtil.incrementVolumeIndex;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotEquals;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertNull;
28 import static org.junit.Assert.assertThrows;
29 import static org.junit.Assert.assertTrue;
30 
31 import android.media.AudioAttributes;
32 import android.media.AudioManager;
33 import android.media.AudioSystem;
34 import android.media.audiopolicy.AudioProductStrategy;
35 import android.media.audiopolicy.AudioVolumeGroup;
36 import android.platform.test.annotations.Presubmit;
37 import android.util.Log;
38 
39 import androidx.test.ext.junit.runners.AndroidJUnit4;
40 
41 import com.google.common.primitives.Ints;
42 
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 
48 import java.util.List;
49 
50 @Presubmit
51 @RunWith(AndroidJUnit4.class)
52 public class AudioManagerTest {
53     private static final String TAG = "AudioManagerTest";
54 
55     private AudioManager mAudioManager;
56 
57     @Rule
58     public final AudioVolumesTestRule rule = new AudioVolumesTestRule();
59 
60     @Before
setUp()61     public void setUp() {
62         mAudioManager = getApplicationContext().getSystemService(AudioManager.class);
63     }
64 
65     //-----------------------------------------------------------------
66     // Test getAudioProductStrategies and validate strategies
67     //-----------------------------------------------------------------
68     @Test
testGetAndValidateProductStrategies()69     public void testGetAndValidateProductStrategies() {
70         List<AudioProductStrategy> audioProductStrategies =
71                 mAudioManager.getAudioProductStrategies();
72         assertTrue(audioProductStrategies.size() > 0);
73 
74         List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
75         assertTrue(audioVolumeGroups.size() > 0);
76 
77         // Validate Audio Product Strategies
78         for (final AudioProductStrategy audioProductStrategy : audioProductStrategies) {
79             AudioAttributes attributes = audioProductStrategy.getAudioAttributes();
80             int strategyStreamType =
81                     audioProductStrategy.getLegacyStreamTypeForAudioAttributes(attributes);
82 
83             assertTrue("Strategy shall support the attributes retrieved from its getter API",
84                     audioProductStrategy.supportsAudioAttributes(attributes));
85 
86             int volumeGroupId =
87                     audioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes);
88 
89             // A strategy must be associated to a volume group
90             assertNotEquals("strategy not assigned to any volume group",
91                     volumeGroupId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
92 
93             // Valid Group ?
94             AudioVolumeGroup audioVolumeGroup = null;
95             for (final AudioVolumeGroup avg : audioVolumeGroups) {
96                 if (avg.getId() == volumeGroupId) {
97                     audioVolumeGroup = avg;
98                     break;
99                 }
100             }
101             assertNotNull("Volume Group not found", audioVolumeGroup);
102 
103             // Cross check: the group shall have at least one aa / stream types following the
104             // considered strategy
105             boolean strategyAttributesSupported = false;
106             for (final AudioAttributes aa : audioVolumeGroup.getAudioAttributes()) {
107                 if (audioProductStrategy.supportsAudioAttributes(aa)) {
108                     strategyAttributesSupported = true;
109                     break;
110                 }
111             }
112             assertTrue("Volume Group and Strategy mismatching", strategyAttributesSupported);
113 
114             // Some Product strategy may not have corresponding stream types as they intends
115             // to address volume setting per attributes to avoid adding new stream type
116             // and going on deprecating the stream type even for volume
117             if (strategyStreamType != AudioSystem.STREAM_DEFAULT) {
118                 boolean strategStreamTypeSupported = false;
119                 for (final int vgStreamType : audioVolumeGroup.getLegacyStreamTypes()) {
120                     if (vgStreamType == strategyStreamType) {
121                         strategStreamTypeSupported = true;
122                         break;
123                     }
124                 }
125                 assertTrue("Volume Group and Strategy mismatching", strategStreamTypeSupported);
126             }
127         }
128     }
129 
130     //-----------------------------------------------------------------
131     // Test getAudioVolumeGroups and validate volume groups
132     //-----------------------------------------------------------------
133     @Test
testGetAndValidateVolumeGroups()134     public void testGetAndValidateVolumeGroups() {
135         List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
136         assertTrue(audioVolumeGroups.size() > 0);
137 
138         List<AudioProductStrategy> audioProductStrategies =
139                 mAudioManager.getAudioProductStrategies();
140         assertTrue(audioProductStrategies.size() > 0);
141 
142         // Validate Audio Volume Groups, check all
143         for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
144             List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
145             int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes();
146 
147             // for each volume group attributes, find the matching product strategy and ensure
148             // it is linked the considered volume group
149             for (final AudioAttributes aa : avgAttributes) {
150                 if (aa.equals(DEFAULT_ATTRIBUTES)) {
151                     // Some volume groups may not have valid attributes, used for internal
152                     // volume management like patch/rerouting
153                     // so bailing out strategy retrieval from attributes
154                     continue;
155                 }
156                 boolean isVolumeGroupAssociatedToStrategy = false;
157                 for (final AudioProductStrategy strategy : audioProductStrategies) {
158                     int groupId = strategy.getVolumeGroupIdForAudioAttributes(aa);
159                     if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
160 
161                         assertEquals("Volume Group ID (" + audioVolumeGroup.toString()
162                                 + "), and Volume group ID associated to Strategy ("
163                                 + strategy.toString() + ") both supporting attributes "
164                                 + aa.toString() + " are mismatching",
165                                 audioVolumeGroup.getId(), groupId);
166                         isVolumeGroupAssociatedToStrategy = true;
167                         break;
168                     }
169                 }
170                 assertTrue("Volume Group (" + audioVolumeGroup.toString()
171                         + ") has no associated strategy for attributes " + aa.toString(),
172                         isVolumeGroupAssociatedToStrategy);
173             }
174 
175             // for each volume group stream type, find the matching product strategy and ensure
176             // it is linked the considered volume group
177             for (final int avgStreamType : avgStreamTypes) {
178                 if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
179                     // Some Volume Groups may not have corresponding stream types as they
180                     // intends to address volume setting per attributes to avoid adding new
181                     //  stream type and going on deprecating the stream type even for volume
182                     // so bailing out strategy retrieval from stream type
183                     continue;
184                 }
185                 boolean isVolumeGroupAssociatedToStrategy = false;
186                 for (final AudioProductStrategy strategy : audioProductStrategies) {
187                     Log.i(TAG, "strategy:" + strategy.toString());
188                     int groupId = strategy.getVolumeGroupIdForLegacyStreamType(avgStreamType);
189                     if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
190 
191                         assertEquals("Volume Group ID (" + audioVolumeGroup.toString()
192                                 + "), and Volume group ID associated to Strategy ("
193                                 + strategy.toString() + ") both supporting stream "
194                                 + AudioSystem.streamToString(avgStreamType) + "("
195                                 + avgStreamType + ") are mismatching",
196                                 audioVolumeGroup.getId(), groupId);
197                         isVolumeGroupAssociatedToStrategy = true;
198                         break;
199                     }
200                 }
201                 assertTrue("Volume Group (" + audioVolumeGroup.toString()
202                         + ") has no associated strategy for stream "
203                         + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")",
204                         isVolumeGroupAssociatedToStrategy);
205             }
206         }
207     }
208 
209     //-----------------------------------------------------------------
210     // Test Volume per Attributes setter/getters
211     //-----------------------------------------------------------------
212     @Test
testSetGetVolumePerAttributesWithInvalidAttributes()213     public void testSetGetVolumePerAttributesWithInvalidAttributes() throws Exception {
214         AudioAttributes nullAttributes = null;
215 
216         assertThrows(NullPointerException.class,
217                 () -> mAudioManager.getMaxVolumeIndexForAttributes(nullAttributes));
218 
219         assertThrows(NullPointerException.class,
220                 () -> mAudioManager.getMinVolumeIndexForAttributes(nullAttributes));
221 
222         assertThrows(NullPointerException.class,
223                 () -> mAudioManager.getVolumeIndexForAttributes(nullAttributes));
224 
225         assertThrows(NullPointerException.class,
226                 () -> mAudioManager.setVolumeIndexForAttributes(
227                         nullAttributes, 0 /*index*/, 0/*flags*/));
228     }
229 
230     @Test
testSetGetVolumePerAttributes()231     public void testSetGetVolumePerAttributes() {
232         for (int usage : AudioAttributes.getSdkUsages()) {
233             if (usage == AudioAttributes.USAGE_UNKNOWN) {
234                 continue;
235             }
236             AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build();
237             int indexMin = 0;
238             int indexMax = 0;
239             int index = 0;
240             Exception ex = null;
241 
242             try {
243                 indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aaForUsage);
244             } catch (Exception e) {
245                 ex = e; // unexpected
246             }
247             assertNull("Exception was thrown for valid attributes", ex);
248             ex = null;
249             try {
250                 indexMin = mAudioManager.getMinVolumeIndexForAttributes(aaForUsage);
251             } catch (Exception e) {
252                 ex = e; // unexpected
253             }
254             assertNull("Exception was thrown for valid attributes", ex);
255             ex = null;
256             try {
257                 index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
258             } catch (Exception e) {
259                 ex = e; // unexpected
260             }
261             assertNull("Exception was thrown for valid attributes", ex);
262             ex = null;
263             try {
264                 mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMin, 0/*flags*/);
265             } catch (Exception e) {
266                 ex = e; // unexpected
267             }
268             assertNull("Exception was thrown for valid attributes", ex);
269 
270             index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
271             assertEquals(index, indexMin);
272 
273             mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMax, 0/*flags*/);
274             index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
275             assertEquals(index, indexMax);
276         }
277     }
278 
279     //-----------------------------------------------------------------
280     // Test register/unregister VolumeGroupCallback
281     //-----------------------------------------------------------------
282     @Test
testVolumeGroupCallback()283     public void testVolumeGroupCallback() {
284         List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
285         assertTrue(audioVolumeGroups.size() > 0);
286 
287         AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
288         mAudioManager.registerVolumeGroupCallback(getApplicationContext().getMainExecutor(),
289                 vgCbReceiver);
290 
291         final List<Integer> publicStreams = Ints.asList(AudioManager.getPublicStreamTypes());
292         try {
293             // Validate Audio Volume Groups callback reception
294             for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
295                 int volumeGroupId = audioVolumeGroup.getId();
296 
297                 // Set the receiver to filter only the current group callback
298                 vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
299 
300                 List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
301                 int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes();
302 
303                 int index = 0;
304                 int indexMax = 0;
305                 int indexMin = 0;
306 
307                 // Set the volume per attributes (if valid) and wait the callback
308                 for (final AudioAttributes aa : avgAttributes) {
309                     if (aa.equals(DEFAULT_ATTRIBUTES)) {
310                         // Some volume groups may not have valid attributes, used for internal
311                         // volume management like patch/rerouting
312                         // so bailing out strategy retrieval from attributes
313                         continue;
314                     }
315                     index = mAudioManager.getVolumeIndexForAttributes(aa);
316                     indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
317                     indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
318                     index = incrementVolumeIndex(index, indexMin, indexMax);
319 
320                     vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
321                     mAudioManager.setVolumeIndexForAttributes(aa, index, 0/*flags*/);
322                     assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged(
323                             AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
324 
325                     int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
326                     assertEquals(readIndex, index);
327                 }
328                 // Set the volume per stream type (if valid) and wait the callback
329                 for (final int avgStreamType : avgStreamTypes) {
330                     if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
331                         // Some Volume Groups may not have corresponding stream types as they
332                         // intends to address volume setting per attributes to avoid adding new
333                         // stream type and going on deprecating the stream type even for volume
334                         // so bailing out strategy retrieval from stream type
335                         continue;
336                     }
337                     if (!publicStreams.contains(avgStreamType)
338                             || avgStreamType == AudioManager.STREAM_ACCESSIBILITY) {
339                         // Limit scope of test to public stream that do not require any
340                         // permission (e.g. Changing ACCESSIBILITY is subject to permission).
341                         continue;
342                     }
343                     index = mAudioManager.getStreamVolume(avgStreamType);
344                     indexMax = mAudioManager.getStreamMaxVolume(avgStreamType);
345                     indexMin = mAudioManager.getStreamMinVolumeInt(avgStreamType);
346                     index = incrementVolumeIndex(index, indexMin, indexMax);
347 
348                     vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
349                     mAudioManager.setStreamVolume(avgStreamType, index, 0/*flags*/);
350                     assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged(
351                             AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
352 
353                     int readIndex = mAudioManager.getStreamVolume(avgStreamType);
354                     assertEquals(index, readIndex);
355                 }
356             }
357         } finally {
358             mAudioManager.unregisterVolumeGroupCallback(vgCbReceiver);
359         }
360     }
361 }
362