diff --git a/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java b/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java deleted file mode 100644 index 45425657c0f64..0000000000000 --- a/services/core/java/com/android/server/hdmi/GiveFeaturesAction.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 to a target device. This action succeeds if the device responds with - * within {@link HdmiConfig.TIMEOUT_MS}. - * - * This action does not update the CEC network directly; an incoming 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/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 64971746dce96..185eaa92d511f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -19,6 +19,7 @@ package com.android.server.hdmi; import static com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import android.annotation.Nullable; +import android.hardware.hdmi.DeviceFeatures; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -512,6 +513,9 @@ public class HdmiCecNetwork { } switch (message.getOpcode()) { + case Constants.MESSAGE_FEATURE_ABORT: + handleFeatureAbort(message); + break; case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS: handleReportPhysicalAddress(message); break; @@ -543,6 +547,38 @@ public class HdmiCecNetwork { updateCecDevice(newDeviceInfo); } + @ServiceThreadOnly + private void handleFeatureAbort(HdmiCecMessage message) { + assertRunOnServiceThread(); + + if (message.getParams().length < 2) { + return; + } + + int originalOpcode = message.getParams()[0] & 0xFF; + int reason = message.getParams()[1] & 0xFF; + + // Check if we received in response to . + // This provides information on whether the source supports the message. + if (originalOpcode == Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL) { + + @DeviceFeatures.FeatureSupportStatus int featureSupport = + reason == Constants.ABORT_UNRECOGNIZED_OPCODE + ? DeviceFeatures.FEATURE_NOT_SUPPORTED + : DeviceFeatures.FEATURE_SUPPORT_UNKNOWN; + + HdmiDeviceInfo currentDeviceInfo = getCecDeviceInfo(message.getSource()); + HdmiDeviceInfo newDeviceInfo = currentDeviceInfo.toBuilder() + .updateDeviceFeatures( + currentDeviceInfo.getDeviceFeatures().toBuilder() + .setSetAudioVolumeLevelSupport(featureSupport) + .build() + ) + .build(); + updateCecDevice(newDeviceInfo); + } + } + @ServiceThreadOnly private void handleCecVersion(HdmiCecMessage message) { assertRunOnServiceThread(); diff --git a/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java index 5154669a90ab2..96fd003c72b48 100644 --- a/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryAction.java @@ -16,7 +16,6 @@ package com.android.server.hdmi; -import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED; import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED; import static com.android.server.hdmi.Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL; @@ -78,13 +77,13 @@ public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction { } private boolean handleFeatureAbort(HdmiCecMessage cmd) { + if (cmd.getParams().length < 2) { + return false; + } int originalOpcode = cmd.getParams()[0] & 0xFF; if (originalOpcode == MESSAGE_SET_AUDIO_VOLUME_LEVEL && cmd.getSource() == mTargetAddress) { - if (updateAvcSupport(FEATURE_NOT_SUPPORTED)) { - finishWithCallback(HdmiControlManager.RESULT_SUCCESS); - } else { - finishWithCallback(HdmiControlManager.RESULT_EXCEPTION); - } + // No need to update the network, since it should already have processed this message. + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); return true; } return false; @@ -107,7 +106,7 @@ public class SetAudioVolumeLevelDiscoveryAction extends HdmiCecFeatureAction { */ private boolean updateAvcSupport( @DeviceFeatures.FeatureSupportStatus int setAudioVolumeLevelSupport) { - HdmiCecNetwork network = source().mService.getHdmiCecNetwork(); + HdmiCecNetwork network = localDevice().mService.getHdmiCecNetwork(); HdmiDeviceInfo currentDeviceInfo = network.getCecDeviceInfo(mTargetAddress); if (currentDeviceInfo == null) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java deleted file mode 100644 index 0b31db69f0a9f..0000000000000 --- a/services/tests/servicestests/src/com/android/server/hdmi/GiveFeaturesActionTest.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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 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 mCallbackResult = new ArrayList(); - - @Override - public void onComplete(int result) { - mCallbackResult.add(result); - } - - private int getResult() { - assertThat(mCallbackResult.size()).isEqualTo(1); - return mCallbackResult.get(0); - } - } -}