summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/hdmi/AbsoluteVolumeAudioStatusAction.java9
-rw-r--r--services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java11
-rw-r--r--services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java11
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java130
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java71
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/TvToAudioSystemAvbTest.java199
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();
+ }
}