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

Commit 091b2e2e authored by Yan Han's avatar Yan Han
Browse files

Rename "Absolute Volume Control" to "absolute volume behavior"

Currently, the CEC framework uses "Absolute Volume Control" to refer to
the state where both of the following are true:
1. The System Audio device supports the <Set Audio Volume Level> message
   (that is, it supports the CEC "Absolute Volume Control" feature)
2. The HDMI audio output device in AudioService uses "absolute volume
   behavior"

This state is currently named for (1); this CL changes it to be named
for (2).

This is because we plan to reuse the code path for enabling this state
for a new state, where (1) is not true: the System Audio device need
not support <Set Audio Volume Level>/"Absolute Volume Control".

In this new case, (2) will still be true, as  the audio output device
will use a variant of absolute volume behavior - adjust-only absolute
volume behavior.

Bug: 240663266
Test: make

Change-Id: I679d5c1e74a1bfd8dcfe9246410ccb3089a5c5ac
parent 39d29383
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -18,10 +18,10 @@ package com.android.server.hdmi;

/**
 * Action to query and track the audio status of the System Audio device when enabling or using
 * Absolute Volume Control. Must be removed when AVC is disabled. Performs two main functions:
 * 1. When enabling AVC: queries the starting audio status of the System Audio device and
 * absolute volume behavior. Must be removed when AVB is disabled. Performs two main functions:
 * 1. When enabling AVB: queries the starting audio status of the System Audio device and
 *    enables the feature upon receiving a response.
 * 2. While AVC is enabled: monitors <Report Audio Status> messages from the System Audio device and
 * 2. While AVB is enabled: monitors <Report Audio Status> messages from the System Audio device and
 *    notifies AudioService if the audio status changes.
 */
