HdmiCecMessageValidator.java revision 184b124ec22a796327642e3546d366179e693f07
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.hdmi; 18 19import android.hardware.hdmi.HdmiDeviceInfo; 20import android.util.SparseArray; 21 22/** 23 * A helper class to validates {@link HdmiCecMessage}. 24 */ 25public final class HdmiCecMessageValidator { 26 private static final String TAG = "HdmiCecMessageValidator"; 27 28 private final HdmiControlService mService; 29 30 interface ParameterValidator { 31 boolean isValid(byte[] params); 32 } 33 34 // Only the direct addressing is allowed. 35 private static final int DEST_DIRECT = 1 << 0; 36 // Only the broadcast addressing is allowed. 37 private static final int DEST_BROADCAST = 1 << 1; 38 // Both the direct and the broadcast addressing are allowed. 39 private static final int DEST_ALL = DEST_DIRECT | DEST_BROADCAST; 40 // True if the messages from address 15 (unregistered) are allowed. 41 private static final int SRC_UNREGISTERED = 1 << 2; 42 43 private static class ValidationInfo { 44 public final ParameterValidator parameterValidator; 45 public final int addressType; 46 47 public ValidationInfo(ParameterValidator validator, int type) { 48 parameterValidator = validator; 49 addressType = type; 50 } 51 } 52 53 final SparseArray<ValidationInfo> mValidationInfo = new SparseArray<>(); 54 55 public HdmiCecMessageValidator(HdmiControlService service) { 56 mService = service; 57 58 // Messages related to the physical address. 59 PhysicalAddressValidator physicalAddressValidator = new PhysicalAddressValidator(); 60 addValidationInfo(Constants.MESSAGE_ACTIVE_SOURCE, 61 physicalAddressValidator, DEST_BROADCAST | SRC_UNREGISTERED); 62 addValidationInfo(Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressValidator, DEST_DIRECT); 63 addValidationInfo(Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, 64 new ReportPhysicalAddressValidator(), DEST_BROADCAST | SRC_UNREGISTERED); 65 addValidationInfo(Constants.MESSAGE_ROUTING_CHANGE, 66 new RoutingChangeValidator(), DEST_BROADCAST | SRC_UNREGISTERED); 67 addValidationInfo(Constants.MESSAGE_ROUTING_INFORMATION, 68 physicalAddressValidator, DEST_BROADCAST | SRC_UNREGISTERED); 69 addValidationInfo(Constants.MESSAGE_SET_STREAM_PATH, 70 physicalAddressValidator, DEST_BROADCAST); 71 addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST, 72 physicalAddressValidator, DEST_DIRECT); 73 74 // Messages have no parameter. 75 FixedLengthValidator noneValidator = new FixedLengthValidator(0); 76 addValidationInfo(Constants.MESSAGE_ABORT, noneValidator, DEST_DIRECT); 77 addValidationInfo(Constants.MESSAGE_GET_CEC_VERSION, noneValidator, DEST_DIRECT); 78 addValidationInfo(Constants.MESSAGE_GET_MENU_LANGUAGE, 79 noneValidator, DEST_DIRECT | SRC_UNREGISTERED); 80 addValidationInfo(Constants.MESSAGE_GIVE_AUDIO_STATUS, noneValidator, DEST_DIRECT); 81 addValidationInfo(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, noneValidator, DEST_DIRECT); 82 addValidationInfo(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID, 83 noneValidator, DEST_DIRECT | SRC_UNREGISTERED); 84 addValidationInfo(Constants.MESSAGE_GIVE_OSD_NAME, noneValidator, DEST_DIRECT); 85 addValidationInfo(Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS, 86 noneValidator, DEST_DIRECT | SRC_UNREGISTERED); 87 addValidationInfo(Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS, 88 noneValidator, DEST_DIRECT); 89 addValidationInfo(Constants.MESSAGE_IMAGE_VIEW_ON, noneValidator, DEST_DIRECT); 90 addValidationInfo(Constants.MESSAGE_INITIATE_ARC, noneValidator, DEST_DIRECT); 91 addValidationInfo(Constants.MESSAGE_RECORD_OFF, noneValidator, DEST_DIRECT); 92 addValidationInfo(Constants.MESSAGE_RECORD_TV_SCREEN, noneValidator, DEST_DIRECT); 93 addValidationInfo(Constants.MESSAGE_REPORT_ARC_INITIATED, noneValidator, DEST_DIRECT); 94 addValidationInfo(Constants.MESSAGE_REPORT_ARC_TERMINATED, noneValidator, DEST_DIRECT); 95 addValidationInfo(Constants.MESSAGE_REQUEST_ARC_INITIATION, noneValidator, DEST_DIRECT); 96 addValidationInfo(Constants.MESSAGE_REQUEST_ARC_TERMINATION, noneValidator, DEST_DIRECT); 97 addValidationInfo(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, 98 noneValidator, DEST_BROADCAST | SRC_UNREGISTERED); 99 addValidationInfo(Constants.MESSAGE_STANDBY, noneValidator, DEST_ALL | SRC_UNREGISTERED); 100 addValidationInfo(Constants.MESSAGE_TERMINATE_ARC, noneValidator, DEST_DIRECT); 101 addValidationInfo(Constants.MESSAGE_TEXT_VIEW_ON, noneValidator, DEST_DIRECT); 102 addValidationInfo(Constants.MESSAGE_TUNER_STEP_DECREMENT, noneValidator, DEST_DIRECT); 103 addValidationInfo(Constants.MESSAGE_TUNER_STEP_INCREMENT, noneValidator, DEST_DIRECT); 104 addValidationInfo(Constants.MESSAGE_USER_CONTROL_RELEASED, noneValidator, DEST_DIRECT); 105 addValidationInfo(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP, noneValidator, DEST_ALL); 106 107 // TODO: Validate more than length for the following messages. 108 109 // Messages for the One Touch Record. 110 FixedLengthValidator oneByteValidator = new FixedLengthValidator(1); 111 addValidationInfo(Constants.MESSAGE_RECORD_ON, 112 new VariableLengthValidator(1, 8), DEST_DIRECT); 113 addValidationInfo(Constants.MESSAGE_RECORD_STATUS, oneByteValidator, DEST_DIRECT); 114 115 // TODO: Handle messages for the Timer Programming. 116 117 // Messages for the System Information. 118 addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT); 119 addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE, 120 new FixedLengthValidator(3), DEST_BROADCAST); 121 122 // TODO: Handle messages for the Deck Control. 123 124 // TODO: Handle messages for the Tuner Control. 125 126 // Messages for the Vendor Specific Commands. 127 VariableLengthValidator maxLengthValidator = new VariableLengthValidator(0, 14); 128 addValidationInfo(Constants.MESSAGE_DEVICE_VENDOR_ID, 129 new FixedLengthValidator(3), DEST_BROADCAST); 130 // Allow unregistered source for all vendor specific commands, because we don't know 131 // how to use the commands at this moment. 132 addValidationInfo(Constants.MESSAGE_VENDOR_COMMAND, 133 maxLengthValidator, DEST_DIRECT | SRC_UNREGISTERED); 134 addValidationInfo(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, 135 maxLengthValidator, DEST_ALL | SRC_UNREGISTERED); 136 addValidationInfo(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN, 137 maxLengthValidator, DEST_ALL | SRC_UNREGISTERED); 138 139 // Messages for the OSD. 140 addValidationInfo(Constants.MESSAGE_SET_OSD_STRING, maxLengthValidator, DEST_DIRECT); 141 addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, maxLengthValidator, DEST_DIRECT); 142 143 // Messages for the Device Menu Control. 144 addValidationInfo(Constants.MESSAGE_MENU_REQUEST, oneByteValidator, DEST_DIRECT); 145 addValidationInfo(Constants.MESSAGE_MENU_STATUS, oneByteValidator, DEST_DIRECT); 146 147 // Messages for the Remote Control Passthrough. 148 // TODO: Parse the first parameter and determine if it can have the next parameter. 149 addValidationInfo(Constants.MESSAGE_USER_CONTROL_PRESSED, 150 new VariableLengthValidator(1, 2), DEST_DIRECT); 151 152 // Messages for the Power Status. 153 addValidationInfo(Constants.MESSAGE_REPORT_POWER_STATUS, oneByteValidator, DEST_DIRECT); 154 155 // Messages for the General Protocol. 156 addValidationInfo(Constants.MESSAGE_FEATURE_ABORT, 157 new FixedLengthValidator(2), DEST_DIRECT); 158 159 // Messages for the System Audio Control. 160 addValidationInfo(Constants.MESSAGE_REPORT_AUDIO_STATUS, oneByteValidator, DEST_DIRECT); 161 addValidationInfo(Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR, 162 new FixedLengthValidator(3), DEST_DIRECT); 163 addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, 164 oneByteValidator, DEST_DIRECT); 165 addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, oneByteValidator, DEST_ALL); 166 addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, 167 oneByteValidator, DEST_DIRECT); 168 169 // Messages for the Audio Rate Control. 170 addValidationInfo(Constants.MESSAGE_SET_AUDIO_RATE, oneByteValidator, DEST_DIRECT); 171 172 // All Messages for the ARC have no parameters. 173 174 // Messages for the Capability Discovery and Control. 175 addValidationInfo(Constants.MESSAGE_CDC_MESSAGE, maxLengthValidator, 176 DEST_BROADCAST | SRC_UNREGISTERED); 177 } 178 179 private void addValidationInfo(int opcode, ParameterValidator validator, int addrType) { 180 mValidationInfo.append(opcode, new ValidationInfo(validator, addrType)); 181 } 182 183 boolean isValid(HdmiCecMessage message) { 184 int opcode = message.getOpcode(); 185 ValidationInfo info = mValidationInfo.get(opcode); 186 if (info == null) { 187 HdmiLogger.warning("No validation information for the message: " + message); 188 return true; 189 } 190 191 // Check the source field. 192 if (message.getSource() == Constants.ADDR_UNREGISTERED && 193 (info.addressType & SRC_UNREGISTERED) == 0) { 194 HdmiLogger.warning("Unexpected source: " + message); 195 return false; 196 } 197 // Check the destination field. 198 if (message.getDestination() == Constants.ADDR_BROADCAST) { 199 if ((info.addressType & DEST_BROADCAST) == 0) { 200 HdmiLogger.warning("Unexpected broadcast message: " + message); 201 return false; 202 } 203 } else { // Direct addressing. 204 if ((info.addressType & DEST_DIRECT) == 0) { 205 HdmiLogger.warning("Unexpected direct message: " + message); 206 return false; 207 } 208 } 209 210 // Check the parameter type. 211 if (!info.parameterValidator.isValid(message.getParams())) { 212 HdmiLogger.warning("Unexpected parameters: " + message); 213 return false; 214 } 215 return true; 216 } 217 218 private static class FixedLengthValidator implements ParameterValidator { 219 private final int mLength; 220 221 public FixedLengthValidator(int length) { 222 mLength = length; 223 } 224 225 @Override 226 public boolean isValid(byte[] params) { 227 return params.length == mLength; 228 } 229 } 230 231 private static class VariableLengthValidator implements ParameterValidator { 232 private final int mMinLength; 233 private final int mMaxLength; 234 235 public VariableLengthValidator(int minLength, int maxLength) { 236 mMinLength = minLength; 237 mMaxLength = maxLength; 238 } 239 240 @Override 241 public boolean isValid(byte[] params) { 242 return params.length >= mMinLength && params.length <= mMaxLength; 243 } 244 } 245 246 private boolean isValidPhysicalAddress(byte[] params, int offset) { 247 // TODO: Add more logic like validating 1.0.1.0. 248 249 if (!mService.isTvDevice()) { 250 // If the device is not TV, we can't convert path to port-id, so stop here. 251 return true; 252 } 253 int path = HdmiUtils.twoBytesToInt(params, offset); 254 if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == mService.getPhysicalAddress()) { 255 return true; 256 } 257 int portId = mService.pathToPortId(path); 258 if (portId == Constants.INVALID_PORT_ID) { 259 return false; 260 } 261 return true; 262 } 263 264 /** 265 * Check if the given type is valid. A valid type is one of the actual logical device types 266 * defined in the standard ({@link HdmiDeviceInfo#DEVICE_TV}, 267 * {@link HdmiDeviceInfo#DEVICE_PLAYBACK}, {@link HdmiDeviceInfo#DEVICE_TUNER}, 268 * {@link HdmiDeviceInfo#DEVICE_RECORDER}, and 269 * {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}). 270 * 271 * @param type device type 272 * @return true if the given type is valid 273 */ 274 static boolean isValidType(int type) { 275 return (HdmiDeviceInfo.DEVICE_TV <= type 276 && type <= HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR) 277 && type != HdmiDeviceInfo.DEVICE_RESERVED; 278 } 279 280 private class PhysicalAddressValidator implements ParameterValidator { 281 @Override 282 public boolean isValid(byte[] params) { 283 if (params.length != 2) { 284 return false; 285 } 286 return isValidPhysicalAddress(params, 0); 287 } 288 } 289 290 private class ReportPhysicalAddressValidator implements ParameterValidator { 291 @Override 292 public boolean isValid(byte[] params) { 293 if (params.length != 3) { 294 return false; 295 } 296 return isValidPhysicalAddress(params, 0) && isValidType(params[2]); 297 } 298 } 299 300 private class RoutingChangeValidator implements ParameterValidator { 301 @Override 302 public boolean isValid(byte[] params) { 303 if (params.length != 4) { 304 return false; 305 } 306 return isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2); 307 } 308 } 309} 310