Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73dee584 authored by Yan Han's avatar Yan Han
Browse files

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
parent 8f18957e
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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();

+14 −0
Original line number Diff line number Diff line
@@ -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();
    }

+10 −3
Original line number Diff line number Diff line
@@ -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);
    }
}
+51 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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());
    }
}