summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/AudioDeviceVolumeManager.java74
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl6
-rw-r--r--media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl31
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java126
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java167
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java9
7 files changed, 385 insertions, 30 deletions
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 0e9c99bf90d9..71042412e546 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -181,6 +181,80 @@ public class AudioDeviceVolumeManager {
}
}
+ /**
+ * Manages the OnDeviceVolumeBehaviorChangedListener listeners and
+ * DeviceVolumeBehaviorDispatcherStub
+ */
+ private final CallbackUtil.LazyListenerManager<OnDeviceVolumeBehaviorChangedListener>
+ mDeviceVolumeBehaviorChangedListenerMgr = new CallbackUtil.LazyListenerManager();
+
+ /**
+ * @hide
+ * Interface definition of a callback to be invoked when the volume behavior of an audio device
+ * is updated.
+ */
+ public interface OnDeviceVolumeBehaviorChangedListener {
+ /**
+ * Called on the listener to indicate that the volume behavior of a device has changed.
+ * @param device the audio device whose volume behavior changed
+ * @param volumeBehavior the new volume behavior of the audio device
+ */
+ void onDeviceVolumeBehaviorChanged(
+ @NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of changes to any device's volume behavior.
+ * @throws SecurityException if the caller doesn't hold the required permission
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public void addOnDeviceVolumeBehaviorChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnDeviceVolumeBehaviorChangedListener listener)
+ throws SecurityException {
+ mDeviceVolumeBehaviorChangedListenerMgr.addListener(executor, listener,
+ "addOnDeviceVolumeBehaviorChangedListener",
+ () -> new DeviceVolumeBehaviorDispatcherStub());
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener of changes to device volume behavior.
+ */
+
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public void removeOnDeviceVolumeBehaviorChangedListener(
+ @NonNull OnDeviceVolumeBehaviorChangedListener listener) {
+ mDeviceVolumeBehaviorChangedListenerMgr.removeListener(listener,
+ "removeOnDeviceVolumeBehaviorChangedListener");
+ }
+
+ private final class DeviceVolumeBehaviorDispatcherStub
+ extends IDeviceVolumeBehaviorDispatcher.Stub implements CallbackUtil.DispatcherStub {
+ public void register(boolean register) {
+ try {
+ getService().registerDeviceVolumeBehaviorDispatcher(register, this);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ mDeviceVolumeBehaviorChangedListenerMgr.callListeners((listener) ->
+ listener.onDeviceVolumeBehaviorChanged(device, volumeBehavior));
+ }
+ }
+
private static IAudioService getService() {
if (sService != null) {
return sService;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c186700a4326..babafaf26036 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -33,6 +33,7 @@ import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
+import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
@@ -44,7 +45,6 @@ import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.ISpatializerOutputCallback;
import android.media.IVolumeController;
-import android.media.IVolumeController;
import android.media.PlayerBase;
import android.media.VolumeInfo;
import android.media.VolumePolicy;
@@ -482,6 +482,10 @@ interface IAudioService {
void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING,android.Manifest.permission.QUERY_AUDIO_STATE})")
+ void registerDeviceVolumeBehaviorDispatcher(boolean register,
+ in IDeviceVolumeBehaviorDispatcher dispatcher);
+
List<AudioFocusInfo> getFocusStack();
boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
diff --git a/media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl b/media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl
new file mode 100644
index 000000000000..2b19bffa97f7
--- /dev/null
+++ b/media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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 android.media;
+
+import android.media.AudioDeviceAttributes;
+
+/**
+ * AIDL for AudioService to signal changes to an audio device's volume behavior
+ *
+ * {@hide}
+ */
+oneway interface IDeviceVolumeBehaviorDispatcher {
+
+ void dispatchDeviceVolumeBehaviorChanged(in AudioDeviceAttributes device,
+ int deviceVolumeBehavior);
+
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a08ef4b5ab8d..a68fc05dbbf1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -96,6 +96,7 @@ import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
+import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
@@ -341,6 +342,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_ADD_ASSISTANT_SERVICE_UID = 44;
private static final int MSG_REMOVE_ASSISTANT_SERVICE_UID = 45;
private static final int MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID = 46;
+ private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -876,13 +878,23 @@ public class AudioService extends IAudioService.Stub
/** @hide */
public AudioService(Context context) {
- this(context, AudioSystemAdapter.getDefaultAdapter(),
+ this(context,
+ AudioSystemAdapter.getDefaultAdapter(),
SystemServerAdapter.getDefaultAdapter(context),
- SettingsAdapter.getDefaultAdapter());
+ SettingsAdapter.getDefaultAdapter(),
+ null);
}
+ /**
+ * @param context
+ * @param audioSystem Adapter for {@link AudioSystem}
+ * @param systemServer Adapter for privilieged functionality for system server components
+ * @param settings Adapter for {@link Settings}
+ * @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) {
+ SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper) {
sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
mContext = context;
mContentResolver = context.getContentResolver();
@@ -1010,7 +1022,11 @@ public class AudioService extends IAudioService.Stub
MAX_STREAM_VOLUME[AudioSystem.STREAM_SYSTEM];
}
- createAudioSystemThread();
+ if (looper == null) {
+ createAudioSystemThread();
+ } else {
+ mAudioHandler = new AudioHandler(looper);
+ }
AudioSystem.setErrorCallback(mAudioSystemCallback);
@@ -6457,34 +6473,41 @@ public class AudioService extends IAudioService.Stub
return;
}
- int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
- device.getType());
- setDeviceVolumeBehaviorInternal(audioSystemDeviceOut, deviceVolumeBehavior, pkgName);
-
- persistDeviceVolumeBehavior(audioSystemDeviceOut, deviceVolumeBehavior);
+ setDeviceVolumeBehaviorInternal(device, deviceVolumeBehavior, pkgName);
+ persistDeviceVolumeBehavior(device.getInternalType(), deviceVolumeBehavior);
}
- private void setDeviceVolumeBehaviorInternal(int audioSystemDeviceOut,
+ private void setDeviceVolumeBehaviorInternal(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @NonNull String caller) {
+ int audioSystemDeviceOut = device.getInternalType();
+ boolean volumeBehaviorChanged = false;
// update device masks based on volume behavior
switch (deviceVolumeBehavior) {
case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE:
- removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut);
- removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
+ volumeBehaviorChanged |=
+ removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
+ | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
break;
case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
- removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut);
- addAudioSystemDeviceOutToFixedVolumeDevices(audioSystemDeviceOut);
+ volumeBehaviorChanged |=
+ removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
+ | addAudioSystemDeviceOutToFixedVolumeDevices(audioSystemDeviceOut);
break;
case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
- addAudioSystemDeviceOutToFullVolumeDevices(audioSystemDeviceOut);
- removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
+ volumeBehaviorChanged |=
+ addAudioSystemDeviceOutToFullVolumeDevices(audioSystemDeviceOut)
+ | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
break;
case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
throw new IllegalArgumentException("Absolute volume unsupported for now");
}
+ if (volumeBehaviorChanged) {
+ sendMsg(mAudioHandler, MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR, SENDMSG_QUEUE,
+ deviceVolumeBehavior, 0, device, /*delay*/ 0);
+ }
+
// log event and caller
sDeviceLogger.log(new AudioEventLogger.StringEvent(
"Volume behavior " + deviceVolumeBehavior + " for dev=0x"
@@ -7761,6 +7784,14 @@ public class AudioService extends IAudioService.Stub
/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
+ AudioHandler() {
+ super();
+ }
+
+ AudioHandler(Looper looper) {
+ super(looper);
+ }
+
private void setAllVolumes(VolumeStreamState streamState) {
// Apply volume
@@ -8063,6 +8094,10 @@ public class AudioService extends IAudioService.Stub
case MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID:
updateActiveAssistantServiceUids();
break;
+
+ case MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR:
+ dispatchDeviceVolumeBehavior((AudioDeviceAttributes) msg.obj, msg.arg1);
+ break;
}
}
}
@@ -9080,6 +9115,35 @@ public class AudioService extends IAudioService.Stub
mMuteAwaitConnectionDispatchers.finishBroadcast();
}
+ final RemoteCallbackList<IDeviceVolumeBehaviorDispatcher> mDeviceVolumeBehaviorDispatchers =
+ new RemoteCallbackList<IDeviceVolumeBehaviorDispatcher>();
+
+ /**
+ * @see AudioDeviceVolumeManager#addOnDeviceVolumeBehaviorChangedListener and
+ * AudioDeviceVolumeManager#removeOnDeviceVolumeBehaviorChangedListener
+ */
+ public void registerDeviceVolumeBehaviorDispatcher(boolean register,
+ @NonNull IDeviceVolumeBehaviorDispatcher dispatcher) {
+ enforceQueryStateOrModifyRoutingPermission();
+ Objects.requireNonNull(dispatcher);
+ if (register) {
+ mDeviceVolumeBehaviorDispatchers.register(dispatcher);
+ } else {
+ mDeviceVolumeBehaviorDispatchers.unregister(dispatcher);
+ }
+ }
+
+ private void dispatchDeviceVolumeBehavior(AudioDeviceAttributes device, int volumeBehavior) {
+ final int dispatchers = mDeviceVolumeBehaviorDispatchers.beginBroadcast();
+ for (int i = 0; i < dispatchers; i++) {
+ try {
+ mDeviceVolumeBehaviorDispatchers.getBroadcastItem(i)
+ .dispatchDeviceVolumeBehaviorChanged(device, volumeBehavior);
+ } catch (RemoteException e) {
+ }
+ }
+ mDeviceVolumeBehaviorDispatchers.finishBroadcast();
+ }
//==========================================================================================
// Device orientation
@@ -9310,14 +9374,20 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.d(TAG, "CEC sink: setting HDMI as full vol device");
}
- addAudioSystemDeviceOutToFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
+ setDeviceVolumeBehaviorInternal(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ "AudioService.updateHdmiCecSinkLocked()");
} else {
if (DEBUG_VOL) {
Log.d(TAG, "TV, no CEC: setting HDMI as regular vol device");
}
// Android TV devices without CEC service apply software volume on
// HDMI output
- removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
+ setDeviceVolumeBehaviorInternal(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ "AudioService.updateHdmiCecSinkLocked()");
}
postUpdateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
"HdmiPlaybackClient.DisplayStatusCallback");
@@ -11463,8 +11533,8 @@ public class AudioService extends IAudioService.Stub
continue;
}
- setDeviceVolumeBehaviorInternal(deviceType, deviceVolumeBehavior,
- "AudioService.restoreDeviceVolumeBehavior()");
+ setDeviceVolumeBehaviorInternal(new AudioDeviceAttributes(deviceType, ""),
+ deviceVolumeBehavior, "AudioService.restoreDeviceVolumeBehavior()");
}
}
@@ -11479,36 +11549,36 @@ public class AudioService extends IAudioService.Stub
!= AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
}
- private void addAudioSystemDeviceOutToFixedVolumeDevices(int audioSystemDeviceOut) {
+ private boolean addAudioSystemDeviceOutToFixedVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " to mFixedVolumeDevices");
}
- mFixedVolumeDevices.add(audioSystemDeviceOut);
+ return mFixedVolumeDevices.add(audioSystemDeviceOut);
}
- private void removeAudioSystemDeviceOutFromFixedVolumeDevices(int audioSystemDeviceOut) {
+ private boolean removeAudioSystemDeviceOutFromFixedVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " from mFixedVolumeDevices");
}
- mFixedVolumeDevices.remove(audioSystemDeviceOut);
+ return mFixedVolumeDevices.remove(audioSystemDeviceOut);
}
- private void addAudioSystemDeviceOutToFullVolumeDevices(int audioSystemDeviceOut) {
+ private boolean addAudioSystemDeviceOutToFullVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " to mFullVolumeDevices");
}
- mFullVolumeDevices.add(audioSystemDeviceOut);
+ return mFullVolumeDevices.add(audioSystemDeviceOut);
}
- private void removeAudioSystemDeviceOutFromFullVolumeDevices(int audioSystemDeviceOut) {
+ private boolean removeAudioSystemDeviceOutFromFullVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " from mFullVolumeDevices");
}
- mFullVolumeDevices.remove(audioSystemDeviceOut);
+ return mFullVolumeDevices.remove(audioSystemDeviceOut);
}
//====================
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 5b6aebccbc11..91c45b4f8b60 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -62,7 +62,7 @@ public class AudioServiceTest {
mSpySystemServer = spy(new NoOpSystemServerAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
- mSettingsAdapter);
+ mSettingsAdapter, null);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
new file mode 100644
index 000000000000..d89c6d5c3c78
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 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.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for AudioService's tracking and reporting of device volume behaviors.
+ */
+public class DeviceVolumeBehaviorTest {
+ private static final String TAG = "DeviceVolumeBehaviorTest";
+
+ private static final String PACKAGE_NAME = "";
+ private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+
+ private Context mContext;
+ private AudioSystemAdapter mAudioSystem;
+ private SystemServerAdapter mSystemServer;
+ private SettingsAdapter mSettingsAdapter;
+ private TestLooper mTestLooper;
+
+ private AudioService mAudioService;
+
+ /**
+ * Volume behaviors that can be set using AudioService#setDeviceVolumeBehavior
+ */
+ public static final int[] BASIC_VOLUME_BEHAVIORS = {
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mContext = InstrumentationRegistry.getTargetContext();
+ mAudioSystem = new NoOpAudioSystemAdapter();
+ mSystemServer = new NoOpSystemServerAdapter();
+ mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioService = new AudioService(mContext, mAudioSystem, mSystemServer,
+ mSettingsAdapter, mTestLooper.getLooper());
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void setDeviceVolumeBehavior_changesDeviceVolumeBehavior() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior, PACKAGE_NAME);
+ 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, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ dispatcher.reset();
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior, PACKAGE_NAME);
+ 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, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void unregisterDeviceVolumeBehaviorDispatcher_noLongerTriggered() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(false, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ 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;
+ }
+ }
+}
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 1f355b096335..09e5d4be14a4 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -17,10 +17,12 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
import android.util.Log;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -122,4 +124,11 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
public boolean isStreamActive(int stream, int inPastMs) {
return mIsStreamActive;
}
+
+ @Override
+ @NonNull
+ public ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
+ @NonNull AudioAttributes attributes, boolean forVolume) {
+ return new ArrayList<>();
+ }
}