diff options
12 files changed, 423 insertions, 90 deletions
diff --git a/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java b/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java index 113384f375bd..d764ec41b3b9 100644 --- a/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java +++ b/services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java @@ -17,10 +17,13 @@ package com.android.server.hdmi; /** - * Action to query and track the audio status of the System Audio device when enabling or using - * absolute volume behavior. Must be removed when AVB is disabled. Performs two main functions: + * Action to query and track the audio status of the System Audio device when using + * absolute volume behavior, or adjust-only absolute volume behavior. Must be removed when + * neither behavior is used. + * + * Performs two main functions: * 1. When enabling AVB: queries the starting audio status of the System Audio device and - * enables the feature upon receiving a response. + * adopts the appropriate volume behavior upon receiving a response. * 2. While AVB is enabled: monitors <Report Audio Status> messages from the System Audio device and * notifies AudioService if the audio status changes. */ diff --git a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java index 23e6a12e590d..94842041af82 100644 --- a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java +++ b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java @@ -61,4 +61,15 @@ public interface AudioDeviceVolumeManagerWrapper { @NonNull @CallbackExecutor Executor executor, @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment); + + /** + * Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeAdjustOnlyBehavior( + * AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)} + */ + void setDeviceAbsoluteVolumeAdjustOnlyBehavior( + @NonNull AudioDeviceAttributes device, + @NonNull VolumeInfo volume, + @NonNull @CallbackExecutor Executor executor, + @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener, + boolean handlesVolumeAdjustment); } diff --git a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java index b9a78c93989f..ff99ace38ef0 100644 --- a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java +++ b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java @@ -67,4 +67,15 @@ public class DefaultAudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeBehavior(device, volume, executor, vclistener, handlesVolumeAdjustment); } + + @Override + public void setDeviceAbsoluteVolumeAdjustOnlyBehavior( + @NonNull AudioDeviceAttributes device, + @NonNull VolumeInfo volume, + @NonNull @CallbackExecutor Executor executor, + @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener, + boolean handlesVolumeAdjustment) { + mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeAdjustOnlyBehavior(device, volume, + executor, vclistener, handlesVolumeAdjustment); + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 8b2942489c7f..f37ad5ed7049 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -1012,17 +1012,23 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice { action.start(); } + @ServiceThreadOnly void addAvbAudioStatusAction(int targetAddress) { + assertRunOnServiceThread(); if (!hasAction(AbsoluteVolumeAudioStatusAction.class)) { addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress)); } } + @ServiceThreadOnly void removeAvbAudioStatusAction() { + assertRunOnServiceThread(); removeAction(AbsoluteVolumeAudioStatusAction.class); } + @ServiceThreadOnly void updateAvbVolume(int volumeIndex) { + assertRunOnServiceThread(); for (AbsoluteVolumeAudioStatusAction action : getActions(AbsoluteVolumeAudioStatusAction.class)) { action.updateVolume(volumeIndex); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index f47c4b2c24d9..5ef06f9f8967 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -66,7 +66,7 @@ import java.util.stream.Collectors; /** * Represent a logical device of type TV residing in Android system. */ -final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { +public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private static final String TAG = "HdmiCecLocalDeviceTv"; // Whether ARC is available or not. "true" means that ARC is established between TV and diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 741e730da022..9edab6da1a7e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -4230,14 +4230,18 @@ public class HdmiControlService extends SystemService { if (avbAudioOutputDevice == null) { return false; } - return getDeviceVolumeBehavior(avbAudioOutputDevice) - == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE; + + @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior = + getDeviceVolumeBehavior(avbAudioOutputDevice); + + return deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE + || deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY; } private AudioDeviceAttributes getAvbAudioOutputDevice() { - if (isTvDevice()) { + if (tv() != null) { return tv().getSystemAudioOutputDevice(); - } else if (isPlaybackDevice()) { + } else if (playback() != null) { return AUDIO_OUTPUT_DEVICE_HDMI; } else { return null; @@ -4245,24 +4249,24 @@ public class HdmiControlService extends SystemService { } /** - * Checks the conditions for Absolute Volume Behavior (AVB), and enables or disables the feature - * if necessary. AVB is enabled precisely when a specific audio output device - * (HDMI for playback devices, and HDMI_ARC or HDMI_EARC for TVs) is using absolute volume - * behavior. + * This method is responsible for adopting or disabling absolute volume behavior and + * adjust-only absolute volume behavior in AudioService. These volume behaviors are adopted on + * specific audio output devices: HDMI for playback devices, and HDMI_ARC or HDMI_EARC for TVs. * - * AVB must be enabled on a Playback device or TV precisely when it is playing - * audio on an external device (the System Audio device) that supports the feature. - * This reduces to these conditions: + * This method enables absolute volume behavior on a Playback device or TV panel when it is + * playing audio on an external device (the System Audio device) that supports the feature. + * This allows the volume level of the System Audio device to be tracked and set by Android. * + * Absolute volume behavior requires the following conditions: * 1. If the System Audio Device is an Audio System: System Audio Mode is active * 2. Our HDMI audio output device is using full volume behavior * 3. CEC volume is enabled - * 4. The System Audio device supports AVB (i.e. it supports <Set Audio Volume Level>) - * - * If not all of these conditions are met, this method disables AVB if necessary. + * 4. The System Audio device supports the <Set Audio Volume Level> message * - * If all of these conditions are met, this method starts an action to query the System Audio - * device's audio status, which enables AVB upon obtaining the audio status. + * This method enables adjust-only absolute volume behavior on TV panels when conditions + * 1, 2, and 3 are met, but condition 4 is not. This allows TVs to track the volume level of + * the System Audio device and display numeric volume UI for it, even if the System Audio device + * does not support <Set Audio Volume Level>. */ @ServiceThreadOnly void checkAndUpdateAbsoluteVolumeBehavior() { @@ -4280,7 +4284,7 @@ public class HdmiControlService extends SystemService { // (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the // TV is the System Audio Device instead.) if (!isSystemAudioActivated()) { - disableAbsoluteVolumeBehavior(); + switchToFullVolumeBehavior(); return; } } else if (isPlaybackDevice() && playback() != null) { @@ -4290,75 +4294,100 @@ public class HdmiControlService extends SystemService { return; } - HdmiDeviceInfo systemAudioDeviceInfo = getHdmiCecNetwork().getSafeCecDeviceInfo( + HdmiDeviceInfo systemAudioDeviceInfo = getDeviceInfo( localCecDevice.findAudioReceiverAddress()); @AudioManager.DeviceVolumeBehavior int currentVolumeBehavior = getDeviceVolumeBehavior(getAvbAudioOutputDevice()); // Condition 2: Already using full or absolute volume behavior boolean alreadyUsingFullOrAbsoluteVolume = - currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL - || currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE; + (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL) + || (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) + || (currentVolumeBehavior + == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY); // Condition 3: CEC volume is enabled boolean cecVolumeEnabled = getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED; if (!cecVolumeEnabled || !alreadyUsingFullOrAbsoluteVolume) { - disableAbsoluteVolumeBehavior(); + switchToFullVolumeBehavior(); return; } // Check for safety: if the System Audio device is a candidate for AVB, we should already // have received messages from it to trigger the other conditions. if (systemAudioDeviceInfo == null) { - disableAbsoluteVolumeBehavior(); + switchToFullVolumeBehavior(); return; } - // Condition 4: The System Audio device supports AVB (i.e. <Set Audio Volume Level>). + + // Condition 4: The System Audio device supports <Set Audio Volume Level> switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) { case DeviceFeatures.FEATURE_SUPPORTED: - if (!isAbsoluteVolumeBehaviorEnabled()) { - // Start an action that will call {@link #enableAbsoluteVolumeBehavior} + if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) { + // If we're currently using adjust-only absolute volume behavior, switch to + // full volume behavior until we successfully adopt absolute volume behavior + switchToFullVolumeBehavior(); + // Start an action that will call enableAbsoluteVolumeBehavior // once the System Audio device sends <Report Audio Status> localCecDevice.addAvbAudioStatusAction( systemAudioDeviceInfo.getLogicalAddress()); } return; case DeviceFeatures.FEATURE_NOT_SUPPORTED: - disableAbsoluteVolumeBehavior(); + // TVs may adopt adjust-only absolute volume behavior if condition 4 isn't met. + // This allows the device to display numeric volume UI for the System Audio device. + if (tv() != null) { + if (currentVolumeBehavior + != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) { + // If we're currently using absolute volume behavior, switch to full volume + // behavior until we successfully adopt adjust-only absolute volume behavior + switchToFullVolumeBehavior(); + // Start an action that will call enableAbsoluteVolumeBehavior + // once the System Audio device sends <Report Audio Status> + localCecDevice.addAvbAudioStatusAction( + systemAudioDeviceInfo.getLogicalAddress()); + } + } else { + switchToFullVolumeBehavior(); + } return; case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN: - disableAbsoluteVolumeBehavior(); + switchToFullVolumeBehavior(); localCecDevice.querySetAudioVolumeLevelSupport( systemAudioDeviceInfo.getLogicalAddress()); - return; - default: - return; } } - private void disableAbsoluteVolumeBehavior() { - if (isPlaybackDevice()) { + /** + * Switches to full volume behavior, if either absolute or adjust-only absolute volume behavior + * are currently used. Removes the action for handling volume updates for these behaviors. + */ + private void switchToFullVolumeBehavior() { + if (playback() != null) { playback().removeAvbAudioStatusAction(); - } else if (isTvDevice()) { + } else if (tv() != null) { tv().removeAvbAudioStatusAction(); } AudioDeviceAttributes device = getAvbAudioOutputDevice(); - if (getDeviceVolumeBehavior(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) { + int volumeBehavior = getDeviceVolumeBehavior(device); + if (volumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE + || volumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) { getAudioManager().setDeviceVolumeBehavior(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); } } /** - * Enables absolute volume behavior. Should only be called when all the conditions for - * AVB are met (see {@link #checkAndUpdateAbsoluteVolumeBehavior}). + * Enables absolute volume behavior or adjust-only absolute volume behavior. Should only be + * called when the conditions for one of these behaviors is met - + * see {@link #checkAndUpdateAbsoluteVolumeBehavior}. + * * @param audioStatus The initial audio status to set the audio output device to */ void enableAbsoluteVolumeBehavior(AudioStatus audioStatus) { HdmiCecLocalDevice localDevice = isPlaybackDevice() ? playback() : tv(); - HdmiDeviceInfo systemAudioDevice = getHdmiCecNetwork().getDeviceInfo( - localDevice.findAudioReceiverAddress()); + HdmiDeviceInfo systemAudioDevice = getDeviceInfo(localDevice.findAudioReceiverAddress()); VolumeInfo volumeInfo = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) .setMuted(audioStatus.getMute()) .setVolumeIndex(audioStatus.getVolume()) @@ -4371,9 +4400,20 @@ public class HdmiControlService extends SystemService { // AudioService sets the volume of the stream and device based on the input VolumeInfo // when enabling absolute volume behavior, but not the mute state notifyAvbMuteChange(audioStatus.getMute()); - getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior( - getAvbAudioOutputDevice(), volumeInfo, mServiceThreadExecutor, - mAbsoluteVolumeChangedListener, true); + + // If <Set Audio Volume Level> is supported, enable absolute volume behavior. + // Otherwise, enable adjust-only AVB on TVs only. + if (systemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport() + == DeviceFeatures.FEATURE_SUPPORTED) { + getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior( + getAvbAudioOutputDevice(), volumeInfo, mServiceThreadExecutor, + mAbsoluteVolumeChangedListener, true); + } else if (tv() != null) { + getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior( + getAvbAudioOutputDevice(), volumeInfo, mServiceThreadExecutor, + mAbsoluteVolumeChangedListener, true); + } + } private AbsoluteVolumeChangedListener mAbsoluteVolumeChangedListener; @@ -4407,6 +4447,14 @@ public class HdmiControlService extends SystemService { public void onAudioDeviceVolumeChanged( @NonNull AudioDeviceAttributes audioDevice, @NonNull VolumeInfo volumeInfo) { + + // Do nothing if the System Audio device does not support <Set Audio Volume Level> + if (mSystemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport() + != DeviceFeatures.FEATURE_SUPPORTED) { + return; + } + + // Send <Set Audio Volume Level> to notify the System Audio device of the volume change int localDeviceAddress = mLocalDevice.getDeviceInfo().getLogicalAddress(); sendCecCommand(SetAudioVolumeLevelMessage.build( localDeviceAddress, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java index fa8927e683dc..bc09d4b10723 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java @@ -74,7 +74,7 @@ import java.util.Collections; * (TV, Audio System): {@link TvToAudioSystemAvbTest} */ public abstract class BaseAbsoluteVolumeBehaviorTest { - private HdmiControlService mHdmiControlService; + protected HdmiControlService mHdmiControlService; private HdmiCecController mHdmiCecController; private HdmiCecLocalDevice mHdmiCecLocalDevice; private FakeHdmiCecConfig mHdmiCecConfig; @@ -282,6 +282,20 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE); } + protected void enableAdjustOnlyAbsoluteVolumeBehavior() { + mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(), + AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); + setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); + enableSystemAudioModeIfNeeded(); + receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED); + receiveReportAudioStatus( + INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(), + INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute()); + + assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( + AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY); + } + protected void verifyGiveAudioStatusNeverSent() { assertThat(mNativeWrapper.getResultMessages()).doesNotContain( HdmiCecMessageBuilder.buildGiveAudioStatus( @@ -462,6 +476,7 @@ public abstract class BaseAbsoluteVolumeBehaviorTest { assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); } + @Test public void avbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() { // Initial <Report Audio Status> has volume=50 and mute=false diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java new file mode 100644 index 000000000000..4c12e436542b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 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.hdmi; + +import android.hardware.hdmi.DeviceFeatures; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; +import android.media.AudioDeviceAttributes; +import android.media.AudioManager; + +import org.junit.Test; + +/** + * Base class for tests for absolute volume behavior on Playback devices. Contains tests that are + * relevant to Playback devices but not to TVs. + * + * Subclasses contain tests for the following pairs of (local device, System Audio device): + * (Playback, TV): {@link PlaybackDeviceToTvAvbTest} + * (Playback, Audio System): {@link PlaybackDeviceToAudioSystemAvbTest} + */ +public abstract class BasePlaybackDeviceAvbTest extends BaseAbsoluteVolumeBehaviorTest { + + @Override + protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) { + return new HdmiCecLocalDevicePlayback(hdmiControlService); + } + + @Override + protected int getPhysicalAddress() { + return 0x1100; + } + + @Override + protected int getDeviceType() { + return HdmiDeviceInfo.DEVICE_PLAYBACK; + } + + @Override + protected AudioDeviceAttributes getAudioOutputDevice() { + return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI; + } + + /** + * Unlike TVs, Playback devices don't start the process for adopting adjust-only AVB + * if the System Audio device doesn't support <Set Audio Volume Level> + */ + @Test + public void savlNotSupported_allOtherConditionsMet_giveAudioStatusNotSent() { + mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(), + AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); + setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); + enableSystemAudioModeIfNeeded(); + + receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED); + verifyGiveAudioStatusNeverSent(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java index 3bcfac38e85b..7294ba62cdae 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java @@ -188,6 +188,17 @@ public class FakeAudioFramework { boolean handlesVolumeAdjustment) { setVolumeBehaviorHelper(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE); } + + @Override + public void setDeviceAbsoluteVolumeAdjustOnlyBehavior( + @NonNull AudioDeviceAttributes device, + @NonNull VolumeInfo volume, + @NonNull @CallbackExecutor Executor executor, + @NonNull OnAudioDeviceVolumeChangedListener vclistener, + boolean handlesVolumeAdjustment) { + setVolumeBehaviorHelper(device, + AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY); + } } /** diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java index abfe0e2e6db8..43ab804e04be 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import android.hardware.hdmi.DeviceFeatures; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; -import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.platform.test.annotations.Presubmit; @@ -42,27 +41,7 @@ import java.util.Arrays; @SmallTest @Presubmit @RunWith(JUnit4.class) -public class PlaybackDeviceToAudioSystemAvbTest extends BaseAbsoluteVolumeBehaviorTest { - - @Override - protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) { - return new HdmiCecLocalDevicePlayback(hdmiControlService); - } - - @Override - protected int getPhysicalAddress() { - return 0x1100; - } - - @Override - protected int getDeviceType() { - return HdmiDeviceInfo.DEVICE_PLAYBACK; - } - - @Override - protected AudioDeviceAttributes getAudioOutputDevice() { - return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI; - } +public class PlaybackDeviceToAudioSystemAvbTest extends BasePlaybackDeviceAvbTest { @Override protected int getSystemAudioDeviceLogicalAddress() { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java index c65b26a1e8bf..9b343e34706a 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java @@ -23,7 +23,6 @@ import static org.mockito.Mockito.clearInvocations; import android.hardware.hdmi.DeviceFeatures; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; -import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.platform.test.annotations.Presubmit; @@ -43,27 +42,7 @@ import java.util.Collections; @SmallTest @Presubmit @RunWith(JUnit4.class) -public class PlaybackDeviceToTvAvbTest extends BaseAbsoluteVolumeBehaviorTest { - - @Override - protected HdmiCecLocalDevice createLocalDevice(HdmiControlService hdmiControlService) { - return new HdmiCecLocalDevicePlayback(hdmiControlService); - } - - @Override - protected int getPhysicalAddress() { - return 0x1100; - } - - @Override - protected int getDeviceType() { - return HdmiDeviceInfo.DEVICE_PLAYBACK; - } - - @Override - protected AudioDeviceAttributes getAudioOutputDevice() { - return HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI; - } +public class PlaybackDeviceToTvAvbTest extends BasePlaybackDeviceAvbTest { @Override protected int getSystemAudioDeviceLogicalAddress() { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java index 64b69fb1e8ba..079ef2e36673 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java @@ -16,12 +16,30 @@ package com.android.server.hdmi; +import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.hardware.hdmi.DeviceFeatures; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceVolumeManager; +import android.media.AudioManager; +import android.media.VolumeInfo; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -63,4 +81,185 @@ public class TvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehaviorTest { protected int getSystemAudioDeviceType() { return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM; } + + /** + * TVs start the process for adopting adjust-only AVB if the System Audio device doesn't + * support <Set Audio Volume Level> + */ + @Test + public void savlNotSupported_allOtherConditionsMet_giveAudioStatusSent() { + mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(), + AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); + setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); + enableSystemAudioModeIfNeeded(); + verifyGiveAudioStatusNeverSent(); + + receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED); + verifyGiveAudioStatusSent(); + } + + @Test + public void savlNotSupported_systemAudioDeviceSendsReportAudioStatus_adjustOnlyAvbEnabled() { + mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(), + AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); + setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED); + enableSystemAudioModeIfNeeded(); + receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED); + + // Adjust-only AVB should not be enabled before receiving <Report Audio Status> + assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( + AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL); + + receiveReportAudioStatus(20, false); + + assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( + AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY); + + verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior( + eq(getAudioOutputDevice()), + eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) + .setVolumeIndex(20) + .setMuted(false) + .setMaxVolumeIndex(AudioStatus.MAX_VOLUME) + .setMinVolumeIndex(AudioStatus.MIN_VOLUME) + .build()), + any(), any(), anyBoolean()); + } + + + @Test + public void avbEnabled_savlNotSupported_receiveReportAudioStatus_switchToAdjustOnlyAvb() { + enableAbsoluteVolumeBehavior(); + + receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED); + + receiveReportAudioStatus(40, true); + + assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( + AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY); + + verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior( + eq(getAudioOutputDevice()), + eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) + .setVolumeIndex(40) + .setMuted(true) + .setMaxVolumeIndex(AudioStatus.MAX_VOLUME) + .setMinVolumeIndex(AudioStatus.MIN_VOLUME) + .build()), + any(), any(), anyBoolean()); + } + + @Test + public void avbEnabled_savlFeatureAborted_receiveReportAudioStatus_switchToAdjustOnlyAvb() { + enableAbsoluteVolumeBehavior(); + + mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand( + getSystemAudioDeviceLogicalAddress(), getLogicalAddress(), + Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE)); + mTestLooper.dispatchAll(); + + receiveReportAudioStatus(40, true); + + assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo( + AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY); + + verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior( + eq(getAudioOutputDevice()), + eq(new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) + .setVolumeIndex(40) + .setMuted(true) + .setMaxVolumeIndex(AudioStatus.MAX_VOLUME) + .setMinVolumeIndex(AudioStatus.MIN_VOLUME) + .build()), + any(), any(), anyBoolean()); + } + + @Test + public void adjustOnlyAvbEnabled_receiveReportAudioStatus_notifiesVolumeOrMuteChanges() { + enableAdjustOnlyAbsoluteVolumeBehavior(); + + // New volume and mute status: sets both + receiveReportAudioStatus(20, true); + verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5), + anyInt()); + verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), + eq(AudioManager.ADJUST_MUTE), anyInt()); + clearInvocations(mAudioManager); + + // New volume only: sets volume only + receiveReportAudioStatus(32, true); + verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), + anyInt()); + verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), + eq(AudioManager.ADJUST_MUTE), anyInt()); + clearInvocations(mAudioManager); + + // New mute status only: sets mute only + receiveReportAudioStatus(32, false); + verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), + anyInt()); + verify(mAudioManager).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), + eq(AudioManager.ADJUST_UNMUTE), anyInt()); + clearInvocations(mAudioManager); + + // Repeat of earlier message: sets neither volume nor mute + receiveReportAudioStatus(32, false); + verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(8), + anyInt()); + verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), + eq(AudioManager.ADJUST_UNMUTE), anyInt()); + + // Volume not within range [0, 100]: sets neither volume nor mute + receiveReportAudioStatus(127, true); + verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(), + anyInt()); + verify(mAudioManager, never()).adjustStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(), + anyInt()); + } + + @Test + public void adjustOnlyAvbEnabled_audioDeviceVolumeAdjusted_sendsUcpAndGiveAudioStatus() { + enableAdjustOnlyAbsoluteVolumeBehavior(); + mNativeWrapper.clearResultMessages(); + + mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeAdjusted( + getAudioOutputDevice(), + new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) + .setMaxVolumeIndex(AudioStatus.MAX_VOLUME) + .setMinVolumeIndex(AudioStatus.MIN_VOLUME) + .build(), + AudioManager.ADJUST_RAISE, + AudioDeviceVolumeManager.ADJUST_MODE_NORMAL + ); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains( + HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(), + getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP)); + assertThat(mNativeWrapper.getResultMessages()).contains( + HdmiCecMessageBuilder.buildUserControlReleased(getLogicalAddress(), + getSystemAudioDeviceLogicalAddress())); + assertThat(mNativeWrapper.getResultMessages()).contains( + HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(), + getSystemAudioDeviceLogicalAddress())); + } + + @Test + public void adjustOnlyAvbEnabled_audioDeviceVolumeChanged_doesNotSendSetAudioVolumeLevel() { + enableAdjustOnlyAbsoluteVolumeBehavior(); + + mNativeWrapper.clearResultMessages(); + + mHdmiControlService.getAbsoluteVolumeChangedListener().onAudioDeviceVolumeChanged( + getAudioOutputDevice(), + new VolumeInfo.Builder(AudioManager.STREAM_MUSIC) + .setVolumeIndex(20) + .setMaxVolumeIndex(AudioStatus.MAX_VOLUME) + .setMinVolumeIndex(AudioStatus.MIN_VOLUME) + .build() + ); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).isEmpty(); + } } |