diff options
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<>(); + } } |