HdmiCecMessageBuilder.java revision 119160a68195bcb2f5bdf4a269807e01228eca97
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 java.io.UnsupportedEncodingException;
20import java.util.Arrays;
21
22/**
23 * A helper class to build {@link HdmiCecMessage} from various cec commands.
24 */
25public class HdmiCecMessageBuilder {
26    private static final int OSD_NAME_MAX_LENGTH = 13;
27
28    private HdmiCecMessageBuilder() {}
29
30    /**
31     * Build {@link HdmiCecMessage} from raw data.
32     *
33     * @param src source address of command
34     * @param dest destination address of command
35     * @param body body of message. It includes opcode.
36     * @return newly created {@link HdmiCecMessage}
37     */
38    static HdmiCecMessage of(int src, int dest, byte[] body) {
39        byte opcode = body[0];
40        byte params[] = Arrays.copyOfRange(body, 1, body.length);
41        return new HdmiCecMessage(src, dest, opcode, params);
42    }
43
44    /**
45     * Build <Feature Abort> command. <Feature Abort> consists of
46     * 1 byte original opcode and 1 byte reason fields with basic fields.
47     *
48     * @param src source address of command
49     * @param dest destination address of command
50     * @param originalOpcode original opcode causing feature abort
51     * @param reason reason of feature abort
52     * @return newly created {@link HdmiCecMessage}
53     */
54    static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode,
55            int reason) {
56        byte[] params = new byte[] {
57                (byte) originalOpcode,
58                (byte) reason,
59        };
60        return buildCommand(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
61    }
62
63    /**
64     * Build <Give Physical Address> command.
65     *
66     * @param src source address of command
67     * @param dest destination address of command
68     * @return newly created {@link HdmiCecMessage}
69     */
70    static HdmiCecMessage buildGivePhysicalAddress(int src, int dest) {
71        return buildCommand(src, dest, Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS);
72    }
73
74    /**
75     * Build <Give Osd Name> command.
76     *
77     * @param src source address of command
78     * @param dest destination address of command
79     * @return newly created {@link HdmiCecMessage}
80     */
81    static HdmiCecMessage buildGiveOsdNameCommand(int src, int dest) {
82        return buildCommand(src, dest, Constants.MESSAGE_GIVE_OSD_NAME);
83    }
84
85    /**
86     * Build <Give Vendor Id Command> command.
87     *
88     * @param src source address of command
89     * @param dest destination address of command
90     * @return newly created {@link HdmiCecMessage}
91     */
92    static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) {
93        return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID);
94    }
95
96    /**
97     * Build <Set Menu Language > command.
98     *
99     * <p>This is a broadcast message sent to all devices on the bus.
100     *
101     * @param src source address of command
102     * @param language 3-letter ISO639-2 based language code
103     * @return newly created {@link HdmiCecMessage} if language is valid.
104     *         Otherwise, return null
105     */
106    static HdmiCecMessage buildSetMenuLanguageCommand(int src, String language) {
107        if (language.length() != 3) {
108            return null;
109        }
110        // Hdmi CEC uses lower-cased ISO 639-2 (3 letters code).
111        String normalized = language.toLowerCase();
112        byte[] params = new byte[] {
113                (byte) normalized.charAt(0),
114                (byte) normalized.charAt(1),
115                (byte) normalized.charAt(2),
116        };
117        // <Set Menu Language> is broadcast message.
118        return buildCommand(src, Constants.ADDR_BROADCAST,
119                Constants.MESSAGE_SET_MENU_LANGUAGE, params);
120    }
121
122    /**
123     * Build &lt;Set Osd Name &gt; command.
124     *
125     * @param src source address of command
126     * @param name display (OSD) name of device
127     * @return newly created {@link HdmiCecMessage} if valid name. Otherwise,
128     *         return null
129     */
130    static HdmiCecMessage buildSetOsdNameCommand(int src, int dest, String name) {
131        int length = Math.min(name.length(), OSD_NAME_MAX_LENGTH);
132        byte[] params;
133        try {
134            params = name.substring(0, length).getBytes("US-ASCII");
135        } catch (UnsupportedEncodingException e) {
136            return null;
137        }
138        return buildCommand(src, dest, Constants.MESSAGE_SET_OSD_NAME, params);
139    }
140
141    /**
142     * Build &lt;Report Physical Address&gt; command. It has two bytes physical
143     * address and one byte device type as parameter.
144     *
145     * <p>This is a broadcast message sent to all devices on the bus.
146     *
147     * @param src source address of command
148     * @param address physical address of device
149     * @param deviceType type of device
150     * @return newly created {@link HdmiCecMessage}
151     */
152    static HdmiCecMessage buildReportPhysicalAddressCommand(int src, int address, int deviceType) {
153        byte[] params = new byte[] {
154                // Two bytes for physical address
155                (byte) ((address >> 8) & 0xFF),
156                (byte) (address & 0xFF),
157                // One byte device type
158                (byte) deviceType
159        };
160        // <Report Physical Address> is broadcast message.
161        return buildCommand(src, Constants.ADDR_BROADCAST,
162                Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, params);
163    }
164
165    /**
166     * Build &lt;Device Vendor Id&gt; command. It has three bytes vendor id as
167     * parameter.
168     *
169     * <p>This is a broadcast message sent to all devices on the bus.
170     *
171     * @param src source address of command
172     * @param vendorId device's vendor id
173     * @return newly created {@link HdmiCecMessage}
174     */
175    static HdmiCecMessage buildDeviceVendorIdCommand(int src, int vendorId) {
176        byte[] params = new byte[] {
177                (byte) ((vendorId >> 16) & 0xFF),
178                (byte) ((vendorId >> 8) & 0xFF),
179                (byte) (vendorId & 0xFF)
180        };
181        // <Device Vendor Id> is broadcast message.
182        return buildCommand(src, Constants.ADDR_BROADCAST,
183                Constants.MESSAGE_DEVICE_VENDOR_ID, params);
184    }
185
186    /**
187     * Build &lt;Device Vendor Id&gt; command. It has one byte cec version as parameter.
188     *
189     * @param src source address of command
190     * @param dest destination address of command
191     * @param version version of cec. Use 0x04 for "Version 1.3a" and 0x05 for
192     *                "Version 1.4 or 1.4a or 1.4b
193     * @return newly created {@link HdmiCecMessage}
194     */
195    static HdmiCecMessage buildCecVersion(int src, int dest, int version) {
196        byte[] params = new byte[] {
197                (byte) version
198        };
199        return buildCommand(src, dest, Constants.MESSAGE_CEC_VERSION, params);
200    }
201
202    /**
203     * Build &lt;Request Arc Initiation&gt;
204     *
205     * @param src source address of command
206     * @param dest destination address of command
207     * @return newly created {@link HdmiCecMessage}
208     */
209    static HdmiCecMessage buildRequestArcInitiation(int src, int dest) {
210        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION);
211    }
212
213    /**
214     * Build &lt;Request Arc Termination&gt;
215     *
216     * @param src source address of command
217     * @param dest destination address of command
218     * @return newly created {@link HdmiCecMessage}
219     */
220    static HdmiCecMessage buildRequestArcTermination(int src, int dest) {
221        return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION);
222    }
223
224    /**
225     * Build &lt;Report Arc Initiated&gt;
226     *
227     * @param src source address of command
228     * @param dest destination address of command
229     * @return newly created {@link HdmiCecMessage}
230     */
231    static HdmiCecMessage buildReportArcInitiated(int src, int dest) {
232        return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED);
233    }
234
235    /**
236     * Build &lt;Report Arc Terminated&gt;
237     *
238     * @param src source address of command
239     * @param dest destination address of command
240     * @return newly created {@link HdmiCecMessage}
241     */
242    static HdmiCecMessage buildReportArcTerminated(int src, int dest) {
243        return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
244    }
245
246    /**
247     * Build &lt;Text View On&gt; command.
248     *
249     * @param src source address of command
250     * @param dest destination address of command
251     * @return newly created {@link HdmiCecMessage}
252     */
253    static HdmiCecMessage buildTextViewOn(int src, int dest) {
254        return buildCommand(src, dest, Constants.MESSAGE_TEXT_VIEW_ON);
255    }
256
257    /**
258     * Build &lt;Active Source&gt; command.
259     *
260     * @param src source address of command
261     * @param physicalAddress physical address of the device to become active
262     * @return newly created {@link HdmiCecMessage}
263     */
264    static HdmiCecMessage buildActiveSource(int src, int physicalAddress) {
265        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ACTIVE_SOURCE,
266                physicalAddressToParam(physicalAddress));
267    }
268
269    /**
270     * Build &lt;Inactive Source&gt; command.
271     *
272     * @param src source address of command
273     * @param physicalAddress physical address of the device to become inactive
274     * @return newly created {@link HdmiCecMessage}
275     */
276    static HdmiCecMessage buildInactiveSource(int src, int physicalAddress) {
277        return buildCommand(src, Constants.ADDR_BROADCAST,
278                Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressToParam(physicalAddress));
279    }
280
281    /**
282     * Build &lt;Set Stream Path&gt; command.
283     *
284     * <p>This is a broadcast message sent to all devices on the bus.
285     *
286     * @param src source address of command
287     * @param streamPath physical address of the device to start streaming
288     * @return newly created {@link HdmiCecMessage}
289     */
290    static HdmiCecMessage buildSetStreamPath(int src, int streamPath) {
291        return buildCommand(src, Constants.ADDR_BROADCAST,
292                Constants.MESSAGE_SET_STREAM_PATH, physicalAddressToParam(streamPath));
293    }
294
295    /**
296     * Build &lt;Routing Change&gt; command.
297     *
298     * <p>This is a broadcast message sent to all devices on the bus.
299     *
300     * @param src source address of command
301     * @param oldPath physical address of the currently active routing path
302     * @param newPath physical address of the new active routing path
303     * @return newly created {@link HdmiCecMessage}
304     */
305    static HdmiCecMessage buildRoutingChange(int src, int oldPath, int newPath) {
306        byte[] param = new byte[] {
307            (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
308            (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
309        };
310        return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ROUTING_CHANGE,
311                param);
312    }
313
314    /**
315     * Build &lt;Give Device Power Status&gt; command.
316     *
317     * @param src source address of command
318     * @param dest destination address of command
319     * @return newly created {@link HdmiCecMessage}
320     */
321    static HdmiCecMessage buildGiveDevicePowerStatus(int src, int dest) {
322        return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS);
323    }
324
325    /**
326     * Build &lt;Report Power Status&gt; command.
327     *
328     * @param src source address of command
329     * @param dest destination address of command
330     * @param powerStatus power status of the device
331     * @return newly created {@link HdmiCecMessage}
332     */
333    static HdmiCecMessage buildReportPowerStatus(int src, int dest, int powerStatus) {
334        byte[] param = new byte[] {
335                (byte) (powerStatus)
336        };
337        return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
338    }
339
340    /**
341     * Build &lt;System Audio Mode Request&gt; command.
342     *
343     * @param src source address of command
344     * @param avr destination address of command, it should be AVR
345     * @param avrPhysicalAddress physical address of AVR
346     * @param enableSystemAudio whether to enable System Audio Mode or not
347     * @return newly created {@link HdmiCecMessage}
348     */
349    static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
350            boolean enableSystemAudio) {
351        if (enableSystemAudio) {
352            return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
353                    physicalAddressToParam(avrPhysicalAddress));
354        } else {
355            return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
356        }
357    }
358
359    /**
360     * Build &lt;Give Audio Status&gt; command.
361     *
362     * @param src source address of command
363     * @param dest destination address of command
364     * @return newly created {@link HdmiCecMessage}
365     */
366    static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
367        return buildCommand(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
368    }
369
370    /**
371     * Build &lt;User Control Pressed&gt; command.
372     *
373     * @param src source address of command
374     * @param dest destination address of command
375     * @param uiCommand keycode that user pressed
376     * @return newly created {@link HdmiCecMessage}
377     */
378    static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
379        return buildUserControlPressed(src, dest, new byte[] { (byte) uiCommand });
380    }
381
382    /**
383     * Build &lt;User Control Pressed&gt; command.
384     *
385     * @param src source address of command
386     * @param dest destination address of command
387     * @param commandParam uiCommand and the additional parameter
388     * @return newly created {@link HdmiCecMessage}
389     */
390    static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) {
391        return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
392    }
393
394    /**
395     * Build &lt;User Control Released&gt; command.
396     *
397     * @param src source address of command
398     * @param dest destination address of command
399     * @return newly created {@link HdmiCecMessage}
400     */
401    static HdmiCecMessage buildUserControlReleased(int src, int dest) {
402        return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
403    }
404
405    /**
406     * Build &lt;Give System Audio Mode Status&gt; command.
407     *
408     * @param src source address of command
409     * @param dest destination address of command
410     * @return newly created {@link HdmiCecMessage}
411     */
412    static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
413        return buildCommand(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
414    }
415
416    /**
417     * Build &lt;Standby&gt; command.
418     *
419     * @param src source address of command
420     * @param dest destination address of command
421     * @return newly created {@link HdmiCecMessage}
422     */
423    public static HdmiCecMessage buildStandby(int src, int dest) {
424        return buildCommand(src, dest, Constants.MESSAGE_STANDBY);
425    }
426
427    /**
428     * Build &lt;Vendor Command&gt; command.
429     *
430     * @param src source address of command
431     * @param dest destination address of command
432     * @param params vendor-specific parameters
433     * @return newly created {@link HdmiCecMessage}
434     */
435    static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
436        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
437    }
438
439    /**
440     * Build &lt;Vendor Command With ID&gt; command.
441     *
442     * @param src source address of command
443     * @param dest destination address of command
444     * @param vendorId vendor ID
445     * @param operands vendor-specific parameters
446     * @return newly created {@link HdmiCecMessage}
447     */
448    static HdmiCecMessage buildVendorCommandWithId(int src, int dest, int vendorId,
449            byte[] operands) {
450        byte[] params = new byte[operands.length + 3];  // parameter plus len(vendorId)
451        params[0] = (byte) ((vendorId >> 16) & 0xFF);
452        params[1] = (byte) ((vendorId >> 8) & 0xFF);
453        params[2] = (byte) (vendorId & 0xFF);
454        System.arraycopy(operands, 0, params, 3, operands.length);
455        return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
456    }
457
458    /***** Please ADD new buildXXX() methods above. ******/
459
460    /**
461     * Build a {@link HdmiCecMessage} without extra parameter.
462     *
463     * @param src source address of command
464     * @param dest destination address of command
465     * @param opcode opcode for a message
466     * @return newly created {@link HdmiCecMessage}
467     */
468    private static HdmiCecMessage buildCommand(int src, int dest, int opcode) {
469        return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM);
470    }
471
472    /**
473     * Build a {@link HdmiCecMessage} with given values.
474     *
475     * @param src source address of command
476     * @param dest destination address of command
477     * @param opcode opcode for a message
478     * @param params extra parameters for command
479     * @return newly created {@link HdmiCecMessage}
480     */
481    private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) {
482        return new HdmiCecMessage(src, dest, opcode, params);
483    }
484
485    private static byte[] physicalAddressToParam(int physicalAddress) {
486        return new byte[] {
487                (byte) (physicalAddress >> 8),
488                (byte) (physicalAddress & 0xFF)
489        };
490    }
491}
492