From e9565df5b8b8a56b47dc0525f670d5d0a85aea8d Mon Sep 17 00:00:00 2001 From: Yan Han Date: Wed, 16 Jun 2021 17:47:31 +0200 Subject: [PATCH] Support "Mute" and "Restore Volume" CEC commands Adds support for ["Mute Function"] and ["Restore Volume Function"] on all HDMI devices. These commands are currently ignored because they are not mapped to an Android keycode. Support for these commands is required for HDMI CEC 2.0. Bug: 191230341 Test: Manual; atest HdmiCecLocalDeviceTest Change-Id: I5146e9e99595a9a124e0ce2176f5e70a29923a49 --- .../server/hdmi/HdmiCecLocalDevice.java | 19 +++++++++ .../server/hdmi/HdmiCecLocalDeviceTest.java | 39 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 505e743ed9a1a..a2cb78d27a57d 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -23,6 +23,7 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.input.InputManager; import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.media.AudioManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -685,11 +686,29 @@ abstract class HdmiCecLocalDevice { Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT), FOLLOWER_SAFETY_TIMEOUT); return Constants.HANDLED; + } else if (params.length > 0) { + // Handle CEC UI commands that are not mapped to an Android keycode + return handleUnmappedCecKeycode(params[0]); } return Constants.ABORT_INVALID_OPERAND; } + @ServiceThreadOnly + @Constants.HandleMessageResult + protected int handleUnmappedCecKeycode(int cecKeycode) { + if (cecKeycode == HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION) { + mService.getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_MUTE, AudioManager.FLAG_SHOW_UI); + return Constants.HANDLED; + } else if (cecKeycode == HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION) { + mService.getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_UNMUTE, AudioManager.FLAG_SHOW_UI); + return Constants.HANDLED; + } + return Constants.ABORT_INVALID_OPERAND; + } + @ServiceThreadOnly @Constants.HandleMessageResult protected int handleUserControlReleased() { 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 68803023c4515..6502e4813171b 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -32,9 +32,15 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; +import android.media.AudioManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -45,6 +51,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; @@ -123,8 +131,12 @@ public class HdmiCecLocalDeviceTest { private boolean isControlEnabled; private int mPowerStatus; + @Mock + private AudioManager mAudioManager; + @Before public void SetUp() { + MockitoAnnotations.initMocks(this); Context context = InstrumentationRegistry.getTargetContext(); @@ -161,6 +173,11 @@ public class HdmiCecLocalDeviceTest { void wakeUp() { mWakeupMessageReceived = true; } + + @Override + AudioManager getAudioManager() { + return mAudioManager; + } }; mHdmiControlService.setIoLooper(mTestLooper.getLooper()); mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); @@ -418,6 +435,28 @@ public class HdmiCecLocalDeviceTest { assertThat(mStandbyMessageReceived).isTrue(); } + @Test + public void handleUserControlPressed_muteFunction() { + @Constants.HandleMessageResult int result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_TV, ADDR_PLAYBACK_1, + HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION)); + + assertEquals(result, Constants.HANDLED); + verify(mAudioManager, times(1)) + .adjustStreamVolume(anyInt(), eq(AudioManager.ADJUST_MUTE), anyInt()); + } + + @Test + public void handleUserControlPressed_restoreVolumeFunction() { + @Constants.HandleMessageResult int result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_TV, ADDR_PLAYBACK_1, + HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION)); + + assertEquals(result, Constants.HANDLED); + verify(mAudioManager, times(1)) + .adjustStreamVolume(anyInt(), eq(AudioManager.ADJUST_UNMUTE), anyInt()); + } + @Test public void handleVendorCommand_notHandled() { HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommand(ADDR_TV,