Add Absolute Volume Control support for a TV as the System Audio device

A TV will react to <Set Audio Volume Level> messages by setting the
volume of STREAM_MUSIC, unless System Audio Mode is enabled. In this
case, the TV does nothing and responds with <Feature Abort>[not in
correct mode to respond], as <Set Audio Volume Level> messages should
only be send to the System Audio device.

Test: atest HdmiCecLocalDeviceTvTest
Bug: 223131113
Change-Id: I1aac3926ffe1280344e97efcbd1dce7f9c3c3ffd
This commit is contained in:
Yan Han
2022-03-17 16:15:16 +01:00
parent 8f18957e4c
commit 73dee58455
4 changed files with 87 additions and 4 deletions

View File

@@ -303,6 +303,13 @@ abstract class HdmiCecLocalDevice {
if (dispatchMessageToAction(message)) {
return Constants.HANDLED;
}
// If a message type has its own class, all valid messages of that type
// will be represented by an instance of that class.
if (message instanceof SetAudioVolumeLevelMessage) {
return handleSetAudioVolumeLevel((SetAudioVolumeLevelMessage) message);
}
switch (message.getOpcode()) {
case Constants.MESSAGE_ACTIVE_SOURCE:
return handleActiveSource(message);
@@ -637,6 +644,11 @@ abstract class HdmiCecLocalDevice {
return Constants.NOT_HANDLED;
}
@Constants.HandleMessageResult
protected int handleSetAudioVolumeLevel(SetAudioVolumeLevelMessage message) {
return Constants.NOT_HANDLED;
}
@Constants.RcProfile
protected abstract int getRcProfile();

View File

@@ -1166,6 +1166,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return Constants.HANDLED;
}
@Override
@Constants.HandleMessageResult
protected int handleSetAudioVolumeLevel(SetAudioVolumeLevelMessage message) {
// <Set Audio Volume Level> should only be sent to the System Audio device, so we don't
// handle it when System Audio Mode is enabled.
if (mService.isSystemAudioActivated()) {
return Constants.ABORT_NOT_IN_CORRECT_MODE;
} else {
mService.setStreamMusicVolume(message.getAudioVolumeLevel(), 0);
return Constants.HANDLED;
}
}
void announceOneTouchRecordResult(int recorderAddress, int result) {
mService.invokeOneTouchRecordResult(recorderAddress, result);
}
@@ -1596,6 +1609,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
.setRecordTvScreenSupport(FEATURE_SUPPORTED)
.setArcTxSupport(hasArcPort ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED)
.setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)
.build();
}

View File

@@ -4176,9 +4176,7 @@ public class HdmiControlService extends SystemService {
List<AudioDeviceAttributes> streamMusicDevices =
getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC,
volume * mStreamMusicMaxVolume / AudioStatus.MAX_VOLUME,
AudioManager.FLAG_ABSOLUTE_VOLUME);
setStreamMusicVolume(volume, AudioManager.FLAG_ABSOLUTE_VOLUME);
}
}
@@ -4196,4 +4194,13 @@ public class HdmiControlService extends SystemService {
AudioManager.FLAG_ABSOLUTE_VOLUME);
}
}
/**
* Sets the volume index of {@link AudioManager#STREAM_MUSIC}. Rescales the input volume index
* from HDMI-CEC volume range to STREAM_MUSIC's.
*/
void setStreamMusicVolume(int volume, int flags) {
getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC,
volume * mStreamMusicMaxVolume / AudioStatus.MAX_VOLUME, flags);
}
}

View File

@@ -30,8 +30,10 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
@@ -770,7 +772,7 @@ public class HdmiCecLocalDeviceTvTest {
// When the device reports its physical address, the listener eventually is invoked.
HdmiCecMessage reportPhysicalAddress =
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
mTestLooper.dispatchAll();
@@ -779,6 +781,54 @@ public class HdmiCecLocalDeviceTvTest {
assertThat(mDeviceEventListeners.size()).isEqualTo(1);
assertThat(mDeviceEventListeners.get(0).getStatus())
.isEqualTo(HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
@Test
public void receiveSetAudioVolumeLevel_samNotActivated_noFeatureAbort_volumeChanges() {
when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)).thenReturn(25);
// Max volume of STREAM_MUSIC is retrieved on boot
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mTestLooper.dispatchAll();
mNativeWrapper.onCecMessage(SetAudioVolumeLevelMessage.build(
ADDR_PLAYBACK_1,
ADDR_TV,
20));
mTestLooper.dispatchAll();
// <Feature Abort>[Not in correct mode] not sent
HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
ADDR_TV,
ADDR_PLAYBACK_1,
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
Constants.ABORT_NOT_IN_CORRECT_MODE);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortMessage);
// <Set Audio Volume Level> uses volume range [0, 100]; STREAM_MUSIC uses range [0, 25]
verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC), eq(5), anyInt());
}
@Test
public void receiveSetAudioVolumeLevel_samActivated_respondsFeatureAbort_noVolumeChange() {
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildSetSystemAudioMode(
ADDR_AUDIO_SYSTEM, ADDR_TV, true));
mTestLooper.dispatchAll();
mNativeWrapper.onCecMessage(SetAudioVolumeLevelMessage.build(
ADDR_PLAYBACK_1, ADDR_TV, 50));
mTestLooper.dispatchAll();
// <Feature Abort>[Not in correct mode] sent
HdmiCecMessage featureAbortMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
ADDR_TV,
ADDR_PLAYBACK_1,
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
Constants.ABORT_NOT_IN_CORRECT_MODE);
assertThat(mNativeWrapper.getResultMessages()).contains(featureAbortMessage);
// AudioManager not notified of volume change
verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
anyInt());
}
}