summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vlad Popa <pvlad@google.com> 2024-01-31 18:35:50 -0800
committer Vlad Popa <pvlad@google.com> 2024-02-12 11:22:17 -0800
commit2b259700c0bc69681ef56e90a436cda921047a1c (patch)
tree6b7a29294f3cbcff87f6bc8426149bb46e0d5372
parentcd452abe41ba3f29c24834ed1498fbc50a64ee6e (diff)
VolRef: add initial UT to cover volume APIs
Trying to improve the code coverage by covering all the test related APIs in AudioService. Currently only trying to obtain a full method coverage. More tests to come that will cover more branches and more volume requirements. Copied test methods from AudioDeviceManagerTest and DeviceVolumeBehaviorTest into VolumeHelperTest in order to have a central class that manages all the volume related API tests. Test: build with EMMA_INSTRUMENT=true EMMA_INSTRUMENT_FRAMEWORK=true SKIP_BOOT_JARS_CHECK=true WITH_DEXPREOPT=false, atest VolumeHelperTest --experimental-coverage Bug: 323273677 Change-Id: I54b243979b71c03f145808688bb36c702da621e4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java48
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java5
-rw-r--r--services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java601
9 files changed, 692 insertions, 29 deletions
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 04deb0271c06..329e388db3d4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -963,6 +963,8 @@ public class AudioService extends IAudioService.Stub
private final HardeningEnforcer mHardeningEnforcer;
+ private final AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
+
private final Object mSupportedSystemUsagesLock = new Object();
@GuardedBy("mSupportedSystemUsagesLock")
private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -973,6 +975,13 @@ public class AudioService extends IAudioService.Stub
return "card=" + card + ";device=" + device;
}
+ private static class AudioVolumeGroupHelper extends AudioVolumeGroupHelperBase {
+ @Override
+ public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ return AudioVolumeGroup.getAudioVolumeGroups();
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private AudioService mService;
@@ -982,6 +991,7 @@ public class AudioService extends IAudioService.Stub
AudioSystemAdapter.getDefaultAdapter(),
SystemServerAdapter.getDefaultAdapter(context),
SettingsAdapter.getDefaultAdapter(),
+ new AudioVolumeGroupHelper(),
new DefaultAudioPolicyFacade(),
null);
@@ -1061,16 +1071,19 @@ public class AudioService extends IAudioService.Stub
/**
* @param context
* @param audioSystem Adapter for {@link AudioSystem}
- * @param systemServer Adapter for privilieged functionality for system server components
+ * @param systemServer Adapter for privileged functionality for system server components
* @param settings Adapter for {@link Settings}
+ * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
+ * @param audioPolicy Interface of a facade to IAudioPolicyManager
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
*/
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings,
- AudioPolicyFacade audioPolicy, @Nullable Looper looper) {
- this (context, audioSystem, systemServer, settings, audioPolicy, looper,
- context.getSystemService(AppOpsManager.class),
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper) {
+ this (context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
+ audioPolicy, looper, context.getSystemService(AppOpsManager.class),
PermissionEnforcer.fromContext(context));
}
@@ -1079,14 +1092,18 @@ public class AudioService extends IAudioService.Stub
* @param audioSystem Adapter for {@link AudioSystem}
* @param systemServer Adapter for privilieged functionality for system server components
* @param settings Adapter for {@link Settings}
+ * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
+ * @param audioPolicy Interface of a facade to IAudioPolicyManager
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
+ * @param appOps {@link AppOpsManager} system service
+ * @param enforcer Used for permission enforcing
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings,
- AudioPolicyFacade audioPolicy, @Nullable Looper looper, AppOpsManager appOps,
- @NonNull PermissionEnforcer enforcer) {
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer) {
super(enforcer);
sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()"));
mContext = context;
@@ -1095,6 +1112,7 @@ public class AudioService extends IAudioService.Stub
mAudioSystem = audioSystem;
mSystemServer = systemServer;
+ mAudioVolumeGroupHelper = audioVolumeGroupHelper;
mSettings = settings;
mAudioPolicy = audioPolicy;
mPlatformType = AudioSystem.getPlatformType(context);
@@ -2102,7 +2120,7 @@ public class AudioService extends IAudioService.Stub
// verify permissions
super.getAudioVolumeGroups_enforcePermission();
- return AudioVolumeGroup.getAudioVolumeGroups();
+ return mAudioVolumeGroupHelper.getAudioVolumeGroups();
}
private void checkAllAliasStreamVolumes() {
@@ -3801,7 +3819,7 @@ public class AudioService extends IAudioService.Stub
}
/**
- * Loops on aliasted stream, update the mute cache attribute of each
+ * Loops on aliased stream, update the mute cache attribute of each
* {@see AudioService#VolumeStreamState}, and then apply the change.
* It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream
* and aliases before mute change changed and after.
@@ -4038,18 +4056,6 @@ public class AudioService extends IAudioService.Stub
}
}
- @Nullable
- private AudioVolumeGroup getAudioVolumeGroupById(int volumeGroupId) {
- for (AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
- if (avg.getId() == volumeGroupId) {
- return avg;
- }
- }
-
- Log.e(TAG, ": invalid volume group id: " + volumeGroupId + " requested");
- return null;
- }
-
@Override
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_SETTINGS_PRIVILEGED,
@@ -8240,7 +8246,7 @@ public class AudioService extends IAudioService.Stub
index = 1;
}
// Set the volume index
- AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+ mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
}
@GuardedBy("AudioService.VolumeStreamState.class")
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 49ab19a816dc..7202fa286453 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -551,6 +551,11 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
return AudioSystem.setStreamVolumeIndexAS(stream, index, device);
}
+ /** Same as {@link AudioSystem#setVolumeIndexForAttributes(AudioAttributes, int, int)} */
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ return AudioSystem.setVolumeIndexForAttributes(attributes, index, device);
+ }
+
/**
* Same as {@link AudioSystem#setPhoneState(int, int)}
* @param state
diff --git a/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java b/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java
new file mode 100644
index 000000000000..6f4de5bc5f81
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 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.server.audio;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.media.audiopolicy.AudioVolumeGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Abstract class for {@link AudioVolumeGroup} related helper methods. */
+@VisibleForTesting(visibility = PACKAGE)
+public class AudioVolumeGroupHelperBase {
+ public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ return new ArrayList<>();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index fc5819de861f..e756082bc912 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -69,6 +69,7 @@ public class AbsoluteVolumeBehaviorTest {
private AudioSystemAdapter mSpyAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioService mAudioService;
@@ -93,9 +94,11 @@ public class AbsoluteVolumeBehaviorTest {
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
- mSettingsAdapter, mMockAudioPolicy, mTestLooper.getLooper()) {
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
+ mTestLooper.getLooper()) {
@Override
public int getDeviceForStream(int stream) {
return AudioSystem.DEVICE_OUT_SPEAKER;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index d4d312894053..3623012b348f 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -56,6 +56,7 @@ public class AudioDeviceVolumeManagerTest {
private AudioSystemAdapter mSpyAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioPolicyFacade mAudioPolicyMock = mock(AudioPolicyFacade.class);
@@ -71,8 +72,10 @@ public class AudioDeviceVolumeManagerTest {
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
- mSettingsAdapter, mAudioPolicyMock, mTestLooper.getLooper()) {
+ mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
+ mTestLooper.getLooper()) {
@Override
public int getDeviceForStream(int stream) {
return AudioSystem.DEVICE_OUT_SPEAKER;
@@ -82,8 +85,9 @@ public class AudioDeviceVolumeManagerTest {
mTestLooper.dispatchAll();
}
+ // ------------ AudioDeviceVolumeManager related tests ------------
@Test
- public void testSetDeviceVolume() {
+ public void setDeviceVolume_checkIndex() {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
@@ -110,7 +114,7 @@ public class AudioDeviceVolumeManagerTest {
@Test
@RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
- public void testConfigurablePreScaleAbsoluteVolume() throws Exception {
+ public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
@@ -159,7 +163,7 @@ public class AudioDeviceVolumeManagerTest {
@Test
@RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
- public void testDisablePreScaleAbsoluteVolume() throws Exception {
+ public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index e565faa1c00b..634877eb2539 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -60,6 +60,7 @@ public class AudioServiceTest {
private Context mContext;
private AudioSystemAdapter mSpyAudioSystem;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
@Spy private NoOpSystemServerAdapter mSpySystemServer;
@Mock private AppOpsManager mMockAppOpsManager;
@@ -80,11 +81,12 @@ public class AudioServiceTest {
mContext = InstrumentationRegistry.getTargetContext();
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer,
- mSettingsAdapter, mMockAudioPolicy, null, mMockAppOpsManager,
- mMockPermissionEnforcer);
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, null,
+ mMockAppOpsManager, mMockPermissionEnforcer);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
index f5862acb2811..8dfcc1843fed 100644
--- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -50,6 +50,7 @@ public class DeviceVolumeBehaviorTest {
private AudioSystemAdapter mAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioPolicyFacade mAudioPolicyMock = mock(AudioPolicyFacade.class);
@@ -71,8 +72,10 @@ public class DeviceVolumeBehaviorTest {
mAudioSystem = new NoOpAudioSystemAdapter();
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mAudioSystem, mSystemServer,
- mSettingsAdapter, mAudioPolicyMock, mTestLooper.getLooper());
+ mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
+ mTestLooper.getLooper());
mTestLooper.dispatchAll();
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 0eac718c2f14..96ac5d251ffd 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -137,6 +137,11 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
}
@Override
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
@NonNull
public ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes, boolean forVolume) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
new file mode 100644
index 000000000000..4b9f8b73df86
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2024 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.server.audio;
+
+import static android.media.AudioManager.ADJUST_LOWER;
+import static android.media.AudioManager.DEVICE_OUT_DEFAULT;
+import static android.media.AudioManager.DEVICE_OUT_SPEAKER;
+import static android.media.AudioManager.DEVICE_OUT_USB_DEVICE;
+import static android.media.AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_NOTIFICATION;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+
+import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.media.VolumeInfo;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.Looper;
+import android.os.PermissionEnforcer;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class VolumeHelperTest {
+ private static final Integer VOLUME_CHANGE_TIMEOUT_MS = 500;
+
+ private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private MyAudioService mAudioService;
+
+ private Context mContext;
+
+ private AudioSystemAdapter mSpyAudioSystem;
+ private SettingsAdapter mSettingsAdapter;
+ @Spy
+ private NoOpSystemServerAdapter mSpySystemServer;
+ @Mock
+ private AppOpsManager mMockAppOpsManager;
+ @Mock
+ private PermissionEnforcer mMockPermissionEnforcer;
+ @Mock
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
+
+ private final AudioPolicyFacade mFakeAudioPolicy = lookbackAudio -> false;
+
+ private AudioVolumeGroup mAudioMusicVolumeGroup;
+
+ private TestLooper mTestLooper;
+
+ public static final int[] BASIC_VOLUME_BEHAVIORS = {
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED
+ };
+
+ private static class MyAudioService extends AudioService {
+ private SparseIntArray mStreamDevice = new SparseIntArray();
+
+ MyAudioService(Context context, AudioSystemAdapter audioSystem,
+ SystemServerAdapter systemServer, SettingsAdapter settings,
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper, AppOpsManager appOps,
+ @NonNull PermissionEnforcer enforcer) {
+ super(context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
+ audioPolicy, looper, appOps, enforcer);
+ }
+
+ public void setDeviceForStream(int stream, int device) {
+ mStreamDevice.put(stream, device);
+ }
+
+ @Override
+ public int getDeviceForStream(int stream) {
+ if (mStreamDevice.indexOfKey(stream) < 0) {
+ return DEVICE_OUT_SPEAKER;
+ }
+ return mStreamDevice.get(stream);
+ }
+ }
+
+ private static class TestDeviceVolumeBehaviorDispatcherStub
+ extends IDeviceVolumeBehaviorDispatcher.Stub {
+
+ private AudioDeviceAttributes mDevice;
+ private int mVolumeBehavior;
+ private int mTimesCalled;
+
+ @Override
+ public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ mDevice = device;
+ mVolumeBehavior = volumeBehavior;
+ mTimesCalled++;
+ }
+
+ public void reset() {
+ mTimesCalled = 0;
+ mVolumeBehavior = DEVICE_VOLUME_BEHAVIOR_UNSET;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mTestLooper = new TestLooper();
+
+ mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+ mSettingsAdapter = new NoOpSettingsAdapter();
+ when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+ mAudioMusicVolumeGroup = getStreamTypeVolumeGroup(STREAM_MUSIC);
+ if (mAudioMusicVolumeGroup != null) {
+ when(mAudioVolumeGroupHelper.getAudioVolumeGroups()).thenReturn(
+ List.of(mAudioMusicVolumeGroup));
+ }
+
+ mAudioService = new MyAudioService(mContext, mSpyAudioSystem, mSpySystemServer,
+ mSettingsAdapter, mAudioVolumeGroupHelper, mFakeAudioPolicy,
+ mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer);
+
+ mTestLooper.dispatchAll();
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ Manifest.permission.MODIFY_AUDIO_ROUTING);
+ }
+
+ private AudioVolumeGroup getStreamTypeVolumeGroup(int streamType) {
+ // get the volume group from the AudioManager to pass permission checks
+ // when requesting from real service
+ final List<AudioVolumeGroup> audioVolumeGroups = AudioManager.getAudioVolumeGroups();
+ for (AudioVolumeGroup vg : audioVolumeGroups) {
+ for (int stream : vg.getLegacyStreamTypes()) {
+ if (stream == streamType) {
+ return vg;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void check_setMasterMute() {
+ mAudioService.setMasterMute(true, /*flags=*/0, mContext.getOpPackageName(),
+ mContext.getUserId(), /*attributionTag*/"");
+
+ assertTrue(mAudioService.isMasterMute());
+ }
+
+ @Test
+ public void check_isStreamAffectedByMute() {
+ assertFalse(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
+ }
+
+ /** --------------- Volume Stream APIs --------------- */
+ @Test
+ public void setStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ int newIndex = circularIncrementAndGetVolume(STREAM_MUSIC);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.setStreamVolume(STREAM_MUSIC, newIndex, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, timeout(VOLUME_CHANGE_TIMEOUT_MS)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), eq(newIndex), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void adjustStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, timeout(VOLUME_CHANGE_TIMEOUT_MS)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void adjustVolume_callsASSetStreamVolumeIndex() throws Exception {
+ mAudioService.adjustVolume(ADJUST_LOWER, /*flags=*/0);
+ mTestLooper.dispatchAll();
+
+ // for checking device we need to mock AudioSystem#generateAudioDeviceTypesSet and
+ // AudioProductStrategy#getAudioAttributesForStrategyWithLegacyStreamType
+ verify(mSpyAudioSystem,
+ timeout(VOLUME_CHANGE_TIMEOUT_MS).atLeast(1)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_DEFAULT));
+ }
+
+ @Test
+ public void adjustSuggestedStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ mAudioService.adjustSuggestedStreamVolume(ADJUST_LOWER, STREAM_NOTIFICATION, /*flags=*/0);
+ mTestLooper.dispatchAll();
+
+ // for checking device we need to mock AudioSystem#generateAudioDeviceTypesSet and
+ // AudioProductStrategy#getAudioAttributesForStrategyWithLegacyStreamType
+ verify(mSpyAudioSystem, timeout(VOLUME_CHANGE_TIMEOUT_MS)).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_DEFAULT));
+ }
+
+ @Test
+ public void handleVolumeKey_callsASSetStreamVolumeIndex() throws Exception {
+ final KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.handleVolumeKey(keyEvent, /*isOnTv=*/false, mContext.getOpPackageName(),
+ "adjustSuggestedStreamVolume_callsAudioSystemSetStreamVolumeIndex");
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, timeout(VOLUME_CHANGE_TIMEOUT_MS)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ /** --------------- Volume Group APIs --------------- */
+
+ @Test
+ public void setVolumeGroupVolumeIndex_callsASSetVolumeIndexForAttributes() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
+ circularIncrementAndGetVolume(STREAM_MUSIC), /*flags=*/0,
+ mContext.getOpPackageName(), /*attributionTag*/null);
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, timeout(VOLUME_CHANGE_TIMEOUT_MS)).setVolumeIndexForAttributes(
+ any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void adjustVolumeGroupVolume_callsASSetVolumeIndexForAttributes() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustVolumeGroupVolume(mAudioMusicVolumeGroup.getId(),
+ ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, timeout(VOLUME_CHANGE_TIMEOUT_MS)).setVolumeIndexForAttributes(
+ any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void check_getVolumeGroupVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ int newIndex = circularIncrementAndGetVolume(STREAM_MUSIC);
+
+ mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
+ newIndex, /*flags=*/0, mContext.getOpPackageName(), /*attributionTag*/null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ newIndex);
+ assertEquals(mAudioService.getStreamVolume(STREAM_MUSIC),
+ newIndex);
+ }
+
+ @Test
+ public void check_getVolumeGroupMaxVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.getVolumeGroupMaxVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getStreamMaxVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_getVolumeGroupMinVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.getVolumeGroupMinVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getStreamMinVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_getLastAudibleVolumeForVolumeGroup() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(
+ mAudioService.getLastAudibleVolumeForVolumeGroup(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getLastAudibleStreamVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_isVolumeGroupMuted() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.isVolumeGroupMuted(mAudioMusicVolumeGroup.getId()),
+ mAudioService.isStreamMute(STREAM_MUSIC));
+ }
+
+ /** ----------------- AudioDeviceVolumeManager ----------------- */
+ @Test
+ public void setDeviceVolume_checkIndex() {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ final int minIndex = am.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = am.getStreamMaxVolume(STREAM_MUSIC);
+ final int midIndex = (minIndex + maxIndex) / 2;
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final VolumeInfo volMin = new VolumeInfo.Builder(volMedia).setVolumeIndex(minIndex).build();
+ final VolumeInfo volMid = new VolumeInfo.Builder(volMedia).setVolumeIndex(midIndex).build();
+ final AudioDeviceAttributes usbDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_USB_DEVICE, /*address*/ "bla");
+
+ mAudioService.setDeviceVolume(volMin, usbDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getDeviceVolume(volMin, usbDevice,
+ mContext.getOpPackageName()), volMin);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+
+ mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertEquals(mAudioService.getDeviceVolume(volMid, usbDevice,
+ mContext.getOpPackageName()), volMid);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ final int minIndex = am.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = am.getStreamMaxVolume(STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "fake_ble");
+ final int maxPreScaleIndex = 3;
+ final float[] preScale = new float[maxPreScaleIndex];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final int targetIndex = (int) (preScale[i] * maxIndex);
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(
+ mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()),
+ volCur);
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, targetIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(
+ mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()),
+ volIndex4);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ final int minIndex = am.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = am.getStreamMaxVolume(STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "bla");
+ final int maxPreScaleIndex = 3;
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ /** ---------------- DeviceVolumeBehaviorTest ---------------- */
+ @Test
+ public void setDeviceVolumeBehavior_changesDeviceVolumeBehavior() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ int actualBehavior = mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT);
+
+ assertWithMessage("Expected volume behavior to be " + behavior
+ + " but was instead " + actualBehavior)
+ .that(actualBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToNewBehavior_triggersDeviceVolumeBehaviorDispatcher() {
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ dispatcher.reset();
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertThat(dispatcher.mTimesCalled).isEqualTo(1);
+ assertThat(dispatcher.mDevice).isEqualTo(DEVICE_SPEAKER_OUT);
+ assertWithMessage("Expected dispatched volume behavior to be " + behavior
+ + " but was instead " + dispatcher.mVolumeBehavior)
+ .that(dispatcher.mVolumeBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToSameBehavior_doesNotTriggerDeviceVolumeBehaviorDispatcher() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void unregisterDeviceVolumeBehaviorDispatcher_noLongerTriggered() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(false, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void setDeviceVolumeBehavior_checkIsVolumeFixed() throws Exception {
+ when(mSpyAudioSystem.getDevicesForAttributes(any(), anyBoolean())).thenReturn(
+ new ArrayList<>(List.of(DEVICE_SPEAKER_OUT)));
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+
+ assertTrue(mAudioService.isVolumeFixed());
+ }
+
+
+ private int circularIncrementAndGetVolume(int streamType) throws Exception {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ final int streamMinVolume = am.getStreamMinVolume(streamType);
+ final int streamMaxVolume = am.getStreamMaxVolume(streamType);
+
+ int streamVolume = mAudioService.getStreamVolume(streamType);
+ if (streamVolume + 1 > streamMaxVolume) {
+ return streamMinVolume;
+ }
+ return streamVolume + 1;
+ }
+}