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