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

Commit 39d29383 authored by Yan Han's avatar Yan Han
Browse files

Fix system server crash on receiving <Report Audio Status>

This crash occurs when enabling absolute volume behavior, when
we receive <Report Audio Status> with an out-of-bounds volume level.
This is parsed as a negative error code, which causes
HdmiControlService to attempt to construct a VolumeInfo with a
negative volume. This throws an IllegalArgumentException.

To fix this, we ignore <Report Audio Status> messages with out-of-bounds
volume levels. To help prevent future issues, we also enforce that the
AudioStatus object can only represent volume levels in the [0, 100]
range.

Bug: 281821462
Test: atest TvToAudioSystemAvcTest PlaybackDeviceToTvAvcTest
PlaybackDeviceToAudioSystemAvcTest

Change-Id: I31dc0fd58da511262d829a10f7f03a4f0c99c05d
parent 6aecb91b
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -74,6 +74,13 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {


        boolean mute = HdmiUtils.isAudioStatusMute(cmd);
        boolean mute = HdmiUtils.isAudioStatusMute(cmd);
        int volume = HdmiUtils.getAudioStatusVolume(cmd);
        int volume = HdmiUtils.getAudioStatusVolume(cmd);

        // If the volume is out of range, report it as handled and ignore the message.
        // According to the spec, such values are either reserved or indicate an unknown volume.
        if (volume == Constants.UNKNOWN_VOLUME) {
            return true;
        }

        AudioStatus audioStatus = new AudioStatus(volume, mute);
        AudioStatus audioStatus = new AudioStatus(volume, mute);
        if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
        if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
            localDevice().getService().enableAbsoluteVolumeControl(audioStatus);
            localDevice().getService().enableAbsoluteVolumeControl(audioStatus);
+3 −1
Original line number Original line Diff line number Diff line
@@ -23,6 +23,8 @@ import java.util.Objects;
/**
/**
 * Immutable representation of the information in the [Audio Status] operand:
 * Immutable representation of the information in the [Audio Status] operand:
 * volume status (0 <= N <= 100) and mute status (muted or unmuted).
 * volume status (0 <= N <= 100) and mute status (muted or unmuted).
 * The volume level is limited to the range [0, 100] upon construction.
 * This object cannot represent an audio status where the volume is unknown, or out of bounds.
 */
 */
public class AudioStatus {
public class AudioStatus {
    public static final int MAX_VOLUME = 100;
    public static final int MAX_VOLUME = 100;
@@ -32,7 +34,7 @@ public class AudioStatus {
    boolean mMute;
    boolean mMute;


    public AudioStatus(int volume, boolean mute) {
    public AudioStatus(int volume, boolean mute) {
        mVolume = volume;
        mVolume = Math.max(Math.min(volume, MAX_VOLUME), MIN_VOLUME);
        mMute = mute;
        mMute = mute;
    }
    }


+23 −0
Original line number Original line Diff line number Diff line
@@ -437,6 +437,21 @@ public abstract class BaseAbsoluteVolumeControlTest {
        verifyAbsoluteVolumeEnabled();
        verifyAbsoluteVolumeEnabled();
    }
    }


    @Test
    public void giveAudioStatusSent_reportAudioStatusVolumeOutOfBounds_avcNotEnabled() {
        mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
                AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
        setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
        enableSystemAudioModeIfNeeded();
        receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);

        assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
                AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
        receiveReportAudioStatus(127, false);
        assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
                AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
    }

    @Test
    @Test
    public void avcEnabled_cecVolumeDisabled_absoluteVolumeDisabled() {
    public void avcEnabled_cecVolumeDisabled_absoluteVolumeDisabled() {
        enableAbsoluteVolumeControl();
        enableAbsoluteVolumeControl();
@@ -512,6 +527,14 @@ public abstract class BaseAbsoluteVolumeControlTest {
                eq(AudioManager.ADJUST_UNMUTE), anyInt());
                eq(AudioManager.ADJUST_UNMUTE), anyInt());
        clearInvocations(mAudioManager);
        clearInvocations(mAudioManager);


        // 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());
        clearInvocations(mAudioManager);

        // If AudioService causes us to send <Set Audio Volume Level>, the System Audio device's
        // If AudioService causes us to send <Set Audio Volume Level>, the System Audio device's
        // volume changes. Afterward, a duplicate of an earlier <Report Audio Status> should
        // volume changes. Afterward, a duplicate of an earlier <Report Audio Status> should
        // still cause us to call setStreamVolume()
        // still cause us to call setStreamVolume()