final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {
@@ -83,14 +83,14 @@ final class AbsoluteVolumeAudioStatusAction extends HdmiCecFeatureAction {

        AudioStatus audioStatus = new AudioStatus(volume, mute);
        if (mState == STATE_WAIT_FOR_INITIAL_AUDIO_STATUS) {
            localDevice().getService().enableAbsoluteVolumeControl(audioStatus);
            localDevice().getService().enableAbsoluteVolumeBehavior(audioStatus);
            mState = STATE_MONITOR_AUDIO_STATUS;
        } else if (mState == STATE_MONITOR_AUDIO_STATUS) {
            if (audioStatus.getVolume() != mLastAudioStatus.getVolume()) {
                localDevice().getService().notifyAvcVolumeChange(audioStatus.getVolume());
                localDevice().getService().notifyAvbVolumeChange(audioStatus.getVolume());
            }
            if (audioStatus.getMute() != mLastAudioStatus.getMute()) {
                localDevice().getService().notifyAvcMuteChange(audioStatus.getMute());
                localDevice().getService().notifyAvbMuteChange(audioStatus.getMute());
            }
        }
        mLastAudioStatus = audioStatus;
+5 −5
Original line number Diff line number Diff line
@@ -1012,17 +1012,17 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
        action.start();
    }

    void addAvcAudioStatusAction(int targetAddress) {
    void addAvbAudioStatusAction(int targetAddress) {
        if (!hasAction(AbsoluteVolumeAudioStatusAction.class)) {
            addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress));
        }
    }

    void removeAvcAudioStatusAction() {
    void removeAvbAudioStatusAction() {
        removeAction(AbsoluteVolumeAudioStatusAction.class);
    }

    void updateAvcVolume(int volumeIndex) {
    void updateAvbVolume(int volumeIndex) {
        for (AbsoluteVolumeAudioStatusAction action :
                getActions(AbsoluteVolumeAudioStatusAction.class)) {
            action.updateVolume(volumeIndex);
@@ -1035,7 +1035,7 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
     * and send <Set Audio Volume Level> (to see if it gets a <Feature Abort> in response).
     */
    @ServiceThreadOnly
    void queryAvcSupport(int targetAddress) {
    void querySetAudioVolumeLevelSupport(int targetAddress) {
        assertRunOnServiceThread();

        // Send <Give Features> if using CEC 2.0 or above.
@@ -1054,7 +1054,7 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
                            @Override
                            public void onComplete(int result) {
                                if (result == HdmiControlManager.RESULT_SUCCESS) {
                                    getService().checkAndUpdateAbsoluteVolumeControlState();
                                    getService().checkAndUpdateAbsoluteVolumeBehavior();
                                }
                            }
                        }));
+4 −4
Original line number Diff line number Diff line
@@ -259,7 +259,7 @@ public class HdmiCecNetwork {
            // The addition of a local device should not notify listeners
            return;
        }
        mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
        mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
        if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
            // Don't notify listeners of devices that haven't reported their physical address yet
            return;
@@ -384,7 +384,7 @@ public class HdmiCecNetwork {
    final void removeCecDevice(HdmiCecLocalDevice localDevice, int address) {
        assertRunOnServiceThread();
        HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
        mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
        mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
        localDevice.mCecMessageCache.flushMessagesFrom(address);
        if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
            // Don't notify listeners of devices that haven't reported their physical address yet
@@ -592,7 +592,7 @@ public class HdmiCecNetwork {

        updateCecDevice(newDeviceInfo);

        mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
        mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
    }

    @ServiceThreadOnly
@@ -625,7 +625,7 @@ public class HdmiCecNetwork {
                    .build();
            updateCecDevice(newDeviceInfo);

            mHdmiControlService.checkAndUpdateAbsoluteVolumeControlState();
            mHdmiControlService.checkAndUpdateAbsoluteVolumeBehavior();
        }
    }

+58 −57
Original line number Diff line number Diff line
@@ -231,8 +231,8 @@ public class HdmiControlService extends SystemService {
            new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
                    AudioDeviceInfo.TYPE_HDMI_EARC, "");

    // Audio output devices used for Absolute Volume Control
    private static final List<AudioDeviceAttributes> AVC_AUDIO_OUTPUT_DEVICES =
    // Audio output devices used for absolute volume behavior
    private static final List<AudioDeviceAttributes> AVB_AUDIO_OUTPUT_DEVICES =
            Collections.unmodifiableList(Arrays.asList(AUDIO_OUTPUT_DEVICE_HDMI,
                    AUDIO_OUTPUT_DEVICE_HDMI_ARC, AUDIO_OUTPUT_DEVICE_HDMI_EARC));

@@ -265,7 +265,7 @@ public class HdmiControlService extends SystemService {
    @HdmiControlManager.VolumeControl
    private int mHdmiCecVolumeControl;

    // Caches the volume behaviors of all audio output devices in AVC_AUDIO_OUTPUT_DEVICES.
    // Caches the volume behaviors of all audio output devices in AVB_AUDIO_OUTPUT_DEVICES.
    @GuardedBy("mLock")
    private Map<AudioDeviceAttributes, Integer> mAudioDeviceVolumeBehaviors = new HashMap<>();

@@ -2751,7 +2751,7 @@ public class HdmiControlService extends SystemService {
            pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
            pw.println("mIsCecAvailable: " + mIsCecAvailable);
            pw.println("mCecVersion: " + mCecVersion);
            pw.println("mIsAbsoluteVolumeControlEnabled: " + isAbsoluteVolumeControlEnabled());
            pw.println("mIsAbsoluteVolumeBehaviorEnabled: " + isAbsoluteVolumeBehaviorEnabled());

            // System settings
            pw.println("System_settings:");
@@ -2919,7 +2919,7 @@ public class HdmiControlService extends SystemService {
            @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
        mHdmiCecVolumeControl = hdmiCecVolumeControl;
        announceHdmiCecVolumeControlFeatureChange(hdmiCecVolumeControl);
        runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
        runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
    }

    // Get the source address to send out commands to devices connected to the current device
@@ -3882,7 +3882,7 @@ public class HdmiControlService extends SystemService {
        synchronized (mLock) {
            mSystemAudioActivated = on;
        }
        runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
        runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
    }

    @ServiceThreadOnly
@@ -3993,7 +3993,7 @@ public class HdmiControlService extends SystemService {
                    deviceIsActiveSource, caller);
        }

        runOnServiceThread(this::checkAndUpdateAbsoluteVolumeControlState);
        runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
    }

    // This method should only be called when the device can be the active source
@@ -4189,17 +4189,17 @@ public class HdmiControlService extends SystemService {

    /**
     * Listener for changes to the volume behavior of an audio output device. Caches the
     * volume behavior of devices used for Absolute Volume Control.
     * volume behavior of devices used for absolute volume behavior.
     */
    @VisibleForTesting
    @ServiceThreadOnly
    void onDeviceVolumeBehaviorChanged(AudioDeviceAttributes device, int volumeBehavior) {
        assertRunOnServiceThread();
        if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
        if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
            synchronized (mLock) {
                mAudioDeviceVolumeBehaviors.put(device, volumeBehavior);
            }
            checkAndUpdateAbsoluteVolumeControlState();
            checkAndUpdateAbsoluteVolumeBehavior();
        }
    }

@@ -4209,7 +4209,7 @@ public class HdmiControlService extends SystemService {
     */
    @AudioManager.DeviceVolumeBehavior
    private int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
        if (AVC_AUDIO_OUTPUT_DEVICES.contains(device)) {
        if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
            synchronized (mLock) {
                if (mAudioDeviceVolumeBehaviors.containsKey(device)) {
                    return mAudioDeviceVolumeBehaviors.get(device);
@@ -4220,22 +4220,22 @@ public class HdmiControlService extends SystemService {
    }

    /**
     * Returns whether Absolute Volume Control is enabled or not. This is determined by the
     * Returns whether absolute volume behavior is enabled or not. This is determined by the
     * volume behavior of the relevant HDMI audio output device(s) for this device's type.
     */
    public boolean isAbsoluteVolumeControlEnabled() {
    public boolean isAbsoluteVolumeBehaviorEnabled() {
        if (!isTvDevice() && !isPlaybackDevice()) {
            return false;
        }
        AudioDeviceAttributes avcAudioOutputDevice = getAvcAudioOutputDevice();
        if (avcAudioOutputDevice == null) {
        AudioDeviceAttributes avbAudioOutputDevice = getAvbAudioOutputDevice();
        if (avbAudioOutputDevice == null) {
            return false;
        }
        return getDeviceVolumeBehavior(avcAudioOutputDevice)
        return getDeviceVolumeBehavior(avbAudioOutputDevice)
                    == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
    }

    private AudioDeviceAttributes getAvcAudioOutputDevice() {
    private AudioDeviceAttributes getAvbAudioOutputDevice() {
        if (isTvDevice()) {
            return tv().getSystemAudioOutputDevice();
        } else if (isPlaybackDevice()) {
@@ -4246,30 +4246,30 @@ public class HdmiControlService extends SystemService {
    }

    /**
     * Checks the conditions for Absolute Volume Control (AVC), and enables or disables the feature
     * if necessary. AVC is enabled precisely when a specific audio output device
     * Checks the conditions for Absolute Volume Behavior (AVB), and enables or disables the feature
     * if necessary. AVB is enabled precisely when a specific audio output device
     * (HDMI for playback devices, and HDMI_ARC or HDMI_EARC for TVs) is using absolute volume
     * behavior.
     *
     * AVC must be enabled on a Playback device or TV precisely when it is playing
     * AVB must be enabled on a Playback device or TV precisely when it is playing
     * audio on an external device (the System Audio device) that supports the feature.
     * This reduces to these conditions:
     *
     * 1. If the System Audio Device is an Audio System: System Audio Mode is active
     * 2. Our HDMI audio output device is using full volume behavior
     * 3. CEC volume is enabled
     * 4. The System Audio device supports AVC (i.e. it supports <Set Audio Volume Level>)
     * 4. The System Audio device supports AVB (i.e. it supports <Set Audio Volume Level>)
     *
     * If not all of these conditions are met, this method disables AVC if necessary.
     * If not all of these conditions are met, this method disables AVB if necessary.
     *
     * If all of these conditions are met, this method starts an action to query the System Audio
     * device's audio status, which enables AVC upon obtaining the audio status.
     * device's audio status, which enables AVB upon obtaining the audio status.
     */
    @ServiceThreadOnly
    void checkAndUpdateAbsoluteVolumeControlState() {
    void checkAndUpdateAbsoluteVolumeBehavior() {
        assertRunOnServiceThread();

        // Can't enable or disable AVC before we have access to system services
        // Can't set volume behavior before we have access to system services
        if (getAudioManager() == null) {
            return;
        }
@@ -4281,20 +4281,20 @@ public class HdmiControlService extends SystemService {
            // (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the
            // TV is the System Audio Device instead.)
            if (!isSystemAudioActivated()) {
                disableAbsoluteVolumeControl();
                disableAbsoluteVolumeBehavior();
                return;
            }
        } else if (isPlaybackDevice() && playback() != null) {
            localCecDevice = playback();
        } else {
            // Either this device type doesn't support AVC, or it hasn't fully initialized yet
            // Either this device type doesn't support AVB, or it hasn't fully initialized yet
            return;
        }

        HdmiDeviceInfo systemAudioDeviceInfo = getHdmiCecNetwork().getSafeCecDeviceInfo(
                localCecDevice.findAudioReceiverAddress());
        @AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
                        getDeviceVolumeBehavior(getAvcAudioOutputDevice());
                        getDeviceVolumeBehavior(getAvbAudioOutputDevice());

        // Condition 2: Already using full or absolute volume behavior
        boolean alreadyUsingFullOrAbsoluteVolume =
@@ -4305,45 +4305,46 @@ public class HdmiControlService extends SystemService {
                getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED;

        if (!cecVolumeEnabled || !alreadyUsingFullOrAbsoluteVolume) {
            disableAbsoluteVolumeControl();
            disableAbsoluteVolumeBehavior();
            return;
        }

        // Check for safety: if the System Audio device is a candidate for AVC, we should already
        // Check for safety: if the System Audio device is a candidate for AVB, we should already
        // have received messages from it to trigger the other conditions.
        if (systemAudioDeviceInfo == null) {
            disableAbsoluteVolumeControl();
            disableAbsoluteVolumeBehavior();
            return;
        }
        // Condition 4: The System Audio device supports AVC (i.e. <Set Audio Volume Level>).
        // Condition 4: The System Audio device supports AVB (i.e. <Set Audio Volume Level>).
        switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
            case DeviceFeatures.FEATURE_SUPPORTED:
                if (!isAbsoluteVolumeControlEnabled()) {
                    // Start an action that will call {@link #enableAbsoluteVolumeControl}
                if (!isAbsoluteVolumeBehaviorEnabled()) {
                    // Start an action that will call {@link #enableAbsoluteVolumeBehavior}
                    // once the System Audio device sends <Report Audio Status>
                    localCecDevice.addAvcAudioStatusAction(
                    localCecDevice.addAvbAudioStatusAction(
                            systemAudioDeviceInfo.getLogicalAddress());
                }
                return;
            case DeviceFeatures.FEATURE_NOT_SUPPORTED:
                disableAbsoluteVolumeControl();
                disableAbsoluteVolumeBehavior();
                return;
            case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN:
                disableAbsoluteVolumeControl();
                localCecDevice.queryAvcSupport(systemAudioDeviceInfo.getLogicalAddress());
                disableAbsoluteVolumeBehavior();
                localCecDevice.querySetAudioVolumeLevelSupport(
                        systemAudioDeviceInfo.getLogicalAddress());
                return;
            default:
                return;
        }
    }

    private void disableAbsoluteVolumeControl() {
    private void disableAbsoluteVolumeBehavior() {
        if (isPlaybackDevice()) {
            playback().removeAvcAudioStatusAction();
            playback().removeAvbAudioStatusAction();
        } else if (isTvDevice()) {
            tv().removeAvcAudioStatusAction();
            tv().removeAvbAudioStatusAction();
        }
        AudioDeviceAttributes device = getAvcAudioOutputDevice();
        AudioDeviceAttributes device = getAvbAudioOutputDevice();
        if (getDeviceVolumeBehavior(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
            getAudioManager().setDeviceVolumeBehavior(device,
                    AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
@@ -4351,11 +4352,11 @@ public class HdmiControlService extends SystemService {
    }

    /**
     * Enables Absolute Volume Control. Should only be called when all the conditions for
     * AVC are met (see {@link #checkAndUpdateAbsoluteVolumeControlState}).
     * Enables absolute volume behavior. Should only be called when all the conditions for
     * AVB are met (see {@link #checkAndUpdateAbsoluteVolumeBehavior}).
     * @param audioStatus The initial audio status to set the audio output device to
     */
    void enableAbsoluteVolumeControl(AudioStatus audioStatus) {
    void enableAbsoluteVolumeBehavior(AudioStatus audioStatus) {
        HdmiCecLocalDevice localDevice = isPlaybackDevice() ? playback() : tv();
        HdmiDeviceInfo systemAudioDevice = getHdmiCecNetwork().getDeviceInfo(
                localDevice.findAudioReceiverAddress());
@@ -4370,9 +4371,9 @@ public class HdmiControlService extends SystemService {

        // AudioService sets the volume of the stream and device based on the input VolumeInfo
        // when enabling absolute volume behavior, but not the mute state
        notifyAvcMuteChange(audioStatus.getMute());
        notifyAvbMuteChange(audioStatus.getMute());
        getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
                getAvcAudioOutputDevice(), volumeInfo, mServiceThreadExecutor,
                getAvbAudioOutputDevice(), volumeInfo, mServiceThreadExecutor,
                mAbsoluteVolumeChangedListener, true);
    }

@@ -4418,8 +4419,8 @@ public class HdmiControlService extends SystemService {
                        if (errorCode == SendMessageResult.SUCCESS) {
                            // Update the volume tracked in our AbsoluteVolumeAudioStatusAction
                            // so it correctly processes incoming <Report Audio Status> messages
                            HdmiCecLocalDevice avcDevice = isTvDevice() ? tv() : playback();
                            avcDevice.updateAvcVolume(volumeInfo.getVolumeIndex());
                            HdmiCecLocalDevice avbDevice = isTvDevice() ? tv() : playback();
                            avbDevice.updateAvbVolume(volumeInfo.getVolumeIndex());
                        } else {
                            sendCecCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
                                    localDeviceAddress,
@@ -4477,13 +4478,13 @@ public class HdmiControlService extends SystemService {

    /**
     * Notifies AudioService of a change in the volume of the System Audio device. Has no effect if
     * AVC is disabled, or the audio output device for AVC is not playing for STREAM_MUSIC
     * AVB is disabled, or the audio output device for AVB is not playing for STREAM_MUSIC
     */
    void notifyAvcVolumeChange(int volume) {
        if (!isAbsoluteVolumeControlEnabled()) return;
    void notifyAvbVolumeChange(int volume) {
        if (!isAbsoluteVolumeBehaviorEnabled()) return;
        List<AudioDeviceAttributes> streamMusicDevices =
                getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
        if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
        if (streamMusicDevices.contains(getAvbAudioOutputDevice())) {
            int flags = AudioManager.FLAG_ABSOLUTE_VOLUME;
            if (isTvDevice()) {
                flags |= AudioManager.FLAG_SHOW_UI;
@@ -4494,13 +4495,13 @@ public class HdmiControlService extends SystemService {

    /**
     * Notifies AudioService of a change in the mute status of the System Audio device. Has no
     * effect if AVC is disabled, or the audio output device for AVC is not playing for STREAM_MUSIC
     * effect if AVB is disabled, or the audio output device for AVB is not playing for STREAM_MUSIC
     */
    void notifyAvcMuteChange(boolean mute) {
        if (!isAbsoluteVolumeControlEnabled()) return;
    void notifyAvbMuteChange(boolean mute) {
        if (!isAbsoluteVolumeBehaviorEnabled()) return;
        List<AudioDeviceAttributes> streamMusicDevices =
                getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
        if (streamMusicDevices.contains(getAvcAudioOutputDevice())) {
        if (streamMusicDevices.contains(getAvbAudioOutputDevice())) {
            int direction = mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE;
            int flags = AudioManager.FLAG_ABSOLUTE_VOLUME;
            if (isTvDevice()) {
+2 −2
Original line number Diff line number Diff line
@@ -172,10 +172,10 @@ final class SendKeyAction extends HdmiCecFeatureAction {
    }

    private void sendKeyUp() {
        // When using Absolute Volume Control, query audio status after a volume key is released.
        // When using absolute volume behavior, query audio status after a volume key is released.
        // This allows us to notify AudioService of the resulting volume or mute status changes.
        if (HdmiCecKeycode.isVolumeKeycode(mLastKeycode)
                && localDevice().getService().isAbsoluteVolumeControlEnabled()) {
                && localDevice().getService().isAbsoluteVolumeBehaviorEnabled()) {
            sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
                    mTargetAddress),
                    __ -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
Loading