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