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