blob: d491ac295b065e41d24b93ccc10d388418bc01b3 [file] [log] [blame]
Yuncheol Heo75a77e72014-07-09 18:27:53 +09001/*
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
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +090035 // 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<>();
Yuncheol Heo75a77e72014-07-09 18:27:53 +090055
56 public HdmiCecMessageValidator(HdmiControlService service) {
57 mService = service;
58
59 // Messages related to the physical address.
60 PhysicalAddressValidator physicalAddressValidator = new PhysicalAddressValidator();
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +090061 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);
Yuncheol Heo75a77e72014-07-09 18:27:53 +090074
75 // Messages have no parameter.
76 FixedLengthValidator noneValidator = new FixedLengthValidator(0);
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +090077 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);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900107
108 // TODO: Validate more than length for the following messages.
109
110 // Messages for the One Touch Record.
111 FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900112 addValidationInfo(Constants.MESSAGE_RECORD_ON,
113 new VariableLengthValidator(1, 8), DEST_DIRECT);
114 addValidationInfo(Constants.MESSAGE_RECORD_STATUS, oneByteValidator, DEST_DIRECT);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900115
116 // TODO: Handle messages for the Timer Programming.
117
118 // Messages for the System Information.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900119 addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT);
120 addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
121 new FixedLengthValidator(3), DEST_BROADCAST);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900122
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);
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900129 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);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900139
140 // Messages for the OSD.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900141 addValidationInfo(Constants.MESSAGE_SET_OSD_STRING, maxLengthValidator, DEST_DIRECT);
142 addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, maxLengthValidator, DEST_DIRECT);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900143
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.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900148 addValidationInfo(Constants.MESSAGE_USER_CONTROL_PRESSED,
149 new VariableLengthValidator(1, 2), DEST_DIRECT);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900150
151 // Messages for the Power Status.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900152 addValidationInfo(Constants.MESSAGE_REPORT_POWER_STATUS, oneByteValidator, DEST_DIRECT);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900153
154 // Messages for the General Protocol.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900155 addValidationInfo(Constants.MESSAGE_FEATURE_ABORT,
156 new FixedLengthValidator(2), DEST_DIRECT);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900157
158 // Messages for the System Audio Control.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900159 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);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900167
168 // Messages for the Audio Rate Control.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900169 addValidationInfo(Constants.MESSAGE_SET_AUDIO_RATE, oneByteValidator, DEST_DIRECT);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900170
171 // All Messages for the ARC have no parameters.
172
173 // Messages for the Capability Discovery and Control.
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900174 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));
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900180 }
181
182 boolean isValid(HdmiCecMessage message) {
183 int opcode = message.getOpcode();
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900184 ValidationInfo info = mValidationInfo.get(opcode);
185 if (info == null) {
186 Slog.w(TAG, "No validation information for the message: " + message);
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900187 return true;
188 }
Yuncheol Heoe9b9b1e2014-07-10 18:52:28 +0900189
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;
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900215 }
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) {
Yuncheol Heoe946ed82014-07-25 14:05:19 +0900246 // TODO: Add more logic like validating 1.0.1.0.
247
248 if (!mService.isTvDevice()) {
249 // If the device is not TV, we can't convert path to port-id, so stop here.
250 return true;
251 }
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900252 int path = HdmiUtils.twoBytesToInt(params, offset);
253 int portId = mService.pathToPortId(path);
254 if (portId == Constants.INVALID_PORT_ID) {
255 return false;
256 }
Yuncheol Heo75a77e72014-07-09 18:27:53 +0900257 return true;
258 }
259
260 /**
261 * Check if the given type is valid. A valid type is one of the actual
262 * logical device types defined in the standard ({@link #DEVICE_TV},
263 * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
264 * and {@link #DEVICE_AUDIO_SYSTEM}).
265 *
266 * @param type device type
267 * @return true if the given type is valid
268 */
269 static boolean isValidType(int type) {
270 return (HdmiCecDeviceInfo.DEVICE_TV <= type
271 && type <= HdmiCecDeviceInfo.DEVICE_VIDEO_PROCESSOR)
272 && type != HdmiCecDeviceInfo.DEVICE_RESERVED;
273 }
274
275 private class PhysicalAddressValidator implements ParameterValidator {
276 @Override
277 public boolean isValid(byte[] params) {
278 if (params.length != 2) {
279 return false;
280 }
281 return isValidPhysicalAddress(params, 0);
282 }
283 }
284
285 private class ReportPhysicalAddressValidator implements ParameterValidator {
286 @Override
287 public boolean isValid(byte[] params) {
288 if (params.length != 3) {
289 return false;
290 }
291 return isValidPhysicalAddress(params, 0) && isValidType(params[2]);
292 }
293 }
294
295 private class RoutingChangeValidator implements ParameterValidator {
296 @Override
297 public boolean isValid(byte[] params) {
298 if (params.length != 4) {
299 return false;
300 }
301 return isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2);
302 }
303 }
304}