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:
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user