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 com.android.audiopolicytest.AudioVolumeTestUtil.INVALID_ATTRIBUTES;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 
27 import android.media.AudioAttributes;
28 import android.media.AudioManager;
29 import android.media.AudioSystem;
30 import android.media.audiopolicy.AudioProductStrategy;
31 import android.media.audiopolicy.AudioVolumeGroup;
32 import android.os.Parcel;
33 import android.platform.test.annotations.Presubmit;
34 import android.util.Log;
35 
36 import androidx.test.ext.junit.runners.AndroidJUnit4;
37 
38 import com.google.common.testing.EqualsTester;
39 
40 import org.junit.Rule;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.List;
45 
46 @Presubmit
47 @RunWith(AndroidJUnit4.class)
48 public class AudioProductStrategyTest {
49     private static final String TAG = "AudioProductStrategyTest";
50 
51     @Rule
52     public final AudioVolumesTestRule rule = new AudioVolumesTestRule();
53 
54     //-----------------------------------------------------------------
55     // Test getAudioProductStrategies and validate strategies
56     //-----------------------------------------------------------------
57     @Test
testGetProductStrategies()58     public void testGetProductStrategies() {
59         List<AudioProductStrategy> audioProductStrategies =
60                 AudioProductStrategy.getAudioProductStrategies();
61 
62         assertNotNull(audioProductStrategies);
63         assertTrue(audioProductStrategies.size() > 0);
64 
65         for (final AudioProductStrategy aps : audioProductStrategies) {
66             assertTrue(aps.getId() >= 0);
67 
68             AudioAttributes aa = aps.getAudioAttributes();
69             assertNotNull(aa);
70 
71             // Ensure API consistency
72             assertTrue(aps.supportsAudioAttributes(aa));
73 
74             int streamType = aps.getLegacyStreamTypeForAudioAttributes(aa);
75             if (streamType == AudioSystem.STREAM_DEFAULT) {
76                 // bailing out test for volume group APIs consistency
77                 continue;
78             }
79             final int volumeGroupFromStream = aps.getVolumeGroupIdForLegacyStreamType(streamType);
80             final int volumeGroupFromAttributes = aps.getVolumeGroupIdForAudioAttributes(aa);
81             assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
82             assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
83         }
84     }
85 
86     //-----------------------------------------------------------------
87     // Test stream to/from attributes conversion
88     //-----------------------------------------------------------------
89     @Test
testAudioAttributesFromStreamTypes()90     public void testAudioAttributesFromStreamTypes() throws Exception {
91         List<AudioProductStrategy> audioProductStrategies =
92                 AudioProductStrategy.getAudioProductStrategies();
93 
94         assertNotNull(audioProductStrategies);
95         assertTrue(audioProductStrategies.size() > 0);
96 
97         for (final int streamType : AudioManager.getPublicStreamTypes()) {
98             AudioAttributes aaFromStreamType =
99                     AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
100                             streamType);
101 
102             // No strategy found for this stream type or no attributes defined for the strategy
103             // hosting this stream type; Bailing out the test, just ensure that any request
104             // for reciproque API with the unknown attributes would return default stream
105             // for volume control, aka STREAM_MUSIC.
106             if (aaFromStreamType.equals(INVALID_ATTRIBUTES)) {
107                 assertEquals(AudioSystem.STREAM_MUSIC,
108                         AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
109                             aaFromStreamType));
110             } else {
111                 // Attributes are valid, i.e. a strategy was found supporting this stream type
112                 // with valid attributes. Ensure reciproque works fine
113                 int streamTypeFromAttributes =
114                         AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
115                                 aaFromStreamType);
116                 assertEquals("stream " + AudioSystem.streamToString(streamType) + "("
117                         + streamType + ") expected to match attributes "
118                         + aaFromStreamType.toString() + " got instead stream "
119                         + AudioSystem.streamToString(streamTypeFromAttributes) + "("
120                         + streamTypeFromAttributes + ") expected to match attributes ",
121                         streamType, streamTypeFromAttributes);
122             }
123 
124             // Now identify the strategy supporting this stream type, ensure uniqueness
125             boolean strategyFound = false;
126             for (final AudioProductStrategy aps : audioProductStrategies) {
127                 AudioAttributes aaFromAps =
128                         aps.getAudioAttributesForLegacyStreamType(streamType);
129 
130                 if (aaFromAps == null) {
131                     // not this one...
132                     continue;
133                 }
134                 // Got it!
135                 assertFalse("Unique ProductStrategy shall match for a given stream type",
136                         strategyFound);
137                 strategyFound = true;
138 
139                 // Ensure getters aligned
140                 assertEquals(aaFromStreamType, aaFromAps);
141                 assertTrue(aps.supportsAudioAttributes(aaFromStreamType));
142 
143                 // Ensure reciproque works fine
144                 assertEquals(streamType,
145                         aps.getLegacyStreamTypeForAudioAttributes(aaFromStreamType));
146 
147                 // Ensure consistency of volume group getter API
148                 final int volumeGroupFromStream =
149                         aps.getVolumeGroupIdForLegacyStreamType(streamType);
150                 final int volumeGroupFromAttributes =
151                         aps.getVolumeGroupIdForAudioAttributes(aaFromStreamType);
152                 assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
153                 assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
154             }
155             if (!strategyFound) {
156                 // No strategy found, ensure volume control is MUSIC
157                 assertEquals(AudioSystem.STREAM_MUSIC,
158                         AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
159                             aaFromStreamType));
160             }
161         }
162     }
163 
164     @Test
testAudioAttributesToStreamTypes()165     public void testAudioAttributesToStreamTypes() {
166         List<AudioProductStrategy> audioProductStrategies =
167                 AudioProductStrategy.getAudioProductStrategies();
168 
169         assertNotNull(audioProductStrategies);
170         assertTrue(audioProductStrategies.size() > 0);
171 
172         for (int usage : AudioAttributes.getSdkUsages()) {
173             AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build();
174 
175             int streamTypeFromUsage =
176                     AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
177                             aaForUsage);
178 
179             // Cannot be undefined, always shall fall back on a valid stream type
180             // to be able to control the volume
181             assertNotEquals(streamTypeFromUsage, AudioSystem.STREAM_DEFAULT);
182 
183             Log.w(TAG, "GUSTAVE aaForUsage=" + aaForUsage.toString());
184 
185             // Now identify the strategy hosting these Audio Attributes and ensure informations
186             // matches.
187             // Now identify the strategy supporting this stream type, ensure uniqueness
188             boolean strategyFound = false;
189             for (final AudioProductStrategy aps : audioProductStrategies) {
190                 if (!aps.supportsAudioAttributes(aaForUsage)) {
191                     // Not this one
192                     continue;
193                 }
194                 // Got it!
195                 String msg = "Unique ProductStrategy shall match for a given audio attributes "
196                         + aaForUsage.toString() + " already associated also matches with"
197                         + aps.toString();
198                 assertFalse(msg, strategyFound);
199                 strategyFound = true;
200 
201                 // It may not return the expected stream type if the strategy does not have
202                 // associated stream type.
203                 // Behavior of member function getLegacyStreamTypeForAudioAttributes is
204                 // different than getLegacyStreamTypeForStrategyWithAudioAttributes since it
205                 // does not fallback on MUSIC stream type for volume operation
206                 int streamTypeFromAps = aps.getLegacyStreamTypeForAudioAttributes(aaForUsage);
207                 if (streamTypeFromAps == AudioSystem.STREAM_DEFAULT) {
208                     // No stream type assigned to this strategy
209                     // Expect static API to return default stream type for volume (aka MUSIC)
210                     assertEquals("Strategy (" + aps.toString() + ") has no associated stream "
211                             + ", must fallback on MUSIC stream as default",
212                             streamTypeFromUsage, AudioSystem.STREAM_MUSIC);
213                 } else {
214                     assertEquals("Attributes " + aaForUsage.toString() + " associated to stream "
215                             + AudioSystem.streamToString(streamTypeFromUsage)
216                             + " are supported by strategy (" + aps.toString() + ") which reports "
217                             + " these attributes are associated to stream "
218                             + AudioSystem.streamToString(streamTypeFromAps),
219                             streamTypeFromUsage, streamTypeFromAps);
220 
221                     // Ensure consistency of volume group getter API
222                     int volumeGroupFromStream =
223                             aps.getVolumeGroupIdForLegacyStreamType(streamTypeFromAps);
224                     int volumeGroupFromAttributes =
225                             aps.getVolumeGroupIdForAudioAttributes(aaForUsage);
226                     assertNotEquals(
227                             volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
228                     assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
229                 }
230             }
231             if (!strategyFound) {
232                 // No strategy found for the given attributes, the expected stream must be MUSIC
233                 assertEquals(streamTypeFromUsage, AudioSystem.STREAM_MUSIC);
234             }
235         }
236     }
237 
238     @Test
testEquals()239     public void testEquals() {
240         final EqualsTester equalsTester = new EqualsTester();
241 
242         AudioProductStrategy.getAudioProductStrategies().forEach(
243                 strategy -> equalsTester.addEqualityGroup(strategy,
244                         writeToAndFromParcel(strategy)));
245 
246         equalsTester.testEquals();
247     }
248 
writeToAndFromParcel( AudioProductStrategy audioProductStrategy)249     private static AudioProductStrategy writeToAndFromParcel(
250             AudioProductStrategy audioProductStrategy) {
251         Parcel parcel = Parcel.obtain();
252         audioProductStrategy.writeToParcel(parcel, /*flags=*/0);
253         parcel.setDataPosition(0);
254         AudioProductStrategy unmarshalledAudioProductStrategy =
255                 AudioProductStrategy.CREATOR.createFromParcel(parcel);
256         parcel.recycle();
257         return unmarshalledAudioProductStrategy;
258     }
259 }
260