diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index ea2453216a598..c24973d4f2d4f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -156,7 +156,6 @@ public class HdmiCecMessageValidator { addValidationInfo(Constants.MESSAGE_GIVE_DECK_STATUS, statusRequestValidator, DEST_DIRECT); addValidationInfo(Constants.MESSAGE_PLAY, new PlayModeValidator(), DEST_DIRECT); - // TODO: Handle messages for the Tuner Control. addValidationInfo( Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS, statusRequestValidator, DEST_DIRECT); addValidationInfo( @@ -167,6 +166,10 @@ public class HdmiCecMessageValidator { Constants.MESSAGE_SELECT_DIGITAL_SERVICE, new SelectDigitalServiceValidator(), DEST_DIRECT); + addValidationInfo( + Constants.MESSAGE_TUNER_DEVICE_STATUS, + new TunerDeviceStatusValidator(), + DEST_DIRECT); // Messages for the Vendor Specific Commands. VariableLengthValidator maxLengthValidator = new VariableLengthValidator(0, 14); @@ -306,7 +309,14 @@ public class HdmiCecMessageValidator { } private boolean isValidPhysicalAddress(byte[] params, int offset) { - // TODO: Add more logic like validating 1.0.1.0. + int physicalAddress = HdmiUtils.twoBytesToInt(params, offset); + while (physicalAddress != 0) { + int maskedAddress = physicalAddress & 0xF000; + physicalAddress = (physicalAddress << 4) & 0xFFFF; + if (maskedAddress == 0 && physicalAddress != 0) { + return false; + } + } if (!mService.isTvDevice()) { // If the device is not TV, we can't convert path to port-id, so stop here. @@ -722,6 +732,35 @@ public class HdmiCecMessageValidator { || (isWithinRange(value, 0xC1, 0xC3))); } + /* + * Check if the given value is a valid Tuner Device info. A valid value is one which falls + * within the range description defined in CEC 1.4 Specification : Operand Descriptions + * (Section 17) + * + * @param params Tuner device info + * @return true if the Tuner device info is valid + */ + private boolean isValidTunerDeviceInfo(byte[] params) { + int tunerDisplayInfo = params[0] & 0x7F; + if (tunerDisplayInfo == 0x00) { + // Displaying digital tuner + if (params.length >= 5) { + return isValidDigitalServiceIdentification(params, 1); + } + } else if (tunerDisplayInfo == 0x01) { + // Not displaying Tuner + return true; + } else if (tunerDisplayInfo == 0x02) { + // Displaying Analogue tuner + if (params.length >= 5) { + return (isValidAnalogueBroadcastType(params[1]) + && isValidAnalogueFrequency(HdmiUtils.twoBytesToInt(params, 2)) + && isValidBroadcastSystem(params[4])); + } + } + return false; + } + private class PhysicalAddressValidator implements ParameterValidator { @Override public int isValid(byte[] params) { @@ -1000,6 +1039,21 @@ public class HdmiCecMessageValidator { } } + /** + * Check if the given tuner device status parameter is valid. A valid parameter should lie + * within the range description defined in CEC 1.4 Specification : Operand Descriptions (Section + * 17) + */ + private class TunerDeviceStatusValidator implements ParameterValidator { + @Override + public int isValid(byte[] params) { + if (params.length < 1) { + return ERROR_PARAMETER_SHORT; + } + return toErrorCode(isValidTunerDeviceInfo(params)); + } + } + /** Check if the given user control press parameter is valid. */ private class UserControlPressedValidator implements ParameterValidator { @Override 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 24a0100396667..ae7f422817e54 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -120,6 +120,16 @@ public class HdmiCecMessageValidatorTest { assertMessageValidity("40:8E:02").isEqualTo(ERROR_PARAMETER); } + @Test + public void isValid_systemAudioModeRequest() { + assertMessageValidity("40:70:00:00").isEqualTo(OK); + assertMessageValidity("40:70").isEqualTo(OK); + + assertMessageValidity("F0:70").isEqualTo(ERROR_SOURCE); + // Invalid physical address + assertMessageValidity("40:70:10:10").isEqualTo(ERROR_PARAMETER); + } + @Test public void isValid_setSystemAudioMode() { assertMessageValidity("40:72:00").isEqualTo(OK); @@ -340,6 +350,8 @@ public class HdmiCecMessageValidatorTest { assertMessageValidity("40:A2:14:09:12:28:4B:19:10:08:10:00").isEqualTo(ERROR_PARAMETER); // Invalid External PLug assertMessageValidity("04:A1:0C:08:15:05:04:1E:02:04:00").isEqualTo(ERROR_PARAMETER); + // Invalid Physical Address + assertMessageValidity("40:A2:14:09:12:28:4B:19:10:05:10:10").isEqualTo(ERROR_PARAMETER); } @Test @@ -475,6 +487,51 @@ public class HdmiCecMessageValidatorTest { assertMessageValidity("40:93:91:09:F4:40").isEqualTo(ERROR_PARAMETER); } + @Test + public void isValid_tunerDeviceStatus() { + // Displaying digital tuner + assertMessageValidity("40:07:00:00:11:CE:90:0F:00:78").isEqualTo(OK); + assertMessageValidity("40:07:80:10:13:0B:34:38").isEqualTo(OK); + assertMessageValidity("40:07:00:9A:06:F9:D3:E6").isEqualTo(OK); + assertMessageValidity("40:07:00:91:09:F4:40:C8").isEqualTo(OK); + // Not displaying tuner + assertMessageValidity("40:07:01").isEqualTo(OK); + assertMessageValidity("40:07:81:07:64:B9:02").isEqualTo(OK); + // Displaying analogue tuner + assertMessageValidity("40:07:02:00:13:0F:00:96").isEqualTo(OK); + assertMessageValidity("40:07:82:02:EA:60:1F").isEqualTo(OK); + + assertMessageValidity("4F:07:00:00:11:CE:90:0F:00:78").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:07:82:02:EA:60:1F").isEqualTo(ERROR_SOURCE); + assertMessageValidity("40:07").isEqualTo(ERROR_PARAMETER_SHORT); + + // Invalid display info + assertMessageValidity("40:07:09:A1:8C:17:51").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:07:A7:0C:29").isEqualTo(ERROR_PARAMETER); + // Invalid Digital Broadcast System + assertMessageValidity("40:07:00:14:11:CE:90:0F:00:78").isEqualTo(ERROR_PARAMETER); + // Invalid Digital Broadcast System + assertMessageValidity("40:07:80:A0:07:95:F1").isEqualTo(ERROR_PARAMETER); + // Insufficient data for ARIB Broadcast system + assertMessageValidity("40:07:00:00:11:CE:90:0F:00").isEqualTo(ERROR_PARAMETER); + // Insufficient data for ATSC Broadcast system + assertMessageValidity("40:07:80:10:13:0B:34").isEqualTo(ERROR_PARAMETER); + // Insufficient data for DVB Broadcast system + assertMessageValidity("40:07:00:18:BE:77:00:7D:01").isEqualTo(ERROR_PARAMETER); + // Invalid channel number format + assertMessageValidity("40:07:80:9A:10:F9:D3").isEqualTo(ERROR_PARAMETER); + // Insufficient data for 1 part channel number + assertMessageValidity("40:07:00:90:04:F7").isEqualTo(ERROR_PARAMETER); + // Insufficient data for 2 part channel number + assertMessageValidity("40:07:80:91:09:F4:40").isEqualTo(ERROR_PARAMETER); + // Invalid Analogue Broadcast type + assertMessageValidity("40:07:02:03:EA:60:1F").isEqualTo(ERROR_PARAMETER); + // Invalid Analogue Frequency + assertMessageValidity("40:07:82:00:FF:FF:00").isEqualTo(ERROR_PARAMETER); + // Invalid Broadcast system + assertMessageValidity("40:07:02:02:EA:60:20").isEqualTo(ERROR_PARAMETER); + } + @Test public void isValid_UserControlPressed() { assertMessageValidity("40:44:07").isEqualTo(OK); @@ -518,6 +575,59 @@ public class HdmiCecMessageValidatorTest { assertMessageValidity("40:44:57:40").isEqualTo(ERROR_PARAMETER); } + @Test + public void isValid_physicalAddress() { + assertMessageValidity("4F:82:10:00").isEqualTo(OK); + assertMessageValidity("4F:82:12:34").isEqualTo(OK); + assertMessageValidity("0F:82:00:00").isEqualTo(OK); + assertMessageValidity("40:9D:14:00").isEqualTo(OK); + assertMessageValidity("40:9D:10:00").isEqualTo(OK); + assertMessageValidity("0F:81:44:20").isEqualTo(OK); + assertMessageValidity("4F:81:13:10").isEqualTo(OK); + assertMessageValidity("4F:86:14:14").isEqualTo(OK); + assertMessageValidity("0F:86:15:24").isEqualTo(OK); + + assertMessageValidity("4F:82:10").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:9D:14").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("0F:81:44").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("0F:86:15").isEqualTo(ERROR_PARAMETER_SHORT); + + assertMessageValidity("4F:82:10:10").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("4F:82:10:06").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:9D:14:04").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:9D:10:01").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("0F:81:44:02").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("4F:81:13:05").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("4F:86:10:14").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("0F:86:10:24").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_reportPhysicalAddress() { + assertMessageValidity("4F:84:10:00:04").isEqualTo(OK); + assertMessageValidity("0F:84:00:00:00").isEqualTo(OK); + + assertMessageValidity("4F:84:10:00").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("0F:84:00").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:84:10:00:04").isEqualTo(ERROR_DESTINATION); + // Invalid Physical Address + assertMessageValidity("4F:84:10:10:04").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("0F:84:00:30:00").isEqualTo(ERROR_PARAMETER); + // Invalid Device Type + assertMessageValidity("4F:84:12:34:08").isEqualTo(ERROR_PARAMETER); + } + + @Test + public void isValid_routingChange() { + assertMessageValidity("0F:80:10:00:40:00").isEqualTo(OK); + assertMessageValidity("4F:80:12:00:50:00").isEqualTo(OK); + + assertMessageValidity("0F:80:10:00:40").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("40:80:12:00:50:00").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("0F:80:10:01:40:00").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("4F:80:12:00:50:50").isEqualTo(ERROR_PARAMETER); + } + private IntegerSubject assertMessageValidity(String message) { return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message))); }