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