Home | History | Annotate | Download | only in hdmi
      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 java.io.UnsupportedEncodingException;
     20 import java.util.Arrays;
     21 
     22 /**
     23  * A helper class to build {@link HdmiCecMessage} from various cec commands.
     24  */
     25 public 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 & 0xFF),
     58                 (byte) (reason & 0xFF),
     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) & 0xFF),
    114                 (byte) (normalized.charAt(1) & 0xFF),
    115                 (byte) (normalized.charAt(2) & 0xFF),
    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 & 0xFF)
    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 & 0xFF)
    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_TV,
    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 & 0xFF)
    336         };
    337         return buildCommand(src, dest, Constants.MESSAGE_REPORT_POWER_STATUS, param);
    338     }
    339 
    340     /**
    341      * Build &lt;Report Menu Status&gt; command.
    342      *
    343      * @param src source address of command
    344      * @param dest destination address of command
    345      * @param menuStatus menu status of the device
    346      * @return newly created {@link HdmiCecMessage}
    347      */
    348     static HdmiCecMessage buildReportMenuStatus(int src, int dest, int menuStatus) {
    349         byte[] param = new byte[] {
    350                 (byte) (menuStatus & 0xFF)
    351         };
    352         return buildCommand(src, dest, Constants.MESSAGE_MENU_STATUS, param);
    353     }
    354 
    355     /**
    356      * Build &lt;System Audio Mode Request&gt; command.
    357      *
    358      * @param src source address of command
    359      * @param avr destination address of command, it should be AVR
    360      * @param avrPhysicalAddress physical address of AVR
    361      * @param enableSystemAudio whether to enable System Audio Mode or not
    362      * @return newly created {@link HdmiCecMessage}
    363      */
    364     static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress,
    365             boolean enableSystemAudio) {
    366         if (enableSystemAudio) {
    367             return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
    368                     physicalAddressToParam(avrPhysicalAddress));
    369         } else {
    370             return buildCommand(src, avr, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST);
    371         }
    372     }
    373 
    374     /**
    375      * Build &lt;Give Audio Status&gt; command.
    376      *
    377      * @param src source address of command
    378      * @param dest destination address of command
    379      * @return newly created {@link HdmiCecMessage}
    380      */
    381     static HdmiCecMessage buildGiveAudioStatus(int src, int dest) {
    382         return buildCommand(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS);
    383     }
    384 
    385     /**
    386      * Build &lt;User Control Pressed&gt; command.
    387      *
    388      * @param src source address of command
    389      * @param dest destination address of command
    390      * @param uiCommand keycode that user pressed
    391      * @return newly created {@link HdmiCecMessage}
    392      */
    393     static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
    394         return buildUserControlPressed(src, dest, new byte[] { (byte) (uiCommand & 0xFF) });
    395     }
    396 
    397     /**
    398      * Build &lt;User Control Pressed&gt; command.
    399      *
    400      * @param src source address of command
    401      * @param dest destination address of command
    402      * @param commandParam uiCommand and the additional parameter
    403      * @return newly created {@link HdmiCecMessage}
    404      */
    405     static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) {
    406         return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_PRESSED, commandParam);
    407     }
    408 
    409     /**
    410      * Build &lt;User Control Released&gt; command.
    411      *
    412      * @param src source address of command
    413      * @param dest destination address of command
    414      * @return newly created {@link HdmiCecMessage}
    415      */
    416     static HdmiCecMessage buildUserControlReleased(int src, int dest) {
    417         return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED);
    418     }
    419 
    420     /**
    421      * Build &lt;Give System Audio Mode Status&gt; command.
    422      *
    423      * @param src source address of command
    424      * @param dest destination address of command
    425      * @return newly created {@link HdmiCecMessage}
    426      */
    427     static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
    428         return buildCommand(src, dest, Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
    429     }
    430 
    431     /**
    432      * Build &lt;Standby&gt; command.
    433      *
    434      * @param src source address of command
    435      * @param dest destination address of command
    436      * @return newly created {@link HdmiCecMessage}
    437      */
    438     public static HdmiCecMessage buildStandby(int src, int dest) {
    439         return buildCommand(src, dest, Constants.MESSAGE_STANDBY);
    440     }
    441 
    442     /**
    443      * Build &lt;Vendor Command&gt; command.
    444      *
    445      * @param src source address of command
    446      * @param dest destination address of command
    447      * @param params vendor-specific parameters
    448      * @return newly created {@link HdmiCecMessage}
    449      */
    450     static HdmiCecMessage buildVendorCommand(int src, int dest, byte[] params) {
    451         return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND, params);
    452     }
    453 
    454     /**
    455      * Build &lt;Vendor Command With ID&gt; command.
    456      *
    457      * @param src source address of command
    458      * @param dest destination address of command
    459      * @param vendorId vendor ID
    460      * @param operands vendor-specific parameters
    461      * @return newly created {@link HdmiCecMessage}
    462      */
    463     static HdmiCecMessage buildVendorCommandWithId(int src, int dest, int vendorId,
    464             byte[] operands) {
    465         byte[] params = new byte[operands.length + 3];  // parameter plus len(vendorId)
    466         params[0] = (byte) ((vendorId >> 16) & 0xFF);
    467         params[1] = (byte) ((vendorId >> 8) & 0xFF);
    468         params[2] = (byte) (vendorId & 0xFF);
    469         System.arraycopy(operands, 0, params, 3, operands.length);
    470         return buildCommand(src, dest, Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, params);
    471     }
    472 
    473     /**
    474      * Build &lt;Record On&gt; command.
    475      *
    476      * @param src source address of command
    477      * @param dest destination address of command
    478      * @param params parameter of command
    479      * @return newly created {@link HdmiCecMessage}
    480      */
    481     static HdmiCecMessage buildRecordOn(int src, int dest, byte[] params) {
    482         return buildCommand(src, dest, Constants.MESSAGE_RECORD_ON, params);
    483     }
    484 
    485     /**
    486      * Build &lt;Record Off&gt; command.
    487      *
    488      * @param src source address of command
    489      * @param dest destination address of command
    490      * @return newly created {@link HdmiCecMessage}
    491      */
    492     static HdmiCecMessage buildRecordOff(int src, int dest) {
    493         return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF);
    494     }
    495 
    496     /**
    497      * Build &lt;Set Digital Timer&gt; command.
    498      *
    499      * @param src source address of command
    500      * @param dest destination address of command
    501      * @param params byte array of timing information and digital service information to be recorded
    502      * @return newly created {@link HdmiCecMessage}
    503      */
    504     static HdmiCecMessage buildSetDigitalTimer(int src, int dest, byte[] params) {
    505         return buildCommand(src, dest, Constants.MESSAGE_SET_DIGITAL_TIMER, params);
    506     }
    507 
    508     /**
    509      * Build &lt;Set Analogue Timer&gt; command.
    510      *
    511      * @param src source address of command
    512      * @param dest destination address of command
    513      * @param params byte array of timing information and analog service information to be recorded
    514      * @return newly created {@link HdmiCecMessage}
    515      */
    516     static HdmiCecMessage buildSetAnalogueTimer(int src, int dest, byte[] params) {
    517         return buildCommand(src, dest, Constants.MESSAGE_SET_ANALOG_TIMER, params);
    518     }
    519 
    520     /**
    521      * Build &lt;Set External Timer&gt; command.
    522      *
    523      * @param src source address of command
    524      * @param dest destination address of command
    525      * @param params byte array of timing information and external source information to be recorded
    526      * @return newly created {@link HdmiCecMessage}
    527      */
    528     static HdmiCecMessage buildSetExternalTimer(int src, int dest, byte[] params) {
    529         return buildCommand(src, dest, Constants.MESSAGE_SET_EXTERNAL_TIMER, params);
    530     }
    531 
    532     /**
    533      * Build &lt;Clear Digital Timer&gt; command.
    534      *
    535      * @param src source address of command
    536      * @param dest destination address of command
    537      * @param params byte array of timing information and digital service information to be cleared
    538      * @return newly created {@link HdmiCecMessage}
    539      */
    540     static HdmiCecMessage buildClearDigitalTimer(int src, int dest, byte[] params) {
    541         return buildCommand(src, dest, Constants.MESSAGE_CLEAR_DIGITAL_TIMER, params);
    542     }
    543 
    544     /**
    545      * Build &lt;Clear Analog Timer&gt; command.
    546      *
    547      * @param src source address of command
    548      * @param dest destination address of command
    549      * @param params byte array of timing information and analog service information to be cleared
    550      * @return newly created {@link HdmiCecMessage}
    551      */
    552     static HdmiCecMessage buildClearAnalogueTimer(int src, int dest, byte[] params) {
    553         return buildCommand(src, dest, Constants.MESSAGE_CLEAR_ANALOG_TIMER, params);
    554     }
    555 
    556     /**
    557      * Build &lt;Clear Digital Timer&gt; command.
    558      *
    559      * @param src source address of command
    560      * @param dest destination address of command
    561      * @param params byte array of timing information and external source information to be cleared
    562      * @return newly created {@link HdmiCecMessage}
    563      */
    564     static HdmiCecMessage buildClearExternalTimer(int src, int dest, byte[] params) {
    565         return buildCommand(src, dest, Constants.MESSAGE_CLEAR_EXTERNAL_TIMER, params);
    566     }
    567 
    568     /***** Please ADD new buildXXX() methods above. ******/
    569 
    570     /**
    571      * Build a {@link HdmiCecMessage} without extra parameter.
    572      *
    573      * @param src source address of command
    574      * @param dest destination address of command
    575      * @param opcode opcode for a message
    576      * @return newly created {@link HdmiCecMessage}
    577      */
    578     private static HdmiCecMessage buildCommand(int src, int dest, int opcode) {
    579         return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM);
    580     }
    581 
    582     /**
    583      * Build a {@link HdmiCecMessage} with given values.
    584      *
    585      * @param src source address of command
    586      * @param dest destination address of command
    587      * @param opcode opcode for a message
    588      * @param params extra parameters for command
    589      * @return newly created {@link HdmiCecMessage}
    590      */
    591     private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) {
    592         return new HdmiCecMessage(src, dest, opcode, params);
    593     }
    594 
    595     private static byte[] physicalAddressToParam(int physicalAddress) {
    596         return new byte[] {
    597                 (byte) ((physicalAddress >> 8) & 0xFF),
    598                 (byte) (physicalAddress & 0xFF)
    599         };
    600     }
    601 }
    602