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