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