Merge "Rework actions for detecting support for <Set Audio Volume Level>." into tm-dev

This commit is contained in:
Yan Han
2022-03-24 09:46:32 +00:00
committed by Android (Google) Code Review
4 changed files with 42 additions and 284 deletions

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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);
}
}
}