/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.audiopolicytest; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES; import static com.android.audiopolicytest.AudioVolumeTestUtil.incrementVolumeIndex; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; import android.media.audiopolicy.AudioProductStrategy; import android.media.audiopolicy.AudioVolumeGroup; import android.platform.test.annotations.Presubmit; import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.primitives.Ints; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; @Presubmit @RunWith(AndroidJUnit4.class) public class AudioManagerTest { private static final String TAG = "AudioManagerTest"; private AudioManager mAudioManager; @Rule public final AudioVolumesTestRule rule = new AudioVolumesTestRule(); @Before public void setUp() { mAudioManager = getApplicationContext().getSystemService(AudioManager.class); } //----------------------------------------------------------------- // Test getAudioProductStrategies and validate strategies //----------------------------------------------------------------- @Test public void testGetAndValidateProductStrategies() { List<AudioProductStrategy> audioProductStrategies = mAudioManager.getAudioProductStrategies(); assertTrue(audioProductStrategies.size() > 0); List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); assertTrue(audioVolumeGroups.size() > 0); // Validate Audio Product Strategies for (final AudioProductStrategy audioProductStrategy : audioProductStrategies) { AudioAttributes attributes = audioProductStrategy.getAudioAttributes(); int strategyStreamType = audioProductStrategy.getLegacyStreamTypeForAudioAttributes(attributes); assertTrue("Strategy shall support the attributes retrieved from its getter API", audioProductStrategy.supportsAudioAttributes(attributes)); int volumeGroupId = audioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes); // A strategy must be associated to a volume group assertNotEquals("strategy not assigned to any volume group", volumeGroupId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP); // Valid Group ? AudioVolumeGroup audioVolumeGroup = null; for (final AudioVolumeGroup avg : audioVolumeGroups) { if (avg.getId() == volumeGroupId) { audioVolumeGroup = avg; break; } } assertNotNull("Volume Group not found", audioVolumeGroup); // Cross check: the group shall have at least one aa / stream types following the // considered strategy boolean strategyAttributesSupported = false; for (final AudioAttributes aa : audioVolumeGroup.getAudioAttributes()) { if (audioProductStrategy.supportsAudioAttributes(aa)) { strategyAttributesSupported = true; break; } } assertTrue("Volume Group and Strategy mismatching", strategyAttributesSupported); // Some Product strategy may not have corresponding stream types as they intends // to address volume setting per attributes to avoid adding new stream type // and going on deprecating the stream type even for volume if (strategyStreamType != AudioSystem.STREAM_DEFAULT) { boolean strategStreamTypeSupported = false; for (final int vgStreamType : audioVolumeGroup.getLegacyStreamTypes()) { if (vgStreamType == strategyStreamType) { strategStreamTypeSupported = true; break; } } assertTrue("Volume Group and Strategy mismatching", strategStreamTypeSupported); } } } //----------------------------------------------------------------- // Test getAudioVolumeGroups and validate volume groups //----------------------------------------------------------------- @Test public void testGetAndValidateVolumeGroups() { List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); assertTrue(audioVolumeGroups.size() > 0); List<AudioProductStrategy> audioProductStrategies = mAudioManager.getAudioProductStrategies(); assertTrue(audioProductStrategies.size() > 0); // Validate Audio Volume Groups, check all for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) { List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes(); int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes(); // for each volume group attributes, find the matching product strategy and ensure // it is linked the considered volume group for (final AudioAttributes aa : avgAttributes) { if (aa.equals(DEFAULT_ATTRIBUTES)) { // Some volume groups may not have valid attributes, used for internal // volume management like patch/rerouting // so bailing out strategy retrieval from attributes continue; } boolean isVolumeGroupAssociatedToStrategy = false; for (final AudioProductStrategy strategy : audioProductStrategies) { int groupId = strategy.getVolumeGroupIdForAudioAttributes(aa); if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) { assertEquals("Volume Group ID (" + audioVolumeGroup.toString() + "), and Volume group ID associated to Strategy (" + strategy.toString() + ") both supporting attributes " + aa.toString() + " are mismatching", audioVolumeGroup.getId(), groupId); isVolumeGroupAssociatedToStrategy = true; break; } } assertTrue("Volume Group (" + audioVolumeGroup.toString() + ") has no associated strategy for attributes " + aa.toString(), isVolumeGroupAssociatedToStrategy); } // for each volume group stream type, find the matching product strategy and ensure // it is linked the considered volume group for (final int avgStreamType : avgStreamTypes) { if (avgStreamType == AudioSystem.STREAM_DEFAULT) { // Some Volume Groups may not have corresponding stream types as they // intends to address volume setting per attributes to avoid adding new // stream type and going on deprecating the stream type even for volume // so bailing out strategy retrieval from stream type continue; } boolean isVolumeGroupAssociatedToStrategy = false; for (final AudioProductStrategy strategy : audioProductStrategies) { Log.i(TAG, "strategy:" + strategy.toString()); int groupId = strategy.getVolumeGroupIdForLegacyStreamType(avgStreamType); if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) { assertEquals("Volume Group ID (" + audioVolumeGroup.toString() + "), and Volume group ID associated to Strategy (" + strategy.toString() + ") both supporting stream " + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ") are mismatching", audioVolumeGroup.getId(), groupId); isVolumeGroupAssociatedToStrategy = true; break; } } assertTrue("Volume Group (" + audioVolumeGroup.toString() + ") has no associated strategy for stream " + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")", isVolumeGroupAssociatedToStrategy); } } } //----------------------------------------------------------------- // Test Volume per Attributes setter/getters //----------------------------------------------------------------- @Test public void testSetGetVolumePerAttributesWithInvalidAttributes() throws Exception { AudioAttributes nullAttributes = null; assertThrows(NullPointerException.class, () -> mAudioManager.getMaxVolumeIndexForAttributes(nullAttributes)); assertThrows(NullPointerException.class, () -> mAudioManager.getMinVolumeIndexForAttributes(nullAttributes)); assertThrows(NullPointerException.class, () -> mAudioManager.getVolumeIndexForAttributes(nullAttributes)); assertThrows(NullPointerException.class, () -> mAudioManager.setVolumeIndexForAttributes( nullAttributes, 0 /*index*/, 0/*flags*/)); } @Test public void testSetGetVolumePerAttributes() { for (int usage : AudioAttributes.SDK_USAGES) { if (usage == AudioAttributes.USAGE_UNKNOWN) { continue; } AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build(); int indexMin = 0; int indexMax = 0; int index = 0; Exception ex = null; try { indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aaForUsage); } catch (Exception e) { ex = e; // unexpected } assertNull("Exception was thrown for valid attributes", ex); ex = null; try { indexMin = mAudioManager.getMinVolumeIndexForAttributes(aaForUsage); } catch (Exception e) { ex = e; // unexpected } assertNull("Exception was thrown for valid attributes", ex); ex = null; try { index = mAudioManager.getVolumeIndexForAttributes(aaForUsage); } catch (Exception e) { ex = e; // unexpected } assertNull("Exception was thrown for valid attributes", ex); ex = null; try { mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMin, 0/*flags*/); } catch (Exception e) { ex = e; // unexpected } assertNull("Exception was thrown for valid attributes", ex); index = mAudioManager.getVolumeIndexForAttributes(aaForUsage); assertEquals(index, indexMin); mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMax, 0/*flags*/); index = mAudioManager.getVolumeIndexForAttributes(aaForUsage); assertEquals(index, indexMax); } } //----------------------------------------------------------------- // Test register/unregister VolumeGroupCallback //----------------------------------------------------------------- @Test public void testVolumeGroupCallback() { List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); assertTrue(audioVolumeGroups.size() > 0); AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper(); mAudioManager.registerVolumeGroupCallback(getApplicationContext().getMainExecutor(), vgCbReceiver); final List<Integer> publicStreams = Ints.asList(AudioManager.getPublicStreamTypes()); try { // Validate Audio Volume Groups callback reception for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) { int volumeGroupId = audioVolumeGroup.getId(); // Set the receiver to filter only the current group callback vgCbReceiver.setExpectedVolumeGroup(volumeGroupId); List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes(); int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes(); int index = 0; int indexMax = 0; int indexMin = 0; // Set the volume per attributes (if valid) and wait the callback for (final AudioAttributes aa : avgAttributes) { if (aa.equals(DEFAULT_ATTRIBUTES)) { // Some volume groups may not have valid attributes, used for internal // volume management like patch/rerouting // so bailing out strategy retrieval from attributes continue; } index = mAudioManager.getVolumeIndexForAttributes(aa); indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa); indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa); index = incrementVolumeIndex(index, indexMin, indexMax); vgCbReceiver.setExpectedVolumeGroup(volumeGroupId); mAudioManager.setVolumeIndexForAttributes(aa, index, 0/*flags*/); assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged( AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS)); int readIndex = mAudioManager.getVolumeIndexForAttributes(aa); assertEquals(readIndex, index); } // Set the volume per stream type (if valid) and wait the callback for (final int avgStreamType : avgStreamTypes) { if (avgStreamType == AudioSystem.STREAM_DEFAULT) { // Some Volume Groups may not have corresponding stream types as they // intends to address volume setting per attributes to avoid adding new // stream type and going on deprecating the stream type even for volume // so bailing out strategy retrieval from stream type continue; } if (!publicStreams.contains(avgStreamType) || avgStreamType == AudioManager.STREAM_ACCESSIBILITY) { // Limit scope of test to public stream that do not require any // permission (e.g. Changing ACCESSIBILITY is subject to permission). continue; } index = mAudioManager.getStreamVolume(avgStreamType); indexMax = mAudioManager.getStreamMaxVolume(avgStreamType); indexMin = mAudioManager.getStreamMinVolumeInt(avgStreamType); index = incrementVolumeIndex(index, indexMin, indexMax); vgCbReceiver.setExpectedVolumeGroup(volumeGroupId); mAudioManager.setStreamVolume(avgStreamType, index, 0/*flags*/); assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged( AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS)); int readIndex = mAudioManager.getStreamVolume(avgStreamType); assertEquals(index, readIndex); } } } finally { mAudioManager.unregisterVolumeGroupCallback(vgCbReceiver); } } }