summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java182
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java29
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java20
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java18
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessage.java12
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java57
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java98
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java37
13 files changed, 449 insertions, 74 deletions
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 5fddae53d632..d0cc8e71dbfa 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -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 */
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 38eb7fb7fe0b..e7f302c1977a 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -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();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 9dc00792f80b..4110de6fee90 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -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;
@@ -176,6 +178,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
@Override
+ protected List<Integer> getDeviceFeatures() {
+ return Lists.newArrayList(Constants.DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX);
+ }
+
+ @Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 960510693f6a..62b7d8f95f5a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -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.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index c2e80caadd3b..8cf6c9757fe8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -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;
@@ -1479,6 +1481,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
@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);
if (targetDevice == null) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index 7a6ce8de8c24..c85fd50c62fe 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -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:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 653323de8d60..1a481b632606 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -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);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index fe97f701e089..cdd92166964d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -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.
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index e54f2c9e6b2e..2f49fb79c3dc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -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;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index f17173f61b69..6882ec1869ea 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -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);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 553df3bafd00..c1532f21adbf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -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)));
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
index 74a00521d7f4..c212bf868c76 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -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";
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 68b46c408028..dea3896f1b77 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -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 {