diff options
Diffstat (limited to 'media/tests')
12 files changed, 1218 insertions, 0 deletions
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp new file mode 100644 index 000000000000..ed3383752695 --- /dev/null +++ b/media/tests/AudioPolicyTest/Android.bp @@ -0,0 +1,17 @@ +android_test { + name: "audiopolicytest", + srcs: ["**/*.java"], + libs: [ + "android.test.runner", + "android.test.base", + ], + static_libs: [ + "mockito-target-minus-junit4", + "androidx.test.rules", + "android-ex-camera2", + "testng", + ], + platform_apis: true, + certificate: "platform", + resource_dirs: ["res"], +} diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml new file mode 100644 index 000000000000..ae5bfaf9162e --- /dev/null +++ b/media/tests/AudioPolicyTest/AndroidManifest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.audiopolicytest"> + + <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> + <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> + + <application> + <uses-library android:name="android.test.runner" /> + <activity android:label="@string/app_name" android:name="AudioPolicyTest" + android:screenOrientation="landscape"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + + <!--instrumentation android:name=".AudioPolicyTestRunner" + android:targetPackage="com.android.audiopolicytest" + android:label="AudioManager policy oriented integration tests InstrumentationRunner"> + </instrumentation--> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.audiopolicytest" + android:label="AudioManager policy oriented integration tests InstrumentationRunner"> + </instrumentation> +</manifest> diff --git a/media/tests/AudioPolicyTest/AndroidTest.xml b/media/tests/AudioPolicyTest/AndroidTest.xml new file mode 100644 index 000000000000..f3ca9a165d1b --- /dev/null +++ b/media/tests/AudioPolicyTest/AndroidTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Runs Media Framework Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="audiopolicytest.apk" /> + </target_preparer> + + <option name="test-tag" value="AudioPolicyTest" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.audiopolicytest" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml b/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml new file mode 100644 index 000000000000..17fdba6f7c15 --- /dev/null +++ b/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> +</LinearLayout> diff --git a/media/tests/AudioPolicyTest/res/values/strings.xml b/media/tests/AudioPolicyTest/res/values/strings.xml new file mode 100644 index 000000000000..036592770450 --- /dev/null +++ b/media/tests/AudioPolicyTest/res/values/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- name of the app [CHAR LIMIT=25]--> + <string name="app_name">Audio Policy APIs Tests</string> +</resources> diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java new file mode 100644 index 000000000000..ff21f0adf04f --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java @@ -0,0 +1,327 @@ +/* + * 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 org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.testng.Assert.assertThrows; + +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.media.audiopolicy.AudioProductStrategy; +import android.media.audiopolicy.AudioVolumeGroup; +import android.util.Log; + +import com.google.common.primitives.Ints; + +import java.util.List; + +public class AudioManagerTest extends AudioVolumesTestBase { + private static final String TAG = "AudioManagerTest"; + + //----------------------------------------------------------------- + // Test getAudioProductStrategies and validate strategies + //----------------------------------------------------------------- + public void testGetAndValidateProductStrategies() throws Exception { + 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 + //----------------------------------------------------------------- + + public void testGetAndValidateVolumeGroups() throws Exception { + 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(sDefaultAttributes)) { + // 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 + //----------------------------------------------------------------- + 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*/)); + } + + public void testSetGetVolumePerAttributes() throws Exception { + 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 + //----------------------------------------------------------------- + public void testVolumeGroupCallback() throws Exception { + List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); + assertTrue(audioVolumeGroups.size() > 0); + + AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper(); + mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver); + + final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES); + 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(sDefaultAttributes)) { + // 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); + } + } +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java new file mode 100644 index 000000000000..e0c7b223a2e4 --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java @@ -0,0 +1,38 @@ +/* + * 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 android.app.Activity; +import android.os.Bundle; + +public class AudioPolicyTest extends Activity { + + public AudioPolicyTest() { + } + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.audiopolicytest); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java new file mode 100644 index 000000000000..c0f596b974e1 --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java @@ -0,0 +1,213 @@ +/* + * 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 org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.media.AudioAttributes; +import android.media.AudioSystem; +import android.media.audiopolicy.AudioProductStrategy; +import android.media.audiopolicy.AudioVolumeGroup; +import android.util.Log; + +import java.util.List; + +public class AudioProductStrategyTest extends AudioVolumesTestBase { + private static final String TAG = "AudioProductStrategyTest"; + + //----------------------------------------------------------------- + // Test getAudioProductStrategies and validate strategies + //----------------------------------------------------------------- + public void testGetProductStrategies() throws Exception { + List<AudioProductStrategy> audioProductStrategies = + AudioProductStrategy.getAudioProductStrategies(); + + assertNotNull(audioProductStrategies); + assertTrue(audioProductStrategies.size() > 0); + + for (final AudioProductStrategy aps : audioProductStrategies) { + assertTrue(aps.getId() >= 0); + + AudioAttributes aa = aps.getAudioAttributes(); + assertNotNull(aa); + + // Ensure API consistency + assertTrue(aps.supportsAudioAttributes(aa)); + + int streamType = aps.getLegacyStreamTypeForAudioAttributes(aa); + if (streamType == AudioSystem.STREAM_DEFAULT) { + // bailing out test for volume group APIs consistency + continue; + } + final int volumeGroupFromStream = aps.getVolumeGroupIdForLegacyStreamType(streamType); + final int volumeGroupFromAttributes = aps.getVolumeGroupIdForAudioAttributes(aa); + assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP); + assertEquals(volumeGroupFromStream, volumeGroupFromAttributes); + } + } + + //----------------------------------------------------------------- + // Test stream to/from attributes conversion + //----------------------------------------------------------------- + public void testAudioAttributesFromStreamTypes() throws Exception { + List<AudioProductStrategy> audioProductStrategies = + AudioProductStrategy.getAudioProductStrategies(); + + assertNotNull(audioProductStrategies); + assertTrue(audioProductStrategies.size() > 0); + + for (final int streamType : PUBLIC_STREAM_TYPES) { + AudioAttributes aaFromStreamType = + AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType( + streamType); + + // No strategy found for this stream type or no attributes defined for the strategy + // hosting this stream type; Bailing out the test, just ensure that any request + // for reciproque API with the unknown attributes would return default stream + // for volume control, aka STREAM_MUSIC. + if (aaFromStreamType.equals(sInvalidAttributes)) { + assertEquals(AudioSystem.STREAM_MUSIC, + AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes( + aaFromStreamType)); + } else { + // Attributes are valid, i.e. a strategy was found supporting this stream type + // with valid attributes. Ensure reciproque works fine + int streamTypeFromAttributes = + AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes( + aaFromStreamType); + assertEquals("stream " + AudioSystem.streamToString(streamType) + "(" + + streamType + ") expected to match attributes " + + aaFromStreamType.toString() + " got instead stream " + + AudioSystem.streamToString(streamTypeFromAttributes) + "(" + + streamTypeFromAttributes + ") expected to match attributes ", + streamType, streamTypeFromAttributes); + } + + // Now identify the strategy supporting this stream type, ensure uniqueness + boolean strategyFound = false; + for (final AudioProductStrategy aps : audioProductStrategies) { + AudioAttributes aaFromAps = + aps.getAudioAttributesForLegacyStreamType(streamType); + + if (aaFromAps == null) { + // not this one... + continue; + } + // Got it! + assertFalse("Unique ProductStrategy shall match for a given stream type", + strategyFound); + strategyFound = true; + + // Ensure getters aligned + assertEquals(aaFromStreamType, aaFromAps); + assertTrue(aps.supportsAudioAttributes(aaFromStreamType)); + + // Ensure reciproque works fine + assertEquals(streamType, + aps.getLegacyStreamTypeForAudioAttributes(aaFromStreamType)); + + // Ensure consistency of volume group getter API + final int volumeGroupFromStream = + aps.getVolumeGroupIdForLegacyStreamType(streamType); + final int volumeGroupFromAttributes = + aps.getVolumeGroupIdForAudioAttributes(aaFromStreamType); + assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP); + assertEquals(volumeGroupFromStream, volumeGroupFromAttributes); + } + if (!strategyFound) { + // No strategy found, ensure volume control is MUSIC + assertEquals(AudioSystem.STREAM_MUSIC, + AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes( + aaFromStreamType)); + } + } + } + + public void testAudioAttributesToStreamTypes() throws Exception { + List<AudioProductStrategy> audioProductStrategies = + AudioProductStrategy.getAudioProductStrategies(); + + assertNotNull(audioProductStrategies); + assertTrue(audioProductStrategies.size() > 0); + + for (int usage : AudioAttributes.SDK_USAGES) { + AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build(); + + int streamTypeFromUsage = + AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes( + aaForUsage); + + // Cannot be undefined, always shall fall back on a valid stream type + // to be able to control the volume + assertNotEquals(streamTypeFromUsage, AudioSystem.STREAM_DEFAULT); + + Log.w(TAG, "GUSTAVE aaForUsage=" + aaForUsage.toString()); + + // Now identify the strategy hosting these Audio Attributes and ensure informations + // matches. + // Now identify the strategy supporting this stream type, ensure uniqueness + boolean strategyFound = false; + for (final AudioProductStrategy aps : audioProductStrategies) { + if (!aps.supportsAudioAttributes(aaForUsage)) { + // Not this one + continue; + } + // Got it! + String msg = "Unique ProductStrategy shall match for a given audio attributes " + + aaForUsage.toString() + " already associated also matches with" + + aps.toString(); + assertFalse(msg, strategyFound); + strategyFound = true; + + // It may not return the expected stream type if the strategy does not have + // associated stream type. + // Behavior of member function getLegacyStreamTypeForAudioAttributes is + // different than getLegacyStreamTypeForStrategyWithAudioAttributes since it + // does not fallback on MUSIC stream type for volume operation + int streamTypeFromAps = aps.getLegacyStreamTypeForAudioAttributes(aaForUsage); + if (streamTypeFromAps == AudioSystem.STREAM_DEFAULT) { + // No stream type assigned to this strategy + // Expect static API to return default stream type for volume (aka MUSIC) + assertEquals("Strategy (" + aps.toString() + ") has no associated stream " + + ", must fallback on MUSIC stream as default", + streamTypeFromUsage, AudioSystem.STREAM_MUSIC); + } else { + assertEquals("Attributes " + aaForUsage.toString() + " associated to stream " + + AudioSystem.streamToString(streamTypeFromUsage) + + " are supported by strategy (" + aps.toString() + ") which reports " + + " these attributes are associated to stream " + + AudioSystem.streamToString(streamTypeFromAps), + streamTypeFromUsage, streamTypeFromAps); + + // Ensure consistency of volume group getter API + int volumeGroupFromStream = + aps.getVolumeGroupIdForLegacyStreamType(streamTypeFromAps); + int volumeGroupFromAttributes = + aps.getVolumeGroupIdForAudioAttributes(aaForUsage); + assertNotEquals( + volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP); + assertEquals(volumeGroupFromStream, volumeGroupFromAttributes); + } + } + if (!strategyFound) { + // No strategy found for the given attributes, the expected stream must be MUSIC + assertEquals(streamTypeFromUsage, AudioSystem.STREAM_MUSIC); + } + } + } +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java new file mode 100644 index 000000000000..0c1d52c57020 --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java @@ -0,0 +1,69 @@ +/* + * 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 org.junit.Assert.assertNotNull; + +import android.media.AudioManager; +import android.util.Log; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + + +final class AudioVolumeGroupCallbackHelper extends AudioManager.VolumeGroupCallback { + private static final String TAG = "AudioVolumeGroupCallbackHelper"; + public static final long ASYNC_TIMEOUT_MS = 800; + + private int mExpectedVolumeGroupId; + + private CountDownLatch mVolumeGroupChanged = null; + + void setExpectedVolumeGroup(int group) { + mVolumeGroupChanged = new CountDownLatch(1); + mExpectedVolumeGroupId = group; + } + + @Override + public void onAudioVolumeGroupChanged(int group, int flags) { + if (group != mExpectedVolumeGroupId) { + return; + } + if (mVolumeGroupChanged == null) { + Log.wtf(TAG, "Received callback but object not initialized"); + return; + } + if (mVolumeGroupChanged.getCount() <= 0) { + Log.i(TAG, "callback for group: " + group + " already received"); + return; + } + mVolumeGroupChanged.countDown(); + } + + public boolean waitForExpectedVolumeGroupChanged(long timeOutMs) { + assertNotNull("Call first setExpectedVolumeGroup before waiting...", mVolumeGroupChanged); + boolean timeoutReached = false; + if (mVolumeGroupChanged.getCount() == 0) { + // done already... + return true; + } + try { + timeoutReached = !mVolumeGroupChanged.await(ASYNC_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { } + return !timeoutReached; + } +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java new file mode 100644 index 000000000000..221f1f7fef17 --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java @@ -0,0 +1,180 @@ +/* + * 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 org.junit.Assert.assertEquals; +import static org.testng.Assert.assertThrows; + +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.audiopolicy.AudioVolumeGroup; +import android.media.audiopolicy.AudioVolumeGroupChangeHandler; + +import java.util.ArrayList; +import java.util.List; + +public class AudioVolumeGroupChangeHandlerTest extends AudioVolumesTestBase { + private static final String TAG = "AudioVolumeGroupChangeHandlerTest"; + + public void testRegisterInvalidCallback() throws Exception { + final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = + new AudioVolumeGroupChangeHandler(); + + audioAudioVolumeGroupChangedHandler.init(); + + assertThrows(NullPointerException.class, () -> { + AudioManager.VolumeGroupCallback nullCb = null; + audioAudioVolumeGroupChangedHandler.registerListener(nullCb); + }); + } + + public void testUnregisterInvalidCallback() throws Exception { + final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = + new AudioVolumeGroupChangeHandler(); + + audioAudioVolumeGroupChangedHandler.init(); + + final AudioVolumeGroupCallbackHelper cb = new AudioVolumeGroupCallbackHelper(); + audioAudioVolumeGroupChangedHandler.registerListener(cb); + + assertThrows(NullPointerException.class, () -> { + AudioManager.VolumeGroupCallback nullCb = null; + audioAudioVolumeGroupChangedHandler.unregisterListener(nullCb); + }); + audioAudioVolumeGroupChangedHandler.unregisterListener(cb); + } + + public void testRegisterUnregisterCallback() throws Exception { + final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = + new AudioVolumeGroupChangeHandler(); + + audioAudioVolumeGroupChangedHandler.init(); + final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper(); + + // Should not assert, otherwise test will fail + audioAudioVolumeGroupChangedHandler.registerListener(validCb); + + // Should not assert, otherwise test will fail + audioAudioVolumeGroupChangedHandler.unregisterListener(validCb); + } + + public void testCallbackReceived() throws Exception { + final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = + new AudioVolumeGroupChangeHandler(); + + audioAudioVolumeGroupChangedHandler.init(); + + final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper(); + audioAudioVolumeGroupChangedHandler.registerListener(validCb); + + List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); + assertTrue(audioVolumeGroups.size() > 0); + + try { + for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) { + int volumeGroupId = audioVolumeGroup.getId(); + + List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes(); + // Set the volume per attributes (if valid) and wait the callback + if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(sDefaultAttributes)) { + // Some volume groups may not have valid attributes, used for internal + // volume management like patch/rerouting + // so bailing out strategy retrieval from attributes + continue; + } + final AudioAttributes aa = avgAttributes.get(0); + + int index = mAudioManager.getVolumeIndexForAttributes(aa); + int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa); + int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa); + + final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax); + + // Set the receiver to filter only the current group callback + validCb.setExpectedVolumeGroup(volumeGroupId); + mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/); + assertTrue(validCb.waitForExpectedVolumeGroupChanged( + AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS)); + + final int readIndex = mAudioManager.getVolumeIndexForAttributes(aa); + assertEquals(readIndex, indexForAa); + } + } finally { + audioAudioVolumeGroupChangedHandler.unregisterListener(validCb); + } + } + + public void testMultipleCallbackReceived() throws Exception { + + final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler = + new AudioVolumeGroupChangeHandler(); + + audioAudioVolumeGroupChangedHandler.init(); + + final int callbackCount = 10; + final List<AudioVolumeGroupCallbackHelper> validCbs = + new ArrayList<AudioVolumeGroupCallbackHelper>(); + for (int i = 0; i < callbackCount; i++) { + validCbs.add(new AudioVolumeGroupCallbackHelper()); + } + for (final AudioVolumeGroupCallbackHelper cb : validCbs) { + audioAudioVolumeGroupChangedHandler.registerListener(cb); + } + + List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); + assertTrue(audioVolumeGroups.size() > 0); + + try { + for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) { + int volumeGroupId = audioVolumeGroup.getId(); + + List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes(); + // Set the volume per attributes (if valid) and wait the callback + if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(sDefaultAttributes)) { + // Some volume groups may not have valid attributes, used for internal + // volume management like patch/rerouting + // so bailing out strategy retrieval from attributes + continue; + } + AudioAttributes aa = avgAttributes.get(0); + + int index = mAudioManager.getVolumeIndexForAttributes(aa); + int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa); + int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa); + + final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax); + + // Set the receiver to filter only the current group callback + for (final AudioVolumeGroupCallbackHelper cb : validCbs) { + cb.setExpectedVolumeGroup(volumeGroupId); + } + mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/); + + for (final AudioVolumeGroupCallbackHelper cb : validCbs) { + assertTrue(cb.waitForExpectedVolumeGroupChanged( + AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS)); + } + int readIndex = mAudioManager.getVolumeIndexForAttributes(aa); + assertEquals(readIndex, indexForAa); + } + } finally { + for (final AudioVolumeGroupCallbackHelper cb : validCbs) { + audioAudioVolumeGroupChangedHandler.unregisterListener(cb); + } + } + } +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java new file mode 100644 index 000000000000..84b24b8fcab3 --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java @@ -0,0 +1,131 @@ +/* + * 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 org.junit.Assert.assertNotEquals; + +import android.media.AudioAttributes; +import android.media.AudioSystem; +import android.media.audiopolicy.AudioProductStrategy; +import android.media.audiopolicy.AudioVolumeGroup; + +import java.util.List; + +public class AudioVolumeGroupTest extends AudioVolumesTestBase { + private static final String TAG = "AudioVolumeGroupTest"; + + //----------------------------------------------------------------- + // Test getAudioVolumeGroups and validate groud id + //----------------------------------------------------------------- + public void testGetVolumeGroupsFromNonServiceCaller() throws Exception { + // The transaction behind getAudioVolumeGroups will fail. Check is done at binder level + // with policy service. Error is not reported, the list is just empty. + // Request must come from service components + List<AudioVolumeGroup> audioVolumeGroup = AudioVolumeGroup.getAudioVolumeGroups(); + + assertNotNull(audioVolumeGroup); + assertEquals(audioVolumeGroup.size(), 0); + } + + //----------------------------------------------------------------- + // Test getAudioVolumeGroups and validate groud id + //----------------------------------------------------------------- + public void testGetVolumeGroups() throws Exception { + // Through AudioManager, the transaction behind getAudioVolumeGroups will succeed + final List<AudioVolumeGroup> audioVolumeGroup = mAudioManager.getAudioVolumeGroups(); + assertNotNull(audioVolumeGroup); + assertTrue(audioVolumeGroup.size() > 0); + + final List<AudioProductStrategy> audioProductStrategies = + mAudioManager.getAudioProductStrategies(); + assertTrue(audioProductStrategies.size() > 0); + + for (final AudioVolumeGroup avg : audioVolumeGroup) { + int avgId = avg.getId(); + assertNotEquals(avgId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP); + + List<AudioAttributes> avgAttributes = avg.getAudioAttributes(); + assertNotNull(avgAttributes); + + final int[] avgStreamTypes = avg.getLegacyStreamTypes(); + assertNotNull(avgStreamTypes); + + // 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(sDefaultAttributes)) { + // 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 aps : audioProductStrategies) { + int groupId = aps.getVolumeGroupIdForAudioAttributes(aa); + if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) { + // Note that Audio Product Strategies are priority ordered, and the + // the first one matching the AudioAttributes will be used to identify + // the volume group associated to the request. + assertTrue(aps.supportsAudioAttributes(aa)); + assertEquals("Volume Group ID (" + avg.toString() + + "), and Volume group ID associated to Strategy (" + + aps.toString() + ") both supporting attributes " + + aa.toString() + " are mismatching", + avgId, groupId); + isVolumeGroupAssociatedToStrategy = true; + break; + } + } + assertTrue("Volume Group (" + avg.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 aps : audioProductStrategies) { + int groupId = aps.getVolumeGroupIdForLegacyStreamType(avgStreamType); + if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) { + + assertEquals("Volume Group ID (" + avg.toString() + + "), and Volume group ID associated to Strategy (" + + aps.toString() + ") both supporting stream " + + AudioSystem.streamToString(avgStreamType) + "(" + + avgStreamType + ") are mismatching", + avgId, groupId); + + isVolumeGroupAssociatedToStrategy = true; + break; + } + } + assertTrue("Volume Group (" + avg.toString() + + ") has no associated strategy for stream " + + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")", + isVolumeGroupAssociatedToStrategy); + } + } + } +} diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java new file mode 100644 index 000000000000..a17d65cf7376 --- /dev/null +++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java @@ -0,0 +1,146 @@ +/* + * 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 android.content.Context; +import android.content.pm.PackageManager; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.audiopolicy.AudioProductStrategy; +import android.media.audiopolicy.AudioVolumeGroup; +import android.test.ActivityInstrumentationTestCase2; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<AudioPolicyTest> { + public AudioManager mAudioManager; + Context mContext; + private Map<Integer, Integer> mOriginalStreamVolumes = new HashMap<>(); + private Map<Integer, Integer> mOriginalVolumeGroupVolumes = new HashMap<>(); + + // Default matches the invalid (empty) attributes from native. + // The difference is the input source default which is not aligned between native and java + public static final AudioAttributes sDefaultAttributes = + AudioProductStrategy.sDefaultAttributes; + + public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build(); + + public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL, + AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC, + AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION, + AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY }; + + public AudioVolumesTestBase() { + super("com.android.audiopolicytest", AudioPolicyTest.class); + } + + /** + * <p>Note: must be called with shell permission (MODIFY_AUDIO_ROUTING) + */ + private void storeAllVolumes() { + List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); + for (final AudioVolumeGroup avg : audioVolumeGroups) { + if (avg.getAudioAttributes().isEmpty()) { + // some volume group may not supports volume control per attributes + // like rerouting/patch since these groups are internal to audio policy manager + continue; + } + AudioAttributes avgAttributes = sDefaultAttributes; + for (final AudioAttributes aa : avg.getAudioAttributes()) { + if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) { + avgAttributes = aa; + break; + } + } + if (avgAttributes.equals(sDefaultAttributes)) { + // This shall not happen, however, not purpose of this base class. + // so bailing out. + continue; + } + mOriginalVolumeGroupVolumes.put( + avg.getId(), mAudioManager.getVolumeIndexForAttributes(avgAttributes)); + } + } + + /** + * <p>Note: must be called with shell permission (MODIFY_AUDIO_ROUTING) + */ + private void restoreAllVolumes() { + List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups(); + for (Map.Entry<Integer, Integer> e : mOriginalVolumeGroupVolumes.entrySet()) { + for (final AudioVolumeGroup avg : audioVolumeGroups) { + if (avg.getId() == e.getKey()) { + assertTrue(!avg.getAudioAttributes().isEmpty()); + AudioAttributes avgAttributes = sDefaultAttributes; + for (final AudioAttributes aa : avg.getAudioAttributes()) { + if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) { + avgAttributes = aa; + break; + } + } + assertTrue(!avgAttributes.equals(sDefaultAttributes)); + mAudioManager.setVolumeIndexForAttributes( + avgAttributes, e.getValue(), AudioManager.FLAG_ALLOW_RINGER_MODES); + } + } + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mContext = getActivity(); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + + assertEquals(PackageManager.PERMISSION_GRANTED, + mContext.checkSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)); + + // Store the original volumes that that they can be recovered in tearDown(). + mOriginalStreamVolumes.clear(); + for (int streamType : PUBLIC_STREAM_TYPES) { + mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType)); + } + // Store the original volume per attributes so that they can be recovered in tearDown() + mOriginalVolumeGroupVolumes.clear(); + storeAllVolumes(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + // Recover the volume and the ringer mode that the test may have overwritten. + for (Map.Entry<Integer, Integer> e : mOriginalStreamVolumes.entrySet()) { + mAudioManager.setStreamVolume(e.getKey(), e.getValue(), + AudioManager.FLAG_ALLOW_RINGER_MODES); + } + + // Recover the original volume per attributes + restoreAllVolumes(); + } + + public static int resetVolumeIndex(int indexMin, int indexMax) { + return (indexMax + indexMin) / 2; + } + + public static int incrementVolumeIndex(int index, int indexMin, int indexMax) { + return (index + 1 > indexMax) ? resetVolumeIndex(indexMin, indexMax) : ++index; + } +} |