Rework actions for detecting support for <Set Audio Volume Level>.
Removes GiveFeaturesAction, as it is unnecessary: HdmiCecNetwork already handles <Report Feature>, so the action's job can accomplished by just sending <Give Features> independently. <Feature Aborts> for <Set Audio Volume Level> messages are now handled in HdmiCecNetwork, because they indicate (lack of) support for the message regardless of whether a SetAudioVolumeLevelDiscovery action is active. Bug: 205817429 Test: atest SetAudioVolumeLevelDiscoveryActionTest Change-Id: I1383a980938ac181920f6d5d3f2ff9ad6c2e57ce
This commit is contained in:
@@ -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 <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);
|
||||
}
|
||||
}
|
||||
@@ -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 <Feature Abort> in response to <Set Audio Volume Level>.
|
||||
// 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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user