175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo/*
275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * Copyright (C) 2014 The Android Open Source Project
375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo *
475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * Licensed under the Apache License, Version 2.0 (the "License");
575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * you may not use this file except in compliance with the License.
675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * You may obtain a copy of the License at
775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo *
875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo *      http://www.apache.org/licenses/LICENSE-2.0
975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo *
1075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * Unless required by applicable law or agreed to in writing, software
1175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * distributed under the License is distributed on an "AS IS" BASIS,
1275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * See the License for the specific language governing permissions and
1475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * limitations under the License.
1575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo */
1675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
1775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heopackage com.android.server.hdmi;
1875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
2075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heoimport android.util.SparseArray;
2175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
2275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo/**
2375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo * A helper class to validates {@link HdmiCecMessage}.
2475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo */
2575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heopublic final class HdmiCecMessageValidator {
2675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private static final String TAG = "HdmiCecMessageValidator";
2775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
284c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo    static final int OK = 0;
294c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo    static final int ERROR_SOURCE = 1;
304c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo    static final int ERROR_DESTINATION = 2;
314c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo    static final int ERROR_PARAMETER = 3;
32a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo    static final int ERROR_PARAMETER_SHORT = 4;
334c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo
3475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private final HdmiControlService mService;
3575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
3675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    interface ParameterValidator {
37a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        /**
38a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo         * @return errorCode errorCode can be {@link #OK}, {@link #ERROR_PARAMETER} or
39a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo         *         {@link #ERROR_PARAMETER_SHORT}.
40a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo         */
41a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        int isValid(byte[] params);
4275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
4375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
44e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    // Only the direct addressing is allowed.
45e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    private static final int DEST_DIRECT = 1 << 0;
46e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    // Only the broadcast addressing is allowed.
47e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    private static final int DEST_BROADCAST = 1 << 1;
48e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    // Both the direct and the broadcast addressing are allowed.
49e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    private static final int DEST_ALL = DEST_DIRECT | DEST_BROADCAST;
50e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    // True if the messages from address 15 (unregistered) are allowed.
51e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    private static final int SRC_UNREGISTERED = 1 << 2;
52e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo
53e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    private static class ValidationInfo {
54e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        public final ParameterValidator parameterValidator;
55e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        public final int addressType;
56e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo
57e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        public ValidationInfo(ParameterValidator validator, int type) {
58e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo            parameterValidator = validator;
59e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo            addressType = type;
60e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        }
61e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    }
62e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo
63e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    final SparseArray<ValidationInfo> mValidationInfo = new SparseArray<>();
6475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
6575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    public HdmiCecMessageValidator(HdmiControlService service) {
6675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mService = service;
6775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
6875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages related to the physical address.
6975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        PhysicalAddressValidator physicalAddressValidator = new PhysicalAddressValidator();
70e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_ACTIVE_SOURCE,
71e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                physicalAddressValidator, DEST_BROADCAST | SRC_UNREGISTERED);
72e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressValidator, DEST_DIRECT);
73e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
74e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new ReportPhysicalAddressValidator(), DEST_BROADCAST | SRC_UNREGISTERED);
75e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_ROUTING_CHANGE,
76e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new RoutingChangeValidator(), DEST_BROADCAST | SRC_UNREGISTERED);
77e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_ROUTING_INFORMATION,
78e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                physicalAddressValidator, DEST_BROADCAST | SRC_UNREGISTERED);
79e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SET_STREAM_PATH,
80e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                physicalAddressValidator, DEST_BROADCAST);
81e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
8203611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo                new SystemAudioModeRequestValidator(), DEST_DIRECT);
8375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
8475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages have no parameter.
8575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        FixedLengthValidator noneValidator = new FixedLengthValidator(0);
86e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_ABORT, noneValidator, DEST_DIRECT);
87e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GET_CEC_VERSION, noneValidator, DEST_DIRECT);
88e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GET_MENU_LANGUAGE,
89e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                noneValidator, DEST_DIRECT | SRC_UNREGISTERED);
90e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GIVE_AUDIO_STATUS, noneValidator, DEST_DIRECT);
91e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, noneValidator, DEST_DIRECT);
92e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID,
93e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                noneValidator, DEST_DIRECT | SRC_UNREGISTERED);
94e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GIVE_OSD_NAME, noneValidator, DEST_DIRECT);
95e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS,
96e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                noneValidator, DEST_DIRECT | SRC_UNREGISTERED);
97e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS,
98e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                noneValidator, DEST_DIRECT);
99e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_IMAGE_VIEW_ON, noneValidator, DEST_DIRECT);
100e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_INITIATE_ARC, noneValidator, DEST_DIRECT);
101e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_RECORD_OFF, noneValidator, DEST_DIRECT);
102e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_RECORD_TV_SCREEN, noneValidator, DEST_DIRECT);
103e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REPORT_ARC_INITIATED, noneValidator, DEST_DIRECT);
104e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REPORT_ARC_TERMINATED, noneValidator, DEST_DIRECT);
105e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REQUEST_ARC_INITIATION, noneValidator, DEST_DIRECT);
106e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REQUEST_ARC_TERMINATION, noneValidator, DEST_DIRECT);
107e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE,
108e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                noneValidator, DEST_BROADCAST | SRC_UNREGISTERED);
109e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_STANDBY, noneValidator, DEST_ALL | SRC_UNREGISTERED);
110e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_TERMINATE_ARC, noneValidator, DEST_DIRECT);
111e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_TEXT_VIEW_ON, noneValidator, DEST_DIRECT);
112e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_TUNER_STEP_DECREMENT, noneValidator, DEST_DIRECT);
113e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_TUNER_STEP_INCREMENT, noneValidator, DEST_DIRECT);
114e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_USER_CONTROL_RELEASED, noneValidator, DEST_DIRECT);
115e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP, noneValidator, DEST_ALL);
11675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
11775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // TODO: Validate more than length for the following messages.
11875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
11975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the One Touch Record.
12075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
121e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_RECORD_ON,
122e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new VariableLengthValidator(1, 8), DEST_DIRECT);
123e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_RECORD_STATUS, oneByteValidator, DEST_DIRECT);
12475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
12575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // TODO: Handle messages for the Timer Programming.
12675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
12775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the System Information.
128e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT);
129e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
130e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new FixedLengthValidator(3), DEST_BROADCAST);
13175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
13275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // TODO: Handle messages for the Deck Control.
13375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
13475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // TODO: Handle messages for the Tuner Control.
13575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
13675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the Vendor Specific Commands.
13775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        VariableLengthValidator maxLengthValidator = new VariableLengthValidator(0, 14);
138e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_DEVICE_VENDOR_ID,
139e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new FixedLengthValidator(3), DEST_BROADCAST);
140e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        // Allow unregistered source for all vendor specific commands, because we don't know
141e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        // how to use the commands at this moment.
142e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_VENDOR_COMMAND,
143e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                maxLengthValidator, DEST_DIRECT | SRC_UNREGISTERED);
144e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
145a95794bc4fbc5cd19561e447535b99bbae00de25Jinsuk Kim                new VariableLengthValidator(4, 14), DEST_ALL | SRC_UNREGISTERED);
146e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN,
147e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                maxLengthValidator, DEST_ALL | SRC_UNREGISTERED);
14875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
14975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the OSD.
150e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SET_OSD_STRING, maxLengthValidator, DEST_DIRECT);
151e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, maxLengthValidator, DEST_DIRECT);
15275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
153184b124ec22a796327642e3546d366179e693f07Yuncheol Heo        // Messages for the Device Menu Control.
154184b124ec22a796327642e3546d366179e693f07Yuncheol Heo        addValidationInfo(Constants.MESSAGE_MENU_REQUEST, oneByteValidator, DEST_DIRECT);
155184b124ec22a796327642e3546d366179e693f07Yuncheol Heo        addValidationInfo(Constants.MESSAGE_MENU_STATUS, oneByteValidator, DEST_DIRECT);
15675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
15775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the Remote Control Passthrough.
15875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // TODO: Parse the first parameter and determine if it can have the next parameter.
159e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_USER_CONTROL_PRESSED,
160e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new VariableLengthValidator(1, 2), DEST_DIRECT);
16175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
16275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the Power Status.
163e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REPORT_POWER_STATUS, oneByteValidator, DEST_DIRECT);
16475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
16575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the General Protocol.
166e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_FEATURE_ABORT,
167e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new FixedLengthValidator(2), DEST_DIRECT);
16875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
16975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the System Audio Control.
170e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REPORT_AUDIO_STATUS, oneByteValidator, DEST_DIRECT);
171e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR,
172e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                new FixedLengthValidator(3), DEST_DIRECT);
173e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
174e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                oneByteValidator, DEST_DIRECT);
175e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, oneByteValidator, DEST_ALL);
176e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
177e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                oneByteValidator, DEST_DIRECT);
17875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
17975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the Audio Rate Control.
180e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_SET_AUDIO_RATE, oneByteValidator, DEST_DIRECT);
18175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
18275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // All Messages for the ARC have no parameters.
18375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
18475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        // Messages for the Capability Discovery and Control.
185e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        addValidationInfo(Constants.MESSAGE_CDC_MESSAGE, maxLengthValidator,
186e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                DEST_BROADCAST | SRC_UNREGISTERED);
187e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    }
188e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo
189e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo    private void addValidationInfo(int opcode, ParameterValidator validator, int addrType) {
190e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        mValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
19175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
19275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
1934c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo    int isValid(HdmiCecMessage message) {
19475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        int opcode = message.getOpcode();
195e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        ValidationInfo info = mValidationInfo.get(opcode);
196e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        if (info == null) {
1972e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.warning("No validation information for the message: " + message);
1984c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            return OK;
19975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
200e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo
201e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        // Check the source field.
202e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        if (message.getSource() == Constants.ADDR_UNREGISTERED &&
203e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo                (info.addressType & SRC_UNREGISTERED) == 0) {
2042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.warning("Unexpected source: " + message);
2054c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            return ERROR_SOURCE;
206e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        }
207e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        // Check the destination field.
208e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        if (message.getDestination() == Constants.ADDR_BROADCAST) {
209e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo            if ((info.addressType & DEST_BROADCAST) == 0) {
2102e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                HdmiLogger.warning("Unexpected broadcast message: " + message);
2114c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo                return ERROR_DESTINATION;
212e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo            }
213e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        } else {  // Direct addressing.
214e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo            if ((info.addressType & DEST_DIRECT) == 0) {
2152e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                HdmiLogger.warning("Unexpected direct message: " + message);
2164c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo                return ERROR_DESTINATION;
217e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo            }
218e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        }
219e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo
220e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        // Check the parameter type.
221a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        int errorCode = info.parameterValidator.isValid(message.getParams());
222a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        if (errorCode != OK) {
2232e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.warning("Unexpected parameters: " + message);
224a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            return errorCode;
225e9b9b1e7845d93015363c627d76b719f33120158Yuncheol Heo        }
2264c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        return OK;
22775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
22875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
22975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private static class FixedLengthValidator implements ParameterValidator {
23075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        private final int mLength;
23175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
23275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        public FixedLengthValidator(int length) {
23375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            mLength = length;
23475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
23575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
23675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        @Override
237a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        public int isValid(byte[] params) {
238a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            // If the length is longer than expected, we assume it's OK since the parameter can be
239a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            // extended in the future version.
240a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            return params.length < mLength ? ERROR_PARAMETER_SHORT : OK;
24175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
24275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
24375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
24475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private static class VariableLengthValidator implements ParameterValidator {
24575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        private final int mMinLength;
24675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        private final int mMaxLength;
24775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
24875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        public VariableLengthValidator(int minLength, int maxLength) {
24975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            mMinLength = minLength;
25075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            mMaxLength = maxLength;
25175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
25275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
25375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        @Override
254a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        public int isValid(byte[] params) {
255a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            return params.length < mMinLength ? ERROR_PARAMETER_SHORT : OK;
25675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
25775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
25875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
25975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private boolean isValidPhysicalAddress(byte[] params, int offset) {
260e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        // TODO: Add more logic like validating 1.0.1.0.
261e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
262e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        if (!mService.isTvDevice()) {
263e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo            // If the device is not TV, we can't convert path to port-id, so stop here.
264e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo            return true;
265e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        }
26675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        int path = HdmiUtils.twoBytesToInt(params, offset);
2677dea98f6f929cce598d669a802b13851987e2e64Yuncheol Heo        if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == mService.getPhysicalAddress()) {
2687dea98f6f929cce598d669a802b13851987e2e64Yuncheol Heo            return true;
2697dea98f6f929cce598d669a802b13851987e2e64Yuncheol Heo        }
27075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        int portId = mService.pathToPortId(path);
27175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (portId == Constants.INVALID_PORT_ID) {
27275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
27375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
27475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        return true;
27575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
27675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
27775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    /**
2788e93c84739902f5adaa499b474f39e3c4807bc1cJungshik Jang     * Check if the given type is valid. A valid type is one of the actual logical device types
2797dea98f6f929cce598d669a802b13851987e2e64Yuncheol Heo     * defined in the standard ({@link HdmiDeviceInfo#DEVICE_TV},
2807dea98f6f929cce598d669a802b13851987e2e64Yuncheol Heo     * {@link HdmiDeviceInfo#DEVICE_PLAYBACK}, {@link HdmiDeviceInfo#DEVICE_TUNER},
281a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo     * {@link HdmiDeviceInfo#DEVICE_RECORDER}, and {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}).
28275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo     *
28375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo     * @param type device type
28475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo     * @return true if the given type is valid
28575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo     */
28675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    static boolean isValidType(int type) {
28761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiDeviceInfo.DEVICE_TV <= type
28861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                && type <= HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)
28961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                && type != HdmiDeviceInfo.DEVICE_RESERVED;
29075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
29175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
292a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo    private static int toErrorCode(boolean success) {
293a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        return success ? OK : ERROR_PARAMETER;
294a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo    }
295a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo
29675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private class PhysicalAddressValidator implements ParameterValidator {
29775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        @Override
298a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        public int isValid(byte[] params) {
299a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            if (params.length < 2) {
300a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo                return ERROR_PARAMETER_SHORT;
30175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            }
302a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            return toErrorCode(isValidPhysicalAddress(params, 0));
30375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
30475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
30575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
30603611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo    private class SystemAudioModeRequestValidator extends PhysicalAddressValidator {
30703611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo        @Override
30803611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo        public int isValid(byte[] params) {
30903611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo            // TV can send <System Audio Mode Request> with no parameters to terminate system audio.
31003611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo            if (params.length == 0) {
31103611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo                return OK;
31203611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo            }
31303611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo            return super.isValid(params);
31403611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo        }
31503611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo    }
31603611473bc1d13f8e55ac92e37a9716c2fd8e412Yuncheol Heo
31775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private class ReportPhysicalAddressValidator implements ParameterValidator {
31875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        @Override
319a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        public int isValid(byte[] params) {
320a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            if (params.length < 3) {
321a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo                return ERROR_PARAMETER_SHORT;
32275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            }
323a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            return toErrorCode(isValidPhysicalAddress(params, 0) && isValidType(params[2]));
32475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
32575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
32675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
32775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private class RoutingChangeValidator implements ParameterValidator {
32875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        @Override
329a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo        public int isValid(byte[] params) {
330a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            if (params.length < 4) {
331a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo                return ERROR_PARAMETER_SHORT;
33275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            }
333a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            return toErrorCode(
334a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo                    isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2));
33575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
33675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    }
33775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo}
338