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