Add new HDMI CEC messages

Add Give/Report Features messages, validators and builders. Add required
constants for arguments.

Add Request/Report Current Latency messages and basic validators.

Bug: 166227212
Test: atest com.android.server.hdmi

Change-Id: I6c0ece20206d8a9131624e5d46519c4ff83ed525
This commit is contained in:
Marvin Ramin
2020-09-16 17:02:04 +02:00
parent ad263c5848
commit b27e202b90
13 changed files with 449 additions and 74 deletions

View File

@@ -88,79 +88,84 @@ final class Constants {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
MESSAGE_FEATURE_ABORT,
MESSAGE_IMAGE_VIEW_ON,
MESSAGE_TUNER_STEP_INCREMENT,
MESSAGE_TUNER_STEP_DECREMENT,
MESSAGE_TUNER_DEVICE_STATUS,
MESSAGE_GIVE_TUNER_DEVICE_STATUS,
MESSAGE_RECORD_ON,
MESSAGE_RECORD_STATUS,
MESSAGE_RECORD_OFF,
MESSAGE_TEXT_VIEW_ON,
MESSAGE_RECORD_TV_SCREEN,
MESSAGE_GIVE_DECK_STATUS,
MESSAGE_DECK_STATUS,
MESSAGE_SET_MENU_LANGUAGE,
MESSAGE_CLEAR_ANALOG_TIMER,
MESSAGE_SET_ANALOG_TIMER,
MESSAGE_TIMER_STATUS,
MESSAGE_STANDBY,
MESSAGE_PLAY,
MESSAGE_DECK_CONTROL,
MESSAGE_TIMER_CLEARED_STATUS,
MESSAGE_USER_CONTROL_PRESSED,
MESSAGE_USER_CONTROL_RELEASED,
MESSAGE_GIVE_OSD_NAME,
MESSAGE_SET_OSD_NAME,
MESSAGE_SET_OSD_STRING,
MESSAGE_SET_TIMER_PROGRAM_TITLE,
MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
MESSAGE_GIVE_AUDIO_STATUS,
MESSAGE_SET_SYSTEM_AUDIO_MODE,
MESSAGE_REPORT_AUDIO_STATUS,
MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS,
MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
MESSAGE_ROUTING_CHANGE,
MESSAGE_ROUTING_INFORMATION,
MESSAGE_ACTIVE_SOURCE,
MESSAGE_GIVE_PHYSICAL_ADDRESS,
MESSAGE_REPORT_PHYSICAL_ADDRESS,
MESSAGE_REQUEST_ACTIVE_SOURCE,
MESSAGE_SET_STREAM_PATH,
MESSAGE_DEVICE_VENDOR_ID,
MESSAGE_VENDOR_COMMAND,
MESSAGE_VENDOR_REMOTE_BUTTON_DOWN,
MESSAGE_VENDOR_REMOTE_BUTTON_UP,
MESSAGE_GIVE_DEVICE_VENDOR_ID,
MESSAGE_MENU_REQUEST,
MESSAGE_MENU_STATUS,
MESSAGE_GIVE_DEVICE_POWER_STATUS,
MESSAGE_REPORT_POWER_STATUS,
MESSAGE_GET_MENU_LANGUAGE,
MESSAGE_SELECT_ANALOG_SERVICE,
MESSAGE_SELECT_DIGITAL_SERVICE,
MESSAGE_SET_DIGITAL_TIMER,
MESSAGE_CLEAR_DIGITAL_TIMER,
MESSAGE_SET_AUDIO_RATE,
MESSAGE_INACTIVE_SOURCE,
MESSAGE_CEC_VERSION,
MESSAGE_GET_CEC_VERSION,
MESSAGE_VENDOR_COMMAND_WITH_ID,
MESSAGE_CLEAR_EXTERNAL_TIMER,
MESSAGE_SET_EXTERNAL_TIMER,
MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR,
MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
MESSAGE_INITIATE_ARC,
MESSAGE_REPORT_ARC_INITIATED,
MESSAGE_REPORT_ARC_TERMINATED,
MESSAGE_REQUEST_ARC_INITIATION,
MESSAGE_REQUEST_ARC_TERMINATION,
MESSAGE_TERMINATE_ARC,
MESSAGE_CDC_MESSAGE,
MESSAGE_ABORT,
MESSAGE_FEATURE_ABORT,
MESSAGE_IMAGE_VIEW_ON,
MESSAGE_TUNER_STEP_INCREMENT,
MESSAGE_TUNER_STEP_DECREMENT,
MESSAGE_TUNER_DEVICE_STATUS,
MESSAGE_GIVE_TUNER_DEVICE_STATUS,
MESSAGE_RECORD_ON,
MESSAGE_RECORD_STATUS,
MESSAGE_RECORD_OFF,
MESSAGE_TEXT_VIEW_ON,
MESSAGE_RECORD_TV_SCREEN,
MESSAGE_GIVE_DECK_STATUS,
MESSAGE_DECK_STATUS,
MESSAGE_SET_MENU_LANGUAGE,
MESSAGE_CLEAR_ANALOG_TIMER,
MESSAGE_SET_ANALOG_TIMER,
MESSAGE_TIMER_STATUS,
MESSAGE_STANDBY,
MESSAGE_PLAY,
MESSAGE_DECK_CONTROL,
MESSAGE_TIMER_CLEARED_STATUS,
MESSAGE_USER_CONTROL_PRESSED,
MESSAGE_USER_CONTROL_RELEASED,
MESSAGE_GIVE_OSD_NAME,
MESSAGE_SET_OSD_NAME,
MESSAGE_SET_OSD_STRING,
MESSAGE_SET_TIMER_PROGRAM_TITLE,
MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
MESSAGE_GIVE_AUDIO_STATUS,
MESSAGE_SET_SYSTEM_AUDIO_MODE,
MESSAGE_REPORT_AUDIO_STATUS,
MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS,
MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
MESSAGE_ROUTING_CHANGE,
MESSAGE_ROUTING_INFORMATION,
MESSAGE_ACTIVE_SOURCE,
MESSAGE_GIVE_PHYSICAL_ADDRESS,
MESSAGE_REPORT_PHYSICAL_ADDRESS,
MESSAGE_REQUEST_ACTIVE_SOURCE,
MESSAGE_SET_STREAM_PATH,
MESSAGE_DEVICE_VENDOR_ID,
MESSAGE_VENDOR_COMMAND,
MESSAGE_VENDOR_REMOTE_BUTTON_DOWN,
MESSAGE_VENDOR_REMOTE_BUTTON_UP,
MESSAGE_GIVE_DEVICE_VENDOR_ID,
MESSAGE_MENU_REQUEST,
MESSAGE_MENU_STATUS,
MESSAGE_GIVE_DEVICE_POWER_STATUS,
MESSAGE_REPORT_POWER_STATUS,
MESSAGE_GET_MENU_LANGUAGE,
MESSAGE_SELECT_ANALOG_SERVICE,
MESSAGE_SELECT_DIGITAL_SERVICE,
MESSAGE_SET_DIGITAL_TIMER,
MESSAGE_CLEAR_DIGITAL_TIMER,
MESSAGE_SET_AUDIO_RATE,
MESSAGE_INACTIVE_SOURCE,
MESSAGE_CEC_VERSION,
MESSAGE_GET_CEC_VERSION,
MESSAGE_VENDOR_COMMAND_WITH_ID,
MESSAGE_CLEAR_EXTERNAL_TIMER,
MESSAGE_SET_EXTERNAL_TIMER,
MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR,
MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
MESSAGE_GIVE_FEATURES,
MESSAGE_REPORT_FEATURES,
MESSAGE_REQUEST_CURRENT_LATENCY,
MESSAGE_REPORT_CURRENT_LATENCY,
MESSAGE_INITIATE_ARC,
MESSAGE_REPORT_ARC_INITIATED,
MESSAGE_REPORT_ARC_TERMINATED,
MESSAGE_REQUEST_ARC_INITIATION,
MESSAGE_REQUEST_ARC_TERMINATION,
MESSAGE_TERMINATE_ARC,
MESSAGE_CDC_MESSAGE,
MESSAGE_ABORT,
})
public @interface FeatureOpcode {}
public @interface FeatureOpcode {
}
static final int MESSAGE_FEATURE_ABORT = 0x00;
static final int MESSAGE_IMAGE_VIEW_ON = 0x04;
@@ -225,6 +230,10 @@ final class Constants {
static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2;
static final int MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR = 0xA3;
static final int MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR = 0xA4;
static final int MESSAGE_GIVE_FEATURES = 0xA5;
static final int MESSAGE_REPORT_FEATURES = 0xA6;
static final int MESSAGE_REQUEST_CURRENT_LATENCY = 0xA7;
static final int MESSAGE_REPORT_CURRENT_LATENCY = 0xA8;
static final int MESSAGE_INITIATE_ARC = 0xC0;
static final int MESSAGE_REPORT_ARC_INITIATED = 0xC1;
static final int MESSAGE_REPORT_ARC_TERMINATED = 0xC2;
@@ -501,6 +510,35 @@ final class Constants {
static final int VERSION_1_4 = 0x05;
static final int VERSION_2_0 = 0x06;
static final int ALL_DEVICE_TYPES_TV = 7;
static final int ALL_DEVICE_TYPES_RECORDER = 6;
static final int ALL_DEVICE_TYPES_TUNER = 5;
static final int ALL_DEVICE_TYPES_PLAYBACK = 4;
static final int ALL_DEVICE_TYPES_AUDIO_SYSTEM = 3;
static final int ALL_DEVICE_TYPES_SWITCH = 2;
static final int DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN = 6;
static final int DEVICE_FEATURE_TV_SUPPORTS_SET_OSD_STRING = 5;
static final int DEVICE_FEATURE_SUPPORTS_DECK_CONTROL = 4;
static final int DEVICE_FEATURE_SUPPORTS_SET_AUDIO_RATE = 3;
static final int DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX = 2;
static final int DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX = 1;
static final int RC_PROFILE_TV = 0;
static final int RC_PROFILE_SOURCE = 1;
static final int RC_PROFILE_TV_NONE = 0x0;
static final int RC_PROFILE_TV_ONE = 0x2;
static final int RC_PROFILE_TV_TWO = 0x6;
static final int RC_PROFILE_TV_THREE = 0xA;
static final int RC_PROFILE_TV_FOUR = 0xE;
static final int RC_PROFILE_SOURCE_HANDLES_ROOT_MENU = 4;
static final int RC_PROFILE_SOURCE_HANDLES_SETUP_MENU = 3;
static final int RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU = 2;
static final int RC_PROFILE_SOURCE_HANDLES_TOP_MENU = 1;
static final int RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU = 0;
private Constants() {
/* cannot be instantiated */
}

View File

@@ -351,6 +351,8 @@ abstract class HdmiCecLocalDevice {
return handleRequestShortAudioDescriptor(message);
case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR:
return handleReportShortAudioDescriptor(message);
case Constants.MESSAGE_GIVE_FEATURES:
return handleGiveFeatures(message);
default:
return false;
}
@@ -551,6 +553,33 @@ abstract class HdmiCecLocalDevice {
return false;
}
protected abstract int getRcProfile();
protected abstract List<Integer> getRcFeatures();
protected abstract List<Integer> getDeviceFeatures();
protected boolean handleGiveFeatures(HdmiCecMessage message) {
if (mService.getCecVersion() < Constants.VERSION_2_0) {
return false;
}
List<Integer> localDeviceTypes = new ArrayList<>();
for (HdmiCecLocalDevice localDevice : mService.getAllLocalDevices()) {
localDeviceTypes.add(localDevice.mDeviceType);
}
int rcProfile = getRcProfile();
List<Integer> rcFeatures = getRcFeatures();
List<Integer> deviceFeatures = getDeviceFeatures();
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportFeatures(mAddress, mService.getCecVersion(),
localDeviceTypes, rcProfile, rcFeatures, deviceFeatures));
return true;
}
@ServiceThreadOnly
protected boolean handleStandby(HdmiCecMessage message) {
assertRunOnServiceThread();

View File

@@ -47,6 +47,8 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiUtils.CodecSad;
import com.android.server.hdmi.HdmiUtils.DeviceConfig;
import com.google.android.collect.Lists;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -175,6 +177,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return info != null;
}
@Override
protected List<Integer> getDeviceFeatures() {
return Lists.newArrayList(Constants.DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX);
}
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {

View File

@@ -28,6 +28,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.Constants.LocalActivePort;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.google.android.collect.Lists;
import java.util.List;
/**
@@ -233,6 +235,24 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
// do nothing
}
@Override
protected int getRcProfile() {
return Constants.RC_PROFILE_SOURCE;
}
@Override
protected List<Integer> getRcFeatures() {
return Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU);
}
@Override
protected List<Integer> getDeviceFeatures() {
return Lists.newArrayList();
}
// Active source claiming needs to be handled in Service
// since service can decide who will be the active source when the device supports
// multiple device types in this method.

View File

@@ -51,6 +51,8 @@ import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import com.google.android.collect.Lists;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -1478,6 +1480,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return true;
}
@Override
protected int getRcProfile() {
return Constants.RC_PROFILE_TV;
}
@Override
protected List<Integer> getRcFeatures() {
return Lists.newArrayList(Constants.RC_PROFILE_TV_NONE);
}
@Override
protected List<Integer> getDeviceFeatures() {
return Lists.newArrayList(Constants.DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX,
Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN);
}
@Override
protected void sendStandby(int deviceId) {
HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(deviceId);

View File

@@ -18,6 +18,8 @@ package com.android.server.hdmi;
import android.annotation.Nullable;
import com.android.server.hdmi.Constants.FeatureOpcode;
import libcore.util.EmptyArray;
import java.util.Arrays;
@@ -126,7 +128,7 @@ public final class HdmiCecMessage {
return s.toString();
}
private static String opcodeToString(int opcode) {
private static String opcodeToString(@FeatureOpcode int opcode) {
switch (opcode) {
case Constants.MESSAGE_FEATURE_ABORT:
return "Feature Abort";
@@ -264,6 +266,14 @@ public final class HdmiCecMessage {
return "Request ARC Initiation";
case Constants.MESSAGE_REQUEST_ARC_TERMINATION:
return "Request ARC Termination";
case Constants.MESSAGE_GIVE_FEATURES:
return "Give Features";
case Constants.MESSAGE_REPORT_FEATURES:
return "Report Features";
case Constants.MESSAGE_REQUEST_CURRENT_LATENCY:
return "Request Current Latency";
case Constants.MESSAGE_REPORT_CURRENT_LATENCY:
return "Report Current Latency";
case Constants.MESSAGE_TERMINATE_ARC:
return "Terminate ARC";
case Constants.MESSAGE_CDC_MESSAGE:

View File

@@ -16,10 +16,14 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
import com.android.server.hdmi.Constants.AudioCodec;
import com.android.server.hdmi.Constants.CecVersion;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
/**
* A helper class to build {@link HdmiCecMessage} from various cec commands.
@@ -688,6 +692,40 @@ public class HdmiCecMessageBuilder {
return buildCommand(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
}
static HdmiCecMessage buildGiveFeatures(int src, int dest) {
return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES);
}
static HdmiCecMessage buildReportFeatures(int src, @CecVersion int cecVersion,
List<Integer> allDeviceTypes, int rcProfile, List<Integer> rcFeatures,
List<Integer> deviceFeatures) {
byte cecVersionByte = (byte) (cecVersion & 0xFF);
byte deviceTypes = 0;
for (Integer deviceType : allDeviceTypes) {
deviceTypes |= 1 << hdmiDeviceInfoDeviceTypeToShiftValue(deviceType);
}
byte rcProfileByte = 0;
rcProfileByte |= rcProfile << 6;
if (rcProfile == Constants.RC_PROFILE_SOURCE) {
for (Integer rcFeature : rcFeatures) {
rcProfileByte |= 1 << rcFeature;
}
} else {
byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF);
rcProfileByte |= rcProfileTv;
}
byte deviceFeaturesByte = 0;
for (Integer deviceFeature : deviceFeatures) {
deviceFeaturesByte |= 1 << deviceFeature;
}
byte[] params = {cecVersionByte, deviceTypes, rcProfileByte, deviceFeaturesByte};
return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REPORT_FEATURES,
params);
}
/***** Please ADD new buildXXX() methods above. ******/
/**
@@ -738,4 +776,23 @@ public class HdmiCecMessageBuilder {
(byte) (physicalAddress & 0xFF)
};
}
private static int hdmiDeviceInfoDeviceTypeToShiftValue(int deviceType) {
switch (deviceType) {
case HdmiDeviceInfo.DEVICE_TV:
return Constants.ALL_DEVICE_TYPES_TV;
case HdmiDeviceInfo.DEVICE_RECORDER:
return Constants.ALL_DEVICE_TYPES_RECORDER;
case HdmiDeviceInfo.DEVICE_TUNER:
return Constants.ALL_DEVICE_TYPES_TUNER;
case HdmiDeviceInfo.DEVICE_PLAYBACK:
return Constants.ALL_DEVICE_TYPES_PLAYBACK;
case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
return Constants.ALL_DEVICE_TYPES_AUDIO_SYSTEM;
case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
return Constants.ALL_DEVICE_TYPES_SWITCH;
default:
throw new IllegalArgumentException("Unhandled device type: " + deviceType);
}
}
}

View File

@@ -211,6 +211,17 @@ public class HdmiCecMessageValidator {
new OneByteRangeValidator(0x00, 0x06),
DEST_DIRECT);
// Messages for Feature Discovery.
addValidationInfo(Constants.MESSAGE_GIVE_FEATURES, noneValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_REPORT_FEATURES, new VariableLengthValidator(4, 14),
DEST_BROADCAST);
// Messages for Dynamic Auto Lipsync
addValidationInfo(Constants.MESSAGE_REQUEST_CURRENT_LATENCY, physicalAddressValidator,
DEST_BROADCAST);
addValidationInfo(Constants.MESSAGE_REPORT_CURRENT_LATENCY,
new VariableLengthValidator(4, 14), DEST_BROADCAST);
// All Messages for the ARC have no parameters.
// Messages for the Capability Discovery and Control.

View File

@@ -42,6 +42,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@SmallTest
@Presubmit
@@ -80,6 +82,21 @@ public class HdmiCecLocalDeviceTest {
@Override
protected void setPreferredAddress(int addr) {}
@Override
protected int getRcProfile() {
return 0;
}
@Override
protected List<Integer> getRcFeatures() {
return Collections.emptyList();
}
@Override
protected List<Integer> getDeviceFeatures() {
return Collections.emptyList();
}
}
private MyHdmiCecLocalDevice mHdmiLocalDevice;

View File

@@ -26,10 +26,14 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.google.android.collect.Lists;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Collections;
@SmallTest
@Presubmit
@RunWith(JUnit4.class)
@@ -88,6 +92,98 @@ public class HdmiCecMessageBuilderTest {
buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E"));
}
@Test
public void buildGiveFeatures() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildGiveFeatures(ADDR_PLAYBACK_1, ADDR_TV);
assertThat(message).isEqualTo(buildMessage("40:A5"));
}
@Test
public void buildReportFeatures_basicTv_1_4() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
Constants.VERSION_1_4,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
assertThat(message).isEqualTo(buildMessage("0F:A6:05:80:00:00"));
}
@Test
public void buildReportFeatures_basicPlayback_1_4() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
Constants.VERSION_1_4,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
assertThat(message).isEqualTo(buildMessage("4F:A6:05:10:00:00"));
}
@Test
public void buildReportFeatures_basicPlaybackAudioSystem_1_4() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
Constants.VERSION_1_4,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
assertThat(message).isEqualTo(buildMessage("4F:A6:05:18:00:00"));
}
@Test
public void buildReportFeatures_basicTv_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
Constants.VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:00"));
}
@Test
public void buildReportFeatures_remoteControlTv_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
Constants.VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList());
assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:02:00"));
}
@Test
public void buildReportFeatures_remoteControlPlayback_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
Constants.VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList());
assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:00"));
}
@Test
public void buildReportFeatures_deviceFeaturesTv_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
Constants.VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN));
assertThat(message).isEqualTo(buildMessage("0F:A6:06:80:00:40"));
}
@Test
public void buildReportFeatures_deviceFeaturesPlayback_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
Constants.VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
Lists.newArrayList(Constants.DEVICE_FEATURE_SUPPORTS_DECK_CONTROL));
assertThat(message).isEqualTo(buildMessage("0F:A6:06:10:4A:10"));
}
/**
* Build a CEC message from a hex byte string with bytes separated by {@code :}.
*
@@ -100,7 +196,7 @@ public class HdmiCecMessageBuilderTest {
int opcode = Integer.parseInt(parts[1], 16);
byte[] params = new byte[parts.length - 2];
for (int i = 0; i < params.length; i++) {
params[i] = Byte.parseByte(parts[i + 2], 16);
params[i] = (byte) Integer.parseInt(parts[i + 2], 16);
}
return new HdmiCecMessage(src, dest, opcode, params);
}

View File

@@ -353,6 +353,24 @@ public class HdmiCecMessageValidatorTest {
assertMessageValidity("40:35:EE:52:4A").isEqualTo(ERROR_PARAMETER);
}
@Test
public void isValid_giveFeatures() {
assertMessageValidity("40:A5").isEqualTo(OK);
assertMessageValidity("4F:A5").isEqualTo(ERROR_DESTINATION);
assertMessageValidity("F0:A5").isEqualTo(ERROR_SOURCE);
}
@Test
public void isValid_reportFeatures() {
assertMessageValidity("0F:A6:05:80:00:00").isEqualTo(OK);
assertMessageValidity("04:A6:05:80:00:00").isEqualTo(ERROR_DESTINATION);
assertMessageValidity("FF:A6:05:80:00:00").isEqualTo(ERROR_SOURCE);
assertMessageValidity("0F:A6").isEqualTo(ERROR_PARAMETER_SHORT);
}
private IntegerSubject assertMessageValidity(String message) {
return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
}

View File

@@ -38,6 +38,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Tests for {@link HdmiControlServiceBinderAPITest} class.
@@ -99,6 +101,21 @@ public class HdmiControlServiceBinderAPITest {
protected void setCanGoToStandby(boolean canGoToStandby) {
mCanGoToStandby = canGoToStandby;
}
@Override
protected int getRcProfile() {
return 0;
}
@Override
protected List<Integer> getRcFeatures() {
return Collections.emptyList();
}
@Override
protected List<Integer> getDeviceFeatures() {
return Collections.emptyList();
}
}
private static final String TAG = "HdmiControlServiceBinderAPITest";

View File

@@ -54,6 +54,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Tests for {@link HdmiControlService} class.
@@ -421,6 +422,42 @@ public class HdmiControlServiceTest {
assertThat(mHdmiControlService.getCecVersion()).isEqualTo(Constants.VERSION_2_0);
}
@Test
public void handleGiveFeatures_cec14_featureAbort() {
Settings.Global.putInt(mContextSpy.getContentResolver(), Settings.Global.HDMI_CEC_VERSION,
Constants.VERSION_1_4);
mHdmiControlService.setControlEnabled(true);
mTestLooper.dispatchAll();
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveFeatures(Constants.ADDR_TV,
Constants.ADDR_PLAYBACK_1));
mTestLooper.dispatchAll();
HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
Constants.ADDR_PLAYBACK_1, Constants.ADDR_TV, Constants.MESSAGE_GIVE_FEATURES,
Constants.ABORT_UNRECOGNIZED_OPCODE);
assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort);
}
@Test
public void handleGiveFeatures_cec20_reportsFeatures() {
Settings.Global.putInt(mContextSpy.getContentResolver(), Settings.Global.HDMI_CEC_VERSION,
Constants.VERSION_2_0);
mHdmiControlService.setControlEnabled(true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildGiveFeatures(Constants.ADDR_TV,
Constants.ADDR_PLAYBACK_1));
mTestLooper.dispatchAll();
HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
Constants.ADDR_PLAYBACK_1, Constants.VERSION_2_0,
Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(),
mMyPlaybackDevice.getDeviceFeatures());
assertThat(mNativeWrapper.getResultMessages()).contains(reportFeatures);
}
private static class VolumeControlFeatureCallback extends
IHdmiCecVolumeControlFeatureListener.Stub {