summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yan Han <yanha@google.com> 2021-12-15 10:04:54 +0100
committer Yan Han <yanha@google.com> 2022-01-14 18:57:43 +0100
commitdcccddfbb10a4dfdf2152fa3d581d18a1b503b9e (patch)
treeddc4e02ca2a101532acbabc060e83ab7e7e56ece
parent2af8848d6921ea6d561290ceaa54812b55cfff33 (diff)
Add feature action for sending <Give Features>
The action reports whether the target device responds with <Report Features>. Test: atest GiveFeaturesActionTest Bug: 199144863 Change-Id: I45653246bdcf384c623c570435d16f5fe797d7d9
-rw-r--r--services/core/java/com/android/server/hdmi/GiveFeaturesAction.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java198
2 files changed, 277 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java
new file mode 100644
index 000000000000..45425657c0f6
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Sends <Give Features> to a target device. This action succeeds if the device responds with
+ * <Report Features> within {@link HdmiConfig.TIMEOUT_MS}.
+ *
+ * This action does not update the CEC network directly; an incoming <Report Features> message
+ * should be handled separately by {@link HdmiCecNetwork}.
+ */
+public class GiveFeaturesAction extends HdmiCecFeatureAction {
+ private static final String TAG = "GiveFeaturesAction";
+
+ private static final int STATE_WAITING_FOR_REPORT_FEATURES = 1;
+
+ private final int mTargetAddress;
+
+ public GiveFeaturesAction(HdmiCecLocalDevice source, int targetAddress,
+ IHdmiControlCallback callback) {
+ super(source, callback);
+
+ mTargetAddress = targetAddress;
+ }
+
+ boolean start() {
+ sendCommand(HdmiCecMessageBuilder.buildGiveFeatures(getSourceAddress(), mTargetAddress),
+ result -> {
+ if (result == SendMessageResult.SUCCESS) {
+ mState = STATE_WAITING_FOR_REPORT_FEATURES;
+ addTimer(STATE_WAITING_FOR_REPORT_FEATURES, HdmiConfig.TIMEOUT_MS);
+ } else {
+ finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+ }
+ });
+ return true;
+ }
+
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_REPORT_FEATURES) {
+ return false;
+ }
+ if (cmd instanceof ReportFeaturesMessage) {
+ return handleReportFeatures((ReportFeaturesMessage) cmd);
+ }
+ return false;
+ }
+
+ private boolean handleReportFeatures(ReportFeaturesMessage cmd) {
+ if (cmd.getSource() == mTargetAddress) {
+ // No need to update the network, since it should already have processed this message.
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ return true;
+ }
+ return false;
+ }
+
+ void handleTimerEvent(int state) {
+ finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java
new file mode 100644
index 000000000000..0b31db69f0a9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
+import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
+import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_VERSION_2_0;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.DeviceFeatures;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class GiveFeaturesActionTest {
+ private HdmiControlService mHdmiControlServiceSpy;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDevicePlayback mPlaybackDevice;
+ private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
+ private Looper mLooper;
+ private Context mContextSpy;
+ private TestLooper mTestLooper = new TestLooper();
+ private int mPhysicalAddress = 0x1100;
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPlaybackLogicalAddress;
+
+ private TestCallback mTestCallback;
+ private GiveFeaturesAction mAction;
+
+ /**
+ * Setup: Local Playback device queries the features of a connected TV.
+ */
+ @Before
+ public void setUp() throws RemoteException {
+ mContextSpy = spy(new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
+
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
+ doNothing().when(mHdmiControlServiceSpy)
+ .writeStringSystemProperty(anyString(), anyString());
+
+ mLooper = mTestLooper.getLooper();
+ mHdmiControlServiceSpy.setIoLooper(mLooper);
+ mHdmiControlServiceSpy.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlServiceSpy, mNativeWrapper, mHdmiControlServiceSpy.getAtomWriter());
+ mHdmiControlServiceSpy.setCecController(mHdmiCecController);
+ mHdmiControlServiceSpy.setHdmiMhlController(
+ HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
+ mHdmiControlServiceSpy.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlServiceSpy.setPowerManager(mPowerManager);
+
+ mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy);
+ mPlaybackDevice.init();
+ mLocalDevices.add(mPlaybackDevice);
+
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mTestLooper.dispatchAll();
+
+ synchronized (mPlaybackDevice.mLock) {
+ mPlaybackLogicalAddress = mPlaybackDevice.getDeviceInfo().getLogicalAddress();
+ }
+
+ // Setup specific to these tests
+ mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ Constants.ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV));
+ mTestLooper.dispatchAll();
+
+ mTestCallback = new TestCallback();
+ mAction = new GiveFeaturesAction(mPlaybackDevice, Constants.ADDR_TV, mTestCallback);
+ }
+
+ @Test
+ public void sendsGiveFeaturesMessage() {
+ mPlaybackDevice.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveFeatures = HdmiCecMessageBuilder.buildGiveFeatures(
+ mPlaybackLogicalAddress, Constants.ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveFeatures);
+ }
+
+ @Test
+ public void noMatchingReportFeaturesReceived_actionFailsAndNetworkIsNotUpdated() {
+ mPlaybackDevice.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ // Wrong source
+ mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
+ Constants.ADDR_AUDIO_SYSTEM, HdmiControlManager.HDMI_CEC_VERSION_2_0,
+ Arrays.asList(DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_SOURCE,
+ Collections.emptyList(), DeviceFeatures.NO_FEATURES_SUPPORTED));
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ @DeviceFeatures.FeatureSupportStatus int avcSupport =
+ mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+ .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+ assertThat(avcSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
+ assertThat(mTestCallback.getResult()).isEqualTo(
+ HdmiControlManager.RESULT_COMMUNICATION_FAILED);
+ }
+
+ @Test
+ public void matchingReportFeaturesReceived_actionSucceedsAndNetworkIsUpdated() {
+ mPlaybackDevice.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ mNativeWrapper.onCecMessage(
+ ReportFeaturesMessage.build(
+ Constants.ADDR_TV, HDMI_CEC_VERSION_2_0, Collections.emptyList(),
+ Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
+ DeviceFeatures.NO_FEATURES_SUPPORTED.toBuilder()
+ .setSetAudioVolumeLevelSupport(FEATURE_SUPPORTED)
+ .build()
+ )
+ );
+ mTestLooper.dispatchAll();
+
+ @DeviceFeatures.FeatureSupportStatus int avcSupport =
+ mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
+ .getDeviceFeatures().getSetAudioVolumeLevelSupport();
+
+ assertThat(avcSupport).isEqualTo(FEATURE_SUPPORTED);
+ assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ private static class TestCallback extends IHdmiControlCallback.Stub {
+ private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assertThat(mCallbackResult.size()).isEqualTo(1);
+ return mCallbackResult.get(0);
+ }
+ }
+}