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
This commit is contained in:
Yan Han
2023-05-10 11:57:15 +02:00
parent 6aecb91bd1
commit 39d2938341
3 changed files with 33 additions and 1 deletions

View File

@@ -74,6 +74,13 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {
boolean mute = HdmiUtils.isAudioStatusMute(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);
if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
localDevice().getService().enableAbsoluteVolumeControl(audioStatus);

View File

@@ -23,6 +23,8 @@ import java.util.Objects;
/**
* Immutable representation of the information in the [Audio Status] operand:
* 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 static final int MAX_VOLUME = 100;
@@ -32,7 +34,7 @@ public class AudioStatus {
boolean mMute;
public AudioStatus(int volume, boolean mute) {
mVolume = volume;
mVolume = Math.max(Math.min(volume, MAX_VOLUME), MIN_VOLUME);
mMute = mute;
}

View File

@@ -437,6 +437,21 @@ public abstract class BaseAbsoluteVolumeControlTest {
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
public void avcEnabled_cecVolumeDisabled_absoluteVolumeDisabled() {
enableAbsoluteVolumeControl();
@@ -512,6 +527,14 @@ public abstract class BaseAbsoluteVolumeControlTest {
eq(AudioManager.ADJUST_UNMUTE), anyInt());
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
// volume changes. Afterward, a duplicate of an earlier <Report Audio Status> should
// still cause us to call setStreamVolume()