Implement soundbar volume UI for TV panels.
This is done by adopting adjust-only absolute volume behavior when the connected amplifier is able to send <Report Audio Status>. Adjust-only absolute volume behavior supports volume tracking and automatically displays volume UI when the volume level is updated in AudioService. In HdmiControlService, adoption of this behavior is implemented as a special case in the logic for adopting absolute volume behavior: - Absolute volume behavior is currently adopted when a TV panel or playback device is able to track AND set the percentage volume level of the System Audio device. - Adjust-only absolute volume behavior will be adopted when a TV panel can track, but not set, the volume level of the System Audio device. The only behavioral difference in HdmiControlService when using adjust-only AVB, compared to regular AVB, is that it does not send <Set Audio Volume Level> to the System Audio device. Bug: 240663266 Test: atest TvToAudioSystemAvbTest PlaybackDeviceToTvAvbTest PlaybackDeviceToAudioSystemAvbTest Change-Id: I05465cd0eab2f3c3464bf41c1194cdc3be7af4bf
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>)
|
||||
* 4. The System Audio device supports the <Set Audio Volume Level> message
|
||||
*
|
||||
* If not all of these conditions are met, this method disables AVB if necessary.
|
||||
*
|
||||
* 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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user