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 
17 package com.android.server.hdmi;
18 
19 import com.android.server.hdmi.Constants.AudioCodec;
20 
21 import java.io.UnsupportedEncodingException;
22 
23 /**
24  * A helper class to build {@link HdmiCecMessage} from various cec commands.
25  *
26  * If a message type has its own specific subclass of {@link HdmiCecMessage},
27  * its static factory method is instead declared in that subclass.
28  */
29 public class HdmiCecMessageBuilder {
30     private static final int OSD_NAME_MAX_LENGTH = 14;
31 
HdmiCecMessageBuilder()32     private HdmiCecMessageBuilder() {}
33 
34     /**
35      * Build <Feature Abort> command. <Feature Abort> consists of
36      * 1 byte original opcode and 1 byte reason fields with basic fields.
37      *
38      * @param src source address of command
39      * @param dest destination address of command
40      * @param originalOpcode original opcode causing feature abort
41      * @param reason reason of feature abort
42      * @return newly created {@link HdmiCecMessage}
43      */
buildFeatureAbortCommand(int src, int dest, int originalOpcode, int reason)44     static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode,
45             int reason) {
46         byte[] params = new byte[] {
47                 (byte) (originalOpcode & 0xFF),
48                 (byte) (reason & 0xFF),
49         };
50         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_FEATURE_ABORT, params);
51     }
52 
53     /**
54      * Build <Give Physical Address> command.
55      *
56      * @param src source address of command
57      * @param dest destination address of command
58      * @return newly created {@link HdmiCecMessage}
59      */
buildGivePhysicalAddress(int src, int dest)60     static HdmiCecMessage buildGivePhysicalAddress(int src, int dest) {
61         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS);
62     }
63 
64     /**
65      * Build <Give Osd Name> command.
66      *
67      * @param src source address of command
68      * @param dest destination address of command
69      * @return newly created {@link HdmiCecMessage}
70      */
buildGiveOsdNameCommand(int src, int dest)71     static HdmiCecMessage buildGiveOsdNameCommand(int src, int dest) {
72         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_OSD_NAME);
73     }
74 
75     /**
76      * Build <Give Vendor Id Command> command.
77      *
78      * @param src source address of command
79      * @param dest destination address of command
80      * @return newly created {@link HdmiCecMessage}
81      */
buildGiveDeviceVendorIdCommand(int src, int dest)82     static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) {
83         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID);
84     }
85 
86     /**
87      * Build <Set Menu Language > command.
88      *
89      * <p>This is a broadcast message sent to all devices on the bus.
90      *
91      * @param src source address of command
92      * @param language 3-letter ISO639-2 based language code
93      * @return newly created {@link HdmiCecMessage} if language is valid.
94      *         Otherwise, return null
95      */
buildSetMenuLanguageCommand(int src, String language)96     static HdmiCecMessage buildSetMenuLanguageCommand(int src, String language) {
97         if (language.length() != 3) {
98             return null;
99         }
100         // Hdmi CEC uses lower-cased ISO 639-2 (3 letters code).
101         String normalized = language.toLowerCase();
102         byte[] params = new byte[] {
103                 (byte) (normalized.charAt(0) & 0xFF),
104                 (byte) (normalized.charAt(1) & 0xFF),
105                 (byte) (normalized.charAt(2) & 0xFF),
106         };
107         // <Set Menu Language> is broadcast message.
108         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
109                 Constants.MESSAGE_SET_MENU_LANGUAGE, params);
110     }
111 
112     /**
113      * Build &lt;Set Osd Name &gt; command.
114      *
115      * @param src source address of command
116      * @param name display (OSD) name of device
117      * @return newly created {@link HdmiCecMessage} if valid name. Otherwise,
118      *         return null
119      */
buildSetOsdNameCommand(int src, int dest, String name)120     static HdmiCecMessage buildSetOsdNameCommand(int src, int dest, String name) {
121         int length = Math.min(name.length(), OSD_NAME_MAX_LENGTH);
122         byte[] params;
123         try {
124             params = name.substring(0, length).getBytes("US-ASCII");
125         } catch (UnsupportedEncodingException e) {
126             return null;
127         }
128         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_OSD_NAME, params);
129     }
130 
131     /**
132      * Build &lt;Report Physical Address&gt; command. It has two bytes physical
133      * address and one byte device type as parameter.
134      *
135      * <p>This is a broadcast message sent to all devices on the bus.
136      *
137      * @param src source address of command
138      * @param address physical address of device
139      * @param deviceType type of device
140      * @return newly created {@link HdmiCecMessage}
141      */
buildReportPhysicalAddressCommand(int src, int address, int deviceType)142     static HdmiCecMessage buildReportPhysicalAddressCommand(int src, int address, int deviceType) {
143         byte[] params = new byte[] {
144                 // Two bytes for physical address
145                 (byte) ((address >> 8) & 0xFF),
146                 (byte) (address & 0xFF),
147                 // One byte device type
148                 (byte) (deviceType & 0xFF)
149         };
150         // <Report Physical Address> is broadcast message.
151         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
152                 Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS, params);
153     }
154 
155     /**
156      * Build &lt;Device Vendor Id&gt; command. It has three bytes vendor id as
157      * parameter.
158      *
159      * <p>This is a broadcast message sent to all devices on the bus.
160      *
161      * @param src source address of command
162      * @param vendorId device's vendor id
163      * @return newly created {@link HdmiCecMessage}
164      */
buildDeviceVendorIdCommand(int src, int vendorId)165     static HdmiCecMessage buildDeviceVendorIdCommand(int src, int vendorId) {
166         byte[] params = new byte[] {
167                 (byte) ((vendorId >> 16) & 0xFF),
168                 (byte) ((vendorId >> 8) & 0xFF),
169                 (byte) (vendorId & 0xFF)
170         };
171         // <Device Vendor Id> is broadcast message.
172         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
173                 Constants.MESSAGE_DEVICE_VENDOR_ID, params);
174     }
175 
176     /**
177      * Build &lt;Device Vendor Id&gt; command. It has one byte cec version as parameter.
178      *
179      * @param src source address of command
180      * @param dest destination address of command
181      * @param version version of cec. Use 0x04 for "Version 1.3a" and 0x05 for
182      *                "Version 1.4 or 1.4a or 1.4b
183      * @return newly created {@link HdmiCecMessage}
184      */
buildCecVersion(int src, int dest, int version)185     static HdmiCecMessage buildCecVersion(int src, int dest, int version) {
186         byte[] params = new byte[] {
187                 (byte) (version & 0xFF)
188         };
189         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CEC_VERSION, params);
190     }
191 
192     /**
193      * Build &lt;Request Arc Initiation&gt;
194      *
195      * @param src source address of command
196      * @param dest destination address of command
197      * @return newly created {@link HdmiCecMessage}
198      */
buildRequestArcInitiation(int src, int dest)199     static HdmiCecMessage buildRequestArcInitiation(int src, int dest) {
200         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION);
201     }
202 
203     /**
204      * Build &lt;Initiate Arc&gt;
205      *
206      * @param src source address of command
207      * @param dest destination address of command
208      * @return newly created {@link HdmiCecMessage}
209      */
buildInitiateArc(int src, int dest)210     static HdmiCecMessage buildInitiateArc(int src, int dest) {
211         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_INITIATE_ARC);
212     }
213 
214     /**
215      * Build &lt;Terminate Arc&gt;
216      *
217      * @param src source address of command
218      * @param dest destination address of command
219      * @return newly created {@link HdmiCecMessage}
220      */
buildTerminateArc(int src, int dest)221     static HdmiCecMessage buildTerminateArc(int src, int dest) {
222         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_TERMINATE_ARC);
223     }
224 
225     /**
226      * Build &lt;Request Arc Termination&gt;
227      *
228      * @param src source address of command
229      * @param dest destination address of command
230      * @return newly created {@link HdmiCecMessage}
231      */
buildRequestArcTermination(int src, int dest)232     static HdmiCecMessage buildRequestArcTermination(int src, int dest) {
233         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION);
234     }
235 
236     /**
237      * Build &lt;Report Arc Initiated&gt;
238      *
239      * @param src source address of command
240      * @param dest destination address of command
241      * @return newly created {@link HdmiCecMessage}
242      */
buildReportArcInitiated(int src, int dest)243     static HdmiCecMessage buildReportArcInitiated(int src, int dest) {
244         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED);
245     }
246 
247     /**
248      * Build &lt;Report Arc Terminated&gt;
249      *
250      * @param src source address of command
251      * @param dest destination address of command
252      * @return newly created {@link HdmiCecMessage}
253      */
buildReportArcTerminated(int src, int dest)254     static HdmiCecMessage buildReportArcTerminated(int src, int dest) {
255         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
256     }
257 
258 
259     /**
260      * Build &lt;Request Short Audio Descriptor&gt; command.
261      *
262      * @param src source address of command
263      * @param dest destination address of command
264      * @param audioFormats the {@link AudioCodec}s desired
265      * @return newly created {@link HdmiCecMessage}
266      */
buildRequestShortAudioDescriptor(int src, int dest, @AudioCodec int[] audioFormats)267     static HdmiCecMessage buildRequestShortAudioDescriptor(int src, int dest,
268             @AudioCodec int[] audioFormats) {
269         byte[] params = new byte[Math.min(audioFormats.length,4)] ;
270         for (int i = 0; i < params.length ; i++){
271             params[i] = (byte) (audioFormats[i] & 0xff);
272         }
273         return HdmiCecMessage.build(
274                 src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
275     }
276 
277 
278     /**
279      * Build &lt;Text View On&gt; command.
280      *
281      * @param src source address of command
282      * @param dest destination address of command
283      * @return newly created {@link HdmiCecMessage}
284      */
buildTextViewOn(int src, int dest)285     static HdmiCecMessage buildTextViewOn(int src, int dest) {
286         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_TEXT_VIEW_ON);
287     }
288 
289     /**
290      * Build &lt;Request Active Source&gt; command.
291      *
292      * @param src source address of command
293      * @return newly created {@link HdmiCecMessage}
294      */
buildRequestActiveSource(int src)295     static HdmiCecMessage buildRequestActiveSource(int src) {
296         return HdmiCecMessage.build(
297                 src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
298     }
299 
300     /**
301      * Build &lt;Active Source&gt; command.
302      *
303      * @param src source address of command
304      * @param physicalAddress physical address of the device to become active
305      * @return newly created {@link HdmiCecMessage}
306      */
buildActiveSource(int src, int physicalAddress)307     static HdmiCecMessage buildActiveSource(int src, int physicalAddress) {
308         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ACTIVE_SOURCE,
309                 physicalAddressToParam(physicalAddress));
310     }
311 
312     /**
313      * Build &lt;Inactive Source&gt; command.
314      *
315      * @param src source address of command
316      * @param physicalAddress physical address of the device to become inactive
317      * @return newly created {@link HdmiCecMessage}
318      */
buildInactiveSource(int src, int physicalAddress)319     static HdmiCecMessage buildInactiveSource(int src, int physicalAddress) {
320         return HdmiCecMessage.build(src, Constants.ADDR_TV,
321                 Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressToParam(physicalAddress));
322     }
323 
324     /**
325      * Build &lt;Set Stream Path&gt; command.
326      *
327      * <p>This is a broadcast message sent to all devices on the bus.
328      *
329      * @param src source address of command
330      * @param streamPath physical address of the device to start streaming
331      * @return newly created {@link HdmiCecMessage}
332      */
buildSetStreamPath(int src, int streamPath)333     static HdmiCecMessage buildSetStreamPath(int src, int streamPath) {
334         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
335                 Constants.MESSAGE_SET_STREAM_PATH, physicalAddressToParam(streamPath));
336     }
337 
338     /**
339      * Build &lt;Routing Change&gt; command.
340      *
341      * <p>This is a broadcast message sent to all devices on the bus.
342      *
343      * @param src source address of command
344      * @param oldPath physical address of the currently active routing path
345      * @param newPath physical address of the new active routing path
346      * @return newly created {@link HdmiCecMessage}
347      */
buildRoutingChange(int src, int oldPath, int newPath)348     static HdmiCecMessage buildRoutingChange(int src, int oldPath, int newPath) {
349         byte[] param = new byte[] {
350             (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
351             (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
352         };
353         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_ROUTING_CHANGE,
354                 param);
355     }
356 
357     /**
358      * Build &lt;Routing Information&gt; command.
359      *
360      * <p>This is a broadcast message sent to all devices on the bus.
361      *
362      * @param src source address of command
363      * @param physicalAddress physical address of the new active routing path
364      * @return newly created {@link HdmiCecMessage}
365      */
buildRoutingInformation(int src, int physicalAddress)366     static HdmiCecMessage buildRoutingInformation(int src, int physicalAddress) {
367         return HdmiCecMessage.build(src, Constants.ADDR_BROADCAST,
368             Constants.MESSAGE_ROUTING_INFORMATION, physicalAddressToParam(physicalAddress));
369     }
370 
371     /**
372      * Build &lt;Give Device Power Status&gt; command.
373      *
374      * @param src source address of command
375      * @param dest destination address of command
376      * @return newly created {@link HdmiCecMessage}
377      */
buildGiveDevicePowerStatus(int src, int dest)378     static HdmiCecMessage buildGiveDevicePowerStatus(int src, int dest) {
379         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS);
380     }
381 
382     /**
383      * Build &lt;Report Power Status&gt; command.
384      *
385      * @param src source address of command
386      * @param dest destination address of command
387      * @param powerStatus power status of the device
388      * @return newly created {@link HdmiCecMessage}
389      */
buildReportPowerStatus(int src, int dest, int powerStatus)390     static HdmiCecMessage buildReportPowerStatus(int src, int dest, int powerStatus) {
391         byte[] param = new byte[] {
392                 (byte) (powerStatus & 0xFF)
393         };
394         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
395     }
396 
397     /**
398      * Build &lt;Report Menu Status&gt; command.
399      *
400      * @param src source address of command
401      * @param dest destination address of command
402      * @param menuStatus menu status of the device
403      * @return newly created {@link HdmiCecMessage}
404      */
buildReportMenuStatus(int src, int dest, int menuStatus)405     static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) {
406         byte[] param = new byte[] {
407                 (byte) (menuStatus & 0xFF)
408         };
409         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_MENU_STATUS, param);
410     }
411 
412     /**
413      * Build &lt;System Audio Mode Request&gt; command.
414      *
415      * @param src source address of command
416      * @param avr destination address of command, it should be AVR
417      * @param avrPhysicalAddress physical address of AVR
418      * @param enableSystemAudio whether to enable System Audio Mode or not
419      * @return newly created {@link HdmiCecMessage}
420      */
buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress, boolean enableSystemAudio)421     static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
422             boolean enableSystemAudio) {
423         if (enableSystemAudio) {
424             return HdmiCecMessage.build(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
425                     physicalAddressToParam(avrPhysicalAddress));
426         } else {
427             return HdmiCecMessage.build(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
428         }
429     }
430 
431     /**
432      * Build &lt;Set System Audio Mode&gt; command.
433      *
434      * @param src source address of command
435      * @param des destination address of command
436      * @param systemAudioStatus whether to set System Audio Mode on or off
437      * @return newly created {@link HdmiCecMessage}
438      */
buildSetSystemAudioMode(int src, int des, boolean systemAudioStatus)439     static HdmiCecMessage buildSetSystemAudioMode(int src, int des, boolean systemAudioStatus) {
440         return buildCommandWithBooleanParam(src, des, Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
441             systemAudioStatus
442         );
443     }
444 
445     /**
446      * Build &lt;Report System Audio Mode&gt; command.
447      *
448      * @param src source address of command
449      * @param des destination address of command
450      * @param systemAudioStatus whether System Audio Mode is on or off
451      * @return newly created {@link HdmiCecMessage}
452      */
buildReportSystemAudioMode(int src, int des, boolean systemAudioStatus)453     static HdmiCecMessage buildReportSystemAudioMode(int src, int des, boolean systemAudioStatus) {
454         return buildCommandWithBooleanParam(src, des, Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
455             systemAudioStatus
456         );
457     }
458 
459     /**
460      * Build &lt;Report Short Audio Descriptor&gt; command.
461      *
462      * @param src source address of command
463      * @param des destination address of command
464      * @param sadBytes Short Audio Descriptor in bytes
465      * @return newly created {@link HdmiCecMessage}
466      */
buildReportShortAudioDescriptor(int src, int des, byte[] sadBytes)467     static HdmiCecMessage buildReportShortAudioDescriptor(int src, int des, byte[] sadBytes) {
468         return HdmiCecMessage.build(
469                 src, des, Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR, sadBytes);
470     }
471 
472     /**
473      * Build &lt;Give Audio Status&gt; command.
474      *
475      * @param src source address of command
476      * @param dest destination address of command
477      * @return newly created {@link HdmiCecMessage}
478      */
buildGiveAudioStatus(int src, int dest)479     static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
480         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
481     }
482 
483     /**
484      * Build &lt;Report Audio Status&gt; command.
485      *
486      * @param src source address of command
487      * @param dest destination address of command
488      * @param volume volume level of current device in param
489      * @param mute mute status of current device in param
490      * @return newly created {@link HdmiCecMessage}
491      */
buildReportAudioStatus(int src, int dest, int volume, boolean mute)492     static HdmiCecMessage buildReportAudioStatus(int src, int dest, int volume, boolean mute) {
493         byte status = (byte) ((byte) (mute ? 1 << 7 : 0) | ((byte) volume & 0x7F));
494         byte[] params = new byte[] { status };
495         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
496     }
497 
498     /**
499      * Build &lt;User Control Pressed&gt; command.
500      *
501      * @param src source address of command
502      * @param dest destination address of command
503      * @param uiCommand keycode that user pressed
504      * @return newly created {@link HdmiCecMessage}
505      */
buildUserControlPressed(int src, int dest, int uiCommand)506     static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
507         return buildUserControlPressed(src, dest, new byte[] { (byte) (uiCommand & 0xFF) });
508     }
509 
510     /**
511      * Build &lt;User Control Pressed&gt; command.
512      *
513      * @param src source address of command
514      * @param dest destination address of command
515      * @param commandParam uiCommand and the additional parameter
516      * @return newly created {@link HdmiCecMessage}
517      */
buildUserControlPressed(int src, int dest, byte[] commandParam)518     static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) {
519         return HdmiCecMessage.build(
520                 src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
521     }
522 
523     /**
524      * Build &lt;User Control Released&gt; command.
525      *
526      * @param src source address of command
527      * @param dest destination address of command
528      * @return newly created {@link HdmiCecMessage}
529      */
buildUserControlReleased(int src, int dest)530     static HdmiCecMessage buildUserControlReleased(int src, int dest) {
531         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
532     }
533 
534     /**
535      * Build &lt;Give System Audio Mode Status&gt; command.
536      *
537      * @param src source address of command
538      * @param dest destination address of command
539      * @return newly created {@link HdmiCecMessage}
540      */
buildGiveSystemAudioModeStatus(int src, int dest)541     static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
542         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
543     }
544 
545     /**
546      * Build &lt;Standby&gt; command.
547      *
548      * @param src source address of command
549      * @param dest destination address of command
550      * @return newly created {@link HdmiCecMessage}
551      */
buildStandby(int src, int dest)552     public static HdmiCecMessage buildStandby(int src, int dest) {
553         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_STANDBY);
554     }
555 
556     /**
557      * Build &lt;Vendor Command&gt; command.
558      *
559      * @param src source address of command
560      * @param dest destination address of command
561      * @param params vendor-specific parameters
562      * @return newly created {@link HdmiCecMessage}
563      */
buildVendorCommand(int src, int dest, byte[] params)564     static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
565         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
566     }
567 
568     /**
569      * Build &lt;Vendor Command With ID&gt; command.
570      *
571      * @param src source address of command
572      * @param dest destination address of command
573      * @param vendorId vendor ID
574      * @param operands vendor-specific parameters
575      * @return newly created {@link HdmiCecMessage}
576      */
buildVendorCommandWithId(int src, int dest, int vendorId, byte[] operands)577     static HdmiCecMessage buildVendorCommandWithId(int src, int dest, int vendorId,
578             byte[] operands) {
579         byte[] params = new byte[operands.length + 3];  // parameter plus len(vendorId)
580         params[0] = (byte) ((vendorId >> 16) & 0xFF);
581         params[1] = (byte) ((vendorId >> 8) & 0xFF);
582         params[2] = (byte) (vendorId & 0xFF);
583         System.arraycopy(operands, 0, params, 3, operands.length);
584         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
585     }
586 
587     /**
588      * Build &lt;Record On&gt; command.
589      *
590      * @param src source address of command
591      * @param dest destination address of command
592      * @param params parameter of command
593      * @return newly created {@link HdmiCecMessage}
594      */
buildRecordOn(int src, int dest, byte[] params)595     static HdmiCecMessage buildRecordOn(int src, int dest, byte[] params) {
596         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_RECORD_ON, params);
597     }
598 
599     /**
600      * Build &lt;Record Off&gt; command.
601      *
602      * @param src source address of command
603      * @param dest destination address of command
604      * @return newly created {@link HdmiCecMessage}
605      */
buildRecordOff(int src, int dest)606     static HdmiCecMessage buildRecordOff(int src, int dest) {
607         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_RECORD_OFF);
608     }
609 
610     /**
611      * Build &lt;Set Digital Timer&gt; command.
612      *
613      * @param src source address of command
614      * @param dest destination address of command
615      * @param params byte array of timing information and digital service information to be recorded
616      * @return newly created {@link HdmiCecMessage}
617      */
buildSetDigitalTimer(int src, int dest, byte[] params)618     static HdmiCecMessage buildSetDigitalTimer(int src, int dest, byte[] params) {
619         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
620     }
621 
622     /**
623      * Build &lt;Set Analogue Timer&gt; command.
624      *
625      * @param src source address of command
626      * @param dest destination address of command
627      * @param params byte array of timing information and analog service information to be recorded
628      * @return newly created {@link HdmiCecMessage}
629      */
buildSetAnalogueTimer(int src, int dest, byte[] params)630     static HdmiCecMessage buildSetAnalogueTimer(int src, int dest, byte[] params) {
631         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
632     }
633 
634     /**
635      * Build &lt;Set External Timer&gt; command.
636      *
637      * @param src source address of command
638      * @param dest destination address of command
639      * @param params byte array of timing information and external source information to be recorded
640      * @return newly created {@link HdmiCecMessage}
641      */
buildSetExternalTimer(int src, int dest, byte[] params)642     static HdmiCecMessage buildSetExternalTimer(int src, int dest, byte[] params) {
643         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
644     }
645 
646     /**
647      * Build &lt;Clear Digital Timer&gt; command.
648      *
649      * @param src source address of command
650      * @param dest destination address of command
651      * @param params byte array of timing information and digital service information to be cleared
652      * @return newly created {@link HdmiCecMessage}
653      */
buildClearDigitalTimer(int src, int dest, byte[] params)654     static HdmiCecMessage buildClearDigitalTimer(int src, int dest, byte[] params) {
655         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
656     }
657 
658     /**
659      * Build &lt;Clear Analog Timer&gt; command.
660      *
661      * @param src source address of command
662      * @param dest destination address of command
663      * @param params byte array of timing information and analog service information to be cleared
664      * @return newly created {@link HdmiCecMessage}
665      */
buildClearAnalogueTimer(int src, int dest, byte[] params)666     static HdmiCecMessage buildClearAnalogueTimer(int src, int dest, byte[] params) {
667         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
668     }
669 
670     /**
671      * Build &lt;Clear Digital Timer&gt; command.
672      *
673      * @param src source address of command
674      * @param dest destination address of command
675      * @param params byte array of timing information and external source information to be cleared
676      * @return newly created {@link HdmiCecMessage}
677      */
buildClearExternalTimer(int src, int dest, byte[] params)678     static HdmiCecMessage buildClearExternalTimer(int src, int dest, byte[] params) {
679         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
680     }
681 
buildGiveFeatures(int src, int dest)682     static HdmiCecMessage buildGiveFeatures(int src, int dest) {
683         return HdmiCecMessage.build(src, dest, Constants.MESSAGE_GIVE_FEATURES);
684     }
685 
686     /***** Please ADD new buildXXX() methods above. ******/
687 
688     /**
689      * Build a {@link HdmiCecMessage} with a boolean param and other given values.
690      *
691      * @param src source address of command
692      * @param des destination address of command
693      * @param opcode opcode for a message
694      * @param param boolean param for building the command
695      * @return newly created {@link HdmiCecMessage}
696      */
buildCommandWithBooleanParam(int src, int des, int opcode, boolean param)697     private static HdmiCecMessage buildCommandWithBooleanParam(int src, int des,
698         int opcode, boolean param) {
699         byte[] params = new byte[]{
700             param ? (byte) 0b1 : 0b0
701         };
702         return HdmiCecMessage.build(src, des, opcode, params);
703     }
704 
physicalAddressToParam(int physicalAddress)705     private static byte[] physicalAddressToParam(int physicalAddress) {
706         return new byte[] {
707                 (byte) ((physicalAddress >> 8) & 0xFF),
708                 (byte) (physicalAddress & 0xFF)
709         };
710     }
711 }
712