From 309efbc7704e4af34d3d322cee02df1a62efa1bc Mon Sep 17 00:00:00 2001 From: Marvin Ramin Date: Mon, 16 Mar 2020 14:17:58 +0100 Subject: [PATCH] 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 Change-Id: I83ae9b423122b540b9adb156fb1c6f5964dd6105 --- .../hardware/hdmi/HdmiControlManager.java | 62 ++++++++++ .../hardware/hdmi/IHdmiControlService.aidl | 2 + core/java/android/provider/Settings.java | 37 ++++++ .../hdmi/HdmiAudioSystemClientTest.java | 9 ++ .../android/provider/SettingsBackupTest.java | 1 + .../server/hdmi/HdmiCecLocalDevice.java | 19 ++- .../hdmi/HdmiCecLocalDeviceAudioSystem.java | 5 +- .../server/hdmi/HdmiCecLocalDeviceTv.java | 9 +- .../server/hdmi/HdmiControlService.java | 60 +++++++++- .../HdmiCecLocalDeviceAudioSystemTest.java | 112 ++++++++++++++++++ .../hdmi/HdmiCecLocalDevicePlaybackTest.java | 72 +++++++++++ .../server/hdmi/HdmiCecLocalDeviceTest.java | 61 ++++++++++ .../server/hdmi/HdmiControlServiceTest.java | 9 ++ 13 files changed, 452 insertions(+), 6 deletions(-) diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 65a8e15ba340d..6bc962b675764 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -649,6 +649,68 @@ public final class HdmiControlManager { } } + /** + * Controls whether volume control commands via HDMI CEC are enabled. + * + *

When disabled: + *

+ * + *

Effects on different device types: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
HDMI CEC device typeenableddisabled
TV (type: 0)Per CEC specification.TV changes system volume. TV no longer reacts to incoming volume changes via + * {@code }. TV no longer handles {@code } + * .
Playback device (type: 4)Device sends volume commands to TV/Audio system via {@code }Device does not send volume commands via {@code }.
Audio device (type: 5)Full "System Audio Control" capabilities.Audio device no longer reacts to incoming {@code } + * volume commands. Audio device no longer reports volume changes via {@code }.
+ * + *

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. * diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index a8fed2b03cfcc..3582a927ff469 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -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(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4eb563f70152c..0e5d03b9faf24 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9646,6 +9646,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). + * + *

Effects on different device types: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
HDMI CEC device type0: disabled1: enabled
TV (type: 0)Per CEC specification.TV changes system volume. TV no longer reacts to incoming volume changes + * via {@code }. TV no longer handles {@code }.
Playback device (type: 4)Device sends volume commands to TV/Audio system via {@code }Device does not send volume commands via {@code }.
Audio device (type: 5)Full "System Audio Control" capabilities.Audio device no longer reacts to incoming {@code } + * volume commands. Audio device no longer reports volume changes via {@code + * }.
+ * + *

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 diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 2141b815fb3bb..7cd2f3b4c2ab2 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -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; + } } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 03f6df0d5d2dc..0dd7fb80379d0 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -296,6 +296,7 @@ public class SettingsBackupTest { Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, + Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HIDDEN_API_POLICY, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index a358707a43463..3ff6ec1afa41a 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -32,6 +32,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; @@ -144,7 +145,8 @@ abstract class HdmiCecLocalDevice { // A collection of FeatureAction. // Note that access to this collection should happen in service thread. - private final ArrayList mActions = new ArrayList<>(); + @VisibleForTesting + final ArrayList mActions = new ArrayList<>(); private final Handler mHandler = new Handler() { @@ -544,6 +546,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(); @@ -618,6 +622,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; } @@ -1038,6 +1052,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; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index e5a08d3e79aa1..611b8c69077db 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -575,7 +575,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly protected boolean handleGiveAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); - if (isSystemAudioControlFeatureEnabled()) { + if (isSystemAudioControlFeatureEnabled() && mService.isHdmiCecVolumeControlEnabled()) { reportAudioStatus(message.getSource()); } else { mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); @@ -930,6 +930,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { void reportAudioStatus(int source) { assertRunOnServiceThread(); + if (!mService.isHdmiCecVolumeControlEnabled()) { + return; + } int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC); boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 7e49bdb0cd089..d0bd635c22559 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -680,6 +680,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleReportAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); + if (!mService.isHdmiCecVolumeControlEnabled()) { + return false; + } boolean mute = HdmiUtils.isAudioStatusMute(message); int volume = HdmiUtils.getAudioStatusVolume(message); @@ -989,7 +992,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } void setAudioStatus(boolean mute, int volume) { - if (!isSystemAudioActivated()) { + if (!isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) { return; } synchronized (mLock) { @@ -1011,7 +1014,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // On initialization process, getAvrDeviceInfo() may return null and cause exception return; } - if (delta == 0 || !isSystemAudioActivated()) { + if (delta == 0 || !isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) { return; } @@ -1040,7 +1043,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void changeMute(boolean mute) { assertRunOnServiceThread(); - if (getAvrDeviceInfo() == null) { + if (getAvrDeviceInfo() == null || !mService.isHdmiCecVolumeControlEnabled()) { // On initialization process, getAvrDeviceInfo() may return null and cause exception return; } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index b91b6a6a8063a..66b85403503b1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -181,6 +181,10 @@ public class HdmiControlService extends SystemService { @GuardedBy("mLock") private boolean mSystemAudioActivated = false; + // Whether HDMI CEC volume control is enabled or not. + @GuardedBy("mLock") + private boolean mHdmiCecVolumeControlEnabled; + /** * Interface to report send result. */ @@ -493,6 +497,8 @@ public class HdmiControlService extends SystemService { mPowerStatus = getInitialPowerStatus(); mProhibitMode = false; mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); + mHdmiCecVolumeControlEnabled = readBooleanSetting( + Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true); mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true); if (mCecController == null) { @@ -642,6 +648,7 @@ public class HdmiControlService extends SystemService { ContentResolver resolver = getContext().getContentResolver(); String[] settings = new String[] { Global.HDMI_CONTROL_ENABLED, + Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, @@ -670,6 +677,9 @@ public class HdmiControlService extends SystemService { case Global.HDMI_CONTROL_ENABLED: setControlEnabled(enabled); break; + case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED: + setHdmiCecVolumeControlEnabled(enabled); + break; case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED: if (isTvDeviceEnabled()) { tv().setAutoWakeup(enabled); @@ -1261,7 +1271,9 @@ public class HdmiControlService extends SystemService { } void setAudioStatus(boolean mute, int volume) { - if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated()) { + if (!isTvDeviceEnabled() + || !tv().isSystemAudioActivated() + || !isHdmiCecVolumeControlEnabled()) { return; } AudioManager audioManager = getAudioManager(); @@ -2174,6 +2186,24 @@ public class HdmiControlService extends SystemService { }); } + @Override + public boolean isHdmiCecVolumeControlEnabled() { + enforceAccessPermission(); + return HdmiControlService.this.isHdmiCecVolumeControlEnabled(); + } + + @Override + public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiControlService.this.setHdmiCecVolumeControlEnabled( + isHdmiCecVolumeControlEnabled); + } + }); + } + @Override public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume, final boolean isMute) { @@ -2238,6 +2268,7 @@ public class HdmiControlService extends SystemService { pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled); pw.println("mSystemAudioActivated: " + isSystemAudioActivated()); + pw.println("mHdmiCecVolumeControlEnabled " + mHdmiCecVolumeControlEnabled); pw.decreaseIndent(); pw.println("mMhlController: "); @@ -2970,6 +3001,29 @@ public class HdmiControlService extends SystemService { } } + void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) { + assertRunOnServiceThread(); + synchronized (mLock) { + mHdmiCecVolumeControlEnabled = isHdmiCecVolumeControlEnabled; + + boolean storedValue = readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, + true); + if (storedValue != isHdmiCecVolumeControlEnabled) { + HdmiLogger.debug("Changing HDMI CEC volume control feature state: %s", + isHdmiCecVolumeControlEnabled); + writeBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, + isHdmiCecVolumeControlEnabled); + } + } + } + + boolean isHdmiCecVolumeControlEnabled() { + assertRunOnServiceThread(); + synchronized (mLock) { + return mHdmiCecVolumeControlEnabled; + } + } + boolean isProhibitMode() { synchronized (mLock) { return mProhibitMode; @@ -3010,8 +3064,12 @@ public class HdmiControlService extends SystemService { if (enabled) { enableHdmiControlService(); + setHdmiCecVolumeControlEnabled( + readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true)); return; } + + setHdmiCecVolumeControlEnabled(false); // Call the vendor handler before the service is disabled. invokeVendorCommandListenersOnControlStateChanged(false, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index b5c4374e4371e..0ee1af920989c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -24,6 +24,7 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_TUNER_1; import static com.android.server.hdmi.Constants.ADDR_TV; +import static com.android.server.hdmi.Constants.MESSAGE_GIVE_AUDIO_STATUS; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; @@ -48,6 +49,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.ArrayList; + @SmallTest @Presubmit @RunWith(JUnit4.class) @@ -169,6 +171,8 @@ public class HdmiCecLocalDeviceAudioSystemTest { } }; + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mMyLooper = mTestLooper.getLooper(); mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService); mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) { @@ -719,4 +723,112 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiCecLocalDeviceAudioSystem.onHotplug(0, false); assertThat(mWokenUp).isFalse(); } + + @Test + public void giveAudioStatus_volumeEnabled() { + mMusicVolume = 50; + mMusicMaxVolume = 100; + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); + + int volume = mHdmiControlService.getAudioManager() + .getStreamVolume(AudioManager.STREAM_MUSIC); + boolean mute = mHdmiControlService.getAudioManager() + .isStreamMute(AudioManager.STREAM_MUSIC); + int maxVolume = mHdmiControlService.getAudioManager() + .getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); + HdmiCecMessage expected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM, + ADDR_TV, scaledVolume, mute); + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, ADDR_TV, MESSAGE_GIVE_AUDIO_STATUS, Constants.ABORT_REFUSED); + + HdmiCecMessage giveAudioStatus = HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, + ADDR_AUDIO_SYSTEM); + mNativeWrapper.clearResultMessages(); + boolean handled = mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(giveAudioStatus); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(expected); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbort); + assertThat(handled).isTrue(); + } + + @Test + public void giveAudioStatus_volumeDisabled() { + mMusicVolume = 50; + mMusicMaxVolume = 100; + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); + + int volume = mHdmiControlService.getAudioManager() + .getStreamVolume(AudioManager.STREAM_MUSIC); + boolean mute = mHdmiControlService.getAudioManager() + .isStreamMute(AudioManager.STREAM_MUSIC); + int maxVolume = mHdmiControlService.getAudioManager() + .getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); + HdmiCecMessage unexpected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM, + ADDR_TV, scaledVolume, mute); + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, ADDR_TV, MESSAGE_GIVE_AUDIO_STATUS, Constants.ABORT_REFUSED); + + HdmiCecMessage giveAudioStatus = HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, + ADDR_AUDIO_SYSTEM); + mNativeWrapper.clearResultMessages(); + boolean handled = mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(giveAudioStatus); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpected); + assertThat(handled).isTrue(); + } + + @Test + public void reportAudioStatus_volumeEnabled() { + mMusicVolume = 50; + mMusicMaxVolume = 100; + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); + + int volume = mHdmiControlService.getAudioManager() + .getStreamVolume(AudioManager.STREAM_MUSIC); + boolean mute = mHdmiControlService.getAudioManager() + .isStreamMute(AudioManager.STREAM_MUSIC); + int maxVolume = mHdmiControlService.getAudioManager() + .getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); + HdmiCecMessage expected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM, + ADDR_TV, scaledVolume, mute); + + mNativeWrapper.clearResultMessages(); + mHdmiCecLocalDeviceAudioSystem.reportAudioStatus(ADDR_TV); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(expected); + } + + @Test + public void reportAudioStatus_volumeDisabled() { + mMusicVolume = 50; + mMusicMaxVolume = 100; + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true); + + int volume = mHdmiControlService.getAudioManager() + .getStreamVolume(AudioManager.STREAM_MUSIC); + boolean mute = mHdmiControlService.getAudioManager() + .isStreamMute(AudioManager.STREAM_MUSIC); + int maxVolume = mHdmiControlService.getAudioManager() + .getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume); + HdmiCecMessage unexpected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM, + ADDR_TV, scaledVolume, mute); + + mNativeWrapper.clearResultMessages(); + mHdmiCecLocalDeviceAudioSystem.reportAudioStatus(ADDR_TV); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpected); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 4841900fa0689..0eb53045c3629 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Looper; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; +import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -155,4 +156,75 @@ public class HdmiCecLocalDevicePlaybackTest { mHdmiCecLocalDevicePlayback.onHotplug(0, false); assertThat(mWokenUp).isFalse(); } + + @Test + @Ignore("b/151147315") + public void sendVolumeKeyEvent_up_volumeEnabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + assertThat(hasSendKeyAction()).isTrue(); + } + + @Test + @Ignore("b/151147315") + public void sendVolumeKeyEvent_down_volumeEnabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false); + + assertThat(hasSendKeyAction()).isTrue(); + } + + @Test + @Ignore("b/151147315") + public void sendVolumeKeyEvent_mute_volumeEnabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false); + + assertThat(hasSendKeyAction()).isTrue(); + } + + @Test + @Ignore("b/151147315") + public void sendVolumeKeyEvent_up_volumeDisabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + assertThat(hasSendKeyAction()).isFalse(); + } + + @Test + @Ignore("b/151147315") + public void sendVolumeKeyEvent_down_volumeDisabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false); + + assertThat(hasSendKeyAction()).isFalse(); + } + + @Test + @Ignore("b/151147315") + public void sendVolumeKeyEvent_mute_volumeDisabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false); + + assertThat(hasSendKeyAction()).isFalse(); + } + + private boolean hasSendKeyAction() { + boolean match = false; + for (HdmiCecFeatureAction action : mHdmiCecLocalDevicePlayback.mActions) { + if (action instanceof SendKeyAction) { + match = true; + break; + } + } + return match; + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java index c22d6ac51e0cb..526c1d07296fd 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -19,6 +19,7 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV; import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID; @@ -187,4 +188,64 @@ public class HdmiCecLocalDeviceTest { HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM)); assertTrue(mStandbyMessageReceived); } + + @Test + public void handleUserControlPressed_volumeUp() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + boolean result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP)); + + assertTrue(result); + } + + @Test + public void handleUserControlPressed_volumeDown() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + boolean result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); + + assertTrue(result); + } + + @Test + public void handleUserControlPressed_volumeMute() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + boolean result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_MUTE)); + + assertTrue(result); + } + + @Test + public void handleUserControlPressed_volumeUp_disabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + boolean result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP)); + + assertFalse(result); + } + + @Test + public void handleUserControlPressed_volumeDown_disabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + boolean result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN)); + + assertFalse(result); + } + + @Test + public void handleUserControlPressed_volumeMute_disabled() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + boolean result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_MUTE)); + + assertFalse(result); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 70284787a382b..a1241cfa4881e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -254,4 +254,13 @@ public class HdmiControlServiceTest { assertThat(mHdmiControlService.getPowerStatus()).isEqualTo( HdmiControlManager.POWER_STATUS_STANDBY); } + + @Test + public void setAndGetCecVolumeControlEnabled_isApi() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(false); + assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse(); + + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue(); + } }