HdmiCecMessageBuilder.java revision 4085d0ef62554c7e139b82bca0f97161bb159f8c
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.HdmiCec;
20import android.hardware.hdmi.HdmiCecMessage;
21
22import java.io.UnsupportedEncodingException;
23import java.util.Arrays;
24
25/**
26 * A helper class to build {@link HdmiCecMessage} from various cec commands.
27 */
28public class HdmiCecMessageBuilder {
29    // TODO: move these values to HdmiCec.java once make it internal constant class.
30    // CEC's ABORT reason values.
31    static final int ABORT_UNRECOGNIZED_MODE = 0;
32    static final int ABORT_NOT_IN_CORRECT_MODE = 1;
33    static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
34    static final int ABORT_INVALID_OPERAND = 3;
35    static final int ABORT_REFUSED = 4;
36    static final int ABORT_UNABLE_TO_DETERMINE = 5;
37
38    private static final int OSD_NAME_MAX_LENGTH = 13;
39
40    private HdmiCecMessageBuilder() {}
41
42    /**
43     * Build {@link HdmiCecMessage} from raw data.
44     *
45     * @param src source address of command
46     * @param dest destination address of command
47     * @param body body of message. It includes opcode.
48     * @return newly created {@link HdmiCecMessage}
49     */
50    static HdmiCecMessage of(int src, int dest, byte[] body) {
51        byte opcode = body[0];
52        byte params[] = Arrays.copyOfRange(body, 1, body.length);
53        return new HdmiCecMessage(src, dest, opcode, params);
54    }
55
56    /**
57     * Build <Feature Abort> command. <Feature Abort> consists of
58     * 1 byte original opcode and 1 byte reason fields with basic fields.
59     *
60     * @param src source address of command
61     * @param dest destination address of command
62     * @param originalOpcode original opcode causing feature abort
63     * @param reason reason of feature abort
64     * @return newly created {@link HdmiCecMessage}
65     */
66    static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode,
67            int reason) {
68        byte[] params = new byte[] {
69                (byte) originalOpcode,
70                (byte) reason,
71        };
72        return buildCommand(src, dest, HdmiCec.MESSAGE_FEATURE_ABORT, params);
73    }
74
75    /**
76     * Build <Give Osd Name> command.
77     *
78     * @param src source address of command
79     * @param dest destination address of command
80     * @return newly created {@link HdmiCecMessage}
81     */
82    static HdmiCecMessage buildGiveOsdNameCommand(int src, int dest) {
83        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_OSD_NAME);
84    }
85
86    /**
87     * Build <Give Vendor Id Command> command.
88     *
89     * @param src source address of command
90     * @param dest destination address of command
91     * @return newly created {@link HdmiCecMessage}
92     */
93    static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) {
94        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID);
95    }
96
97    /**
98     * Build <Set Menu Language > command.
99     *
100     * <p>This is a broadcast message sent to all devices on the bus.
101     *
102     * @param src source address of command
103     * @param language 3-letter ISO639-2 based language code
104     * @return newly created {@link HdmiCecMessage} if language is valid.
105     *         Otherwise, return null
106     */
107    static HdmiCecMessage buildSetMenuLanguageCommand(int src, String language) {
108        if (language.length() != 3) {
109            return null;
110        }
111        // Hdmi CEC uses lower-cased ISO 639-2 (3 letters code).
112        String normalized = language.toLowerCase();
113        byte[] params = new byte[] {
114                (byte) normalized.charAt(0),
115                (byte) normalized.charAt(1),
116                (byte) normalized.charAt(2),
117        };
118        // <Set Menu Language> is broadcast message.
119        return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_MENU_LANGUAGE,
120                params);
121    }
122
123    /**
124     * Build &lt;Set Osd Name &gt; command.
125     *
126     * @param src source address of command
127     * @param name display (OSD) name of device
128     * @return newly created {@link HdmiCecMessage} if valid name. Otherwise,
129     *         return null
130     */
131    static HdmiCecMessage buildSetOsdNameCommand(int src, int dest, String name) {
132        int length = Math.min(name.length(), OSD_NAME_MAX_LENGTH);
133        byte[] params;
134        try {
135            params = name.substring(0, length).getBytes("US-ASCII");
136        } catch (UnsupportedEncodingException e) {
137            return null;
138        }
139        return buildCommand(src, dest, HdmiCec.MESSAGE_SET_OSD_NAME, params);
140    }
141
142    /**
143     * Build &lt;Report Physical Address&gt; command. It has two bytes physical
144     * address and one byte device type as parameter.
145     *
146     * <p>This is a broadcast message sent to all devices on the bus.
147     *
148     * @param src source address of command
149     * @param address physical address of device
150     * @param deviceType type of device
151     * @return newly created {@link HdmiCecMessage}
152     */
153    static HdmiCecMessage buildReportPhysicalAddressCommand(int src, int address, int deviceType) {
154        byte[] params = new byte[] {
155                // Two bytes for physical address
156                (byte) ((address >> 8) & 0xFF),
157                (byte) (address & 0xFF),
158                // One byte device type
159                (byte) deviceType
160        };
161        // <Report Physical Address> is broadcast message.
162        return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS,
163                params);
164    }
165
166    /**
167     * Build &lt;Device Vendor Id&gt; command. It has three bytes vendor id as
168     * parameter.
169     *
170     * <p>This is a broadcast message sent to all devices on the bus.
171     *
172     * @param src source address of command
173     * @param vendorId device's vendor id
174     * @return newly created {@link HdmiCecMessage}
175     */
176    static HdmiCecMessage buildDeviceVendorIdCommand(int src, int vendorId) {
177        byte[] params = new byte[] {
178                (byte) ((vendorId >> 16) & 0xFF),
179                (byte) ((vendorId >> 8) & 0xFF),
180                (byte) (vendorId & 0xFF)
181        };
182        // <Device Vendor Id> is broadcast message.
183        return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_DEVICE_VENDOR_ID,
184                params);
185    }
186
187    /**
188     * Build &lt;Device Vendor Id&gt; command. It has one byte cec version as parameter.
189     *
190     * @param src source address of command
191     * @param dest destination address of command
192     * @param version version of cec. Use 0x04 for "Version 1.3a" and 0x05 for
193     *                "Version 1.4 or 1.4a or 1.4b
194     * @return newly created {@link HdmiCecMessage}
195     */
196    static HdmiCecMessage buildCecVersion(int src, int dest, int version) {
197        byte[] params = new byte[] {
198                (byte) version
199        };
200        return buildCommand(src, dest, HdmiCec.MESSAGE_CEC_VERSION, params);
201    }
202
203    /**
204     * Build &lt;Request Arc Initiation&gt;
205     *
206     * @param src source address of command
207     * @param dest destination address of command
208     * @return newly created {@link HdmiCecMessage}
209     */
210    static HdmiCecMessage buildRequestArcInitiation(int src, int dest) {
211        return buildCommand(src, dest, HdmiCec.MESSAGE_REQUEST_ARC_INITIATION);
212    }
213
214    /**
215     * Build &lt;Request Arc Termination&gt;
216     *
217     * @param src source address of command
218     * @param dest destination address of command
219     * @return newly created {@link HdmiCecMessage}
220     */
221    static HdmiCecMessage buildRequestArcTermination(int src, int dest) {
222        return buildCommand(src, dest, HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION);
223    }
224
225    /**
226     * Build &lt;Report Arc Initiated&gt;
227     *
228     * @param src source address of command
229     * @param dest destination address of command
230     * @return newly created {@link HdmiCecMessage}
231     */
232    static HdmiCecMessage buildReportArcInitiated(int src, int dest) {
233        return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_ARC_INITIATED);
234    }
235
236    /**
237     * Build &lt;Report Arc Terminated&gt;
238     *
239     * @param src source address of command
240     * @param dest destination address of command
241     * @return newly created {@link HdmiCecMessage}
242     */
243    static HdmiCecMessage buildReportArcTerminated(int src, int dest) {
244        return buildCommand(src, dest, HdmiCec.MESSAGE_REPORT_ARC_TERMINATED);
245    }
246
247    /**
248     * Build &lt;Text View On&gt; command.
249     *
250     * @param src source address of command
251     * @param dest destination address of command
252     * @return newly created {@link HdmiCecMessage}
253     */
254    static HdmiCecMessage buildTextViewOn(int src, int dest) {
255        return buildCommand(src, dest, HdmiCec.MESSAGE_TEXT_VIEW_ON);
256    }
257
258    /**
259     * Build &lt;Active Source&gt; command.
260     *
261     * @param src source address of command
262     * @param physicalAddress physical address of the device to become active
263     * @return newly created {@link HdmiCecMessage}
264     */
265    static HdmiCecMessage buildActiveSource(int src, int physicalAddress) {
266        return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ACTIVE_SOURCE,
267                physicalAddressToParam(physicalAddress));
268    }
269
270    /**
271     * Build &lt;Give Device Power Status&gt; command.
272     *
273     * @param src source address of command
274     * @param dest destination address of command
275     * @return newly created {@link HdmiCecMessage}
276     */
277    static HdmiCecMessage buildGiveDevicePowerStatus(int src, int dest) {
278        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS);
279    }
280
281    /**
282     * Build a {@link HdmiCecMessage} without extra parameter.
283     *
284     * @param src source address of command
285     * @param dest destination address of command
286     * @param opcode opcode for a message
287     * @return newly created {@link HdmiCecMessage}
288     */
289    private static HdmiCecMessage buildCommand(int src, int dest, int opcode) {
290        return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM);
291    }
292
293    /**
294     * Build a {@link HdmiCecMessage} with given values.
295     *
296     * @param src source address of command
297     * @param dest destination address of command
298     * @param opcode opcode for a message
299     * @param params extra parameters for command
300     * @return newly created {@link HdmiCecMessage}
301     */
302    private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) {
303        return new HdmiCecMessage(src, dest, opcode, params);
304    }
305
306    private static byte[] physicalAddressToParam(int physicalAddress) {
307        return new byte[] {
308                (byte) (physicalAddress >> 8),
309                (byte) (physicalAddress & 0xFF)
310        };
311    }
312}
313