diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 8acec128245b4..fb2d2ee08cbda 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -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(); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 2fae6dcc21c69..1ea1457439ecf 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -1166,6 +1166,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return Constants.HANDLED; } + @Override + @Constants.HandleMessageResult + protected int handleSetAudioVolumeLevel(SetAudioVolumeLevelMessage message) { + // 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(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index f44dc49da095f..9824b4e6c43ae 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -4176,9 +4176,7 @@ public class HdmiControlService extends SystemService { List 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); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 2554118306769..f27b8c2f4b3a3 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -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(); + + // [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); + + // 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(); + + // [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()); } }