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

Commit dccbb024 authored by Marvin Ramin's avatar Marvin Ramin Committed by Aaron Kling
Browse files

CEC: Make HDMI CEC volume control configurable

Add API (backed by Setting) to enable/disable volume changes via HDMI
CEC for HDMI CEC source devices.
This state is persisted via Settings.

If volume control is disabled, no incoming HDMI CEC commands related to
volume will be processed. If disabled, no HDMI CEC volume control
messages will be sent by the device.

Test: atest com.android.server.hdmi
Bug: 149800547

Merged-In: I83ae9b423122b540b9adb156fb1c6f5964dd6105
Change-Id: I83ae9b423122b540b9adb156fb1c6f5964dd6105
parent b093ac46
Loading
Loading
Loading
Loading
+62 −0
Original line number Diff line number Diff line
@@ -592,6 +592,68 @@ public final class HdmiControlManager {
        }
    }

    /**
     * Controls whether volume control commands via HDMI CEC are enabled.
     *
     * <p>When disabled:
     * <ul>
     *     <li>the device will not send any HDMI CEC audio messages
     *     <li>received HDMI CEC audio messages are responded to with {@code <Feature Abort>}
     * </ul>
     *
     * <p>Effects on different device types:
     * <table>
     *     <tr><th>HDMI CEC device type</th><th>enabled</th><th>disabled</th></tr>
     *     <tr>
     *         <td>TV (type: 0)</td>
     *         <td>Per CEC specification.</td>
     *         <td>TV changes system volume. TV no longer reacts to incoming volume changes via
     *         {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio Status>}
     *         .</td>
     *     </tr>
     *     <tr>
     *         <td>Playback device (type: 4)</td>
     *         <td>Device sends volume commands to TV/Audio system via {@code <User Control
     *         Pressed>}</td><td>Device does not send volume commands via {@code <User Control
     *         Pressed>}.</td>
     *     </tr>
     *     <tr>
     *         <td>Audio device (type: 5)</td>
     *         <td>Full "System Audio Control" capabilities.</td>
     *         <td>Audio device no longer reacts to incoming {@code <User Control Pressed>}
     *         volume commands. Audio device no longer reports volume changes via {@code <Report
     *         Audio Status>}.</td>
     *     </tr>
     * </table>
     *
     * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
     *
     * @param isHdmiCecVolumeControlEnabled target state of HDMI CEC volume control.
     * @see Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
    public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
        try {
            mService.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Returns whether volume changes via HDMI CEC are enabled.
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
    public boolean isHdmiCecVolumeControlEnabled() {
        try {
            return mService.isHdmiCecVolumeControlEnabled();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Gets whether the system is in system audio mode.
     *
+2 −0
Original line number Diff line number Diff line
@@ -80,6 +80,8 @@ interface IHdmiControlService {
    void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data);
    void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
    void setStandbyMode(boolean isStandbyModeOn);
    void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled);
    boolean isHdmiCecVolumeControlEnabled();
    void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
    void setSystemAudioModeOnForAudioOnlySource();
}
+37 −0
Original line number Diff line number Diff line
@@ -10075,6 +10075,43 @@ public final class Settings {
        */
       public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
        /**
         * Controls whether volume control commands via HDMI CEC are enabled. (0 = false, 1 =
         * true).
         *
         * <p>Effects on different device types:
         * <table>
         *     <tr><th>HDMI CEC device type</th><th>0: disabled</th><th>1: enabled</th></tr>
         *     <tr>
         *         <td>TV (type: 0)</td>
         *         <td>Per CEC specification.</td>
         *         <td>TV changes system volume. TV no longer reacts to incoming volume changes
         *         via {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio
         *         Status>}.</td>
         *     </tr>
         *     <tr>
         *         <td>Playback device (type: 4)</td>
         *         <td>Device sends volume commands to TV/Audio system via {@code <User Control
         *         Pressed>}</td>
         *         <td>Device does not send volume commands via {@code <User Control Pressed>}.</td>
         *     </tr>
         *     <tr>
         *         <td>Audio device (type: 5)</td>
         *         <td>Full "System Audio Control" capabilities.</td>
         *         <td>Audio device no longer reacts to incoming {@code <User Control Pressed>}
         *         volume commands. Audio device no longer reports volume changes via {@code
         *         <Report Audio Status>}.</td>
         *     </tr>
         * </table>
         *
         * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
         *
         * @hide
         * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)
         */
        public static final String HDMI_CONTROL_VOLUME_CONTROL_ENABLED =
                "hdmi_control_volume_control_enabled";
       /**
        * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
        * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will
+9 −0
Original line number Diff line number Diff line
@@ -354,6 +354,15 @@ public class HdmiAudioSystemClientTest {
        @Override
        public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
        }

        @Override
        public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
        }

        @Override
        public boolean isHdmiCecVolumeControlEnabled() {
            return true;
        }
    }

}
+18 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.Constants.LocalActivePort;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -143,7 +144,8 @@ abstract class HdmiCecLocalDevice {

    // A collection of FeatureAction.
    // Note that access to this collection should happen in service thread.
    private final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>();
    @VisibleForTesting
    final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>();

    private final Handler mHandler =
            new Handler() {
@@ -532,6 +534,8 @@ abstract class HdmiCecLocalDevice {
        } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
            mService.wakeUp();
            return true;
        } else if (!mService.isHdmiCecVolumeControlEnabled() && isVolumeOrMuteCommand(message)) {
            return false;
        }

        final long downTime = SystemClock.uptimeMillis();
@@ -606,6 +610,16 @@ abstract class HdmiCecLocalDevice {
                        || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
    }

    static boolean isVolumeOrMuteCommand(HdmiCecMessage message) {
        byte[] params = message.getParams();
        return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
                && (params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN
                    || params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
                    || params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE
                    || params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION
                    || params[0] == HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
    }

    protected boolean handleTextViewOn(HdmiCecMessage message) {
        return false;
    }
@@ -1026,6 +1040,9 @@ abstract class HdmiCecLocalDevice {
    @ServiceThreadOnly
    protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) {
        assertRunOnServiceThread();
        if (!mService.isHdmiCecVolumeControlEnabled()) {
            return;
        }
        if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) {
            Slog.w(TAG, "Not a volume key: " + keyCode);
            return;
Loading