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 HdmiCecMessageBuilder()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 */ of(int src, int dest, byte[] body)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 */ buildFeatureAbortCommand(int src, int dest, int originalOpcode, int reason)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 */ buildGivePhysicalAddress(int src, int dest)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 */ buildGiveOsdNameCommand(int src, int dest)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 */ buildGiveDeviceVendorIdCommand(int src, int dest)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 */ buildSetMenuLanguageCommand(int src, String language)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 <Set Osd Name > 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 */ buildSetOsdNameCommand(int src, int dest, String name)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 <Report Physical Address> 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 */ buildReportPhysicalAddressCommand(int src, int address, int deviceType)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 <Device Vendor Id> 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 */ buildDeviceVendorIdCommand(int src, int vendorId)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 <Device Vendor Id> 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 */ buildCecVersion(int src, int dest, int version)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 <Request Arc Initiation> 204 * 205 * @param src source address of command 206 * @param dest destination address of command 207 * @return newly created {@link HdmiCecMessage} 208 */ buildRequestArcInitiation(int src, int dest)209 static HdmiCecMessage buildRequestArcInitiation(int src, int dest) { 210 return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_INITIATION); 211 } 212 213 /** 214 * Build <Request Arc Termination> 215 * 216 * @param src source address of command 217 * @param dest destination address of command 218 * @return newly created {@link HdmiCecMessage} 219 */ buildRequestArcTermination(int src, int dest)220 static HdmiCecMessage buildRequestArcTermination(int src, int dest) { 221 return buildCommand(src, dest, Constants.MESSAGE_REQUEST_ARC_TERMINATION); 222 } 223 224 /** 225 * Build <Report Arc Initiated> 226 * 227 * @param src source address of command 228 * @param dest destination address of command 229 * @return newly created {@link HdmiCecMessage} 230 */ buildReportArcInitiated(int src, int dest)231 static HdmiCecMessage buildReportArcInitiated(int src, int dest) { 232 return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_INITIATED); 233 } 234 235 /** 236 * Build <Report Arc Terminated> 237 * 238 * @param src source address of command 239 * @param dest destination address of command 240 * @return newly created {@link HdmiCecMessage} 241 */ buildReportArcTerminated(int src, int dest)242 static HdmiCecMessage buildReportArcTerminated(int src, int dest) { 243 return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED); 244 } 245 246 /** 247 * Build <Text View On> command. 248 * 249 * @param src source address of command 250 * @param dest destination address of command 251 * @return newly created {@link HdmiCecMessage} 252 */ buildTextViewOn(int src, int dest)253 static HdmiCecMessage buildTextViewOn(int src, int dest) { 254 return buildCommand(src, dest, Constants.MESSAGE_TEXT_VIEW_ON); 255 } 256 257 /** 258 * Build <Active Source> 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 */ buildActiveSource(int src, int physicalAddress)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 <Inactive Source> 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 */ buildInactiveSource(int src, int physicalAddress)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 <Set Stream Path> 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 */ buildSetStreamPath(int src, int streamPath)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 <Routing Change> 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 */ buildRoutingChange(int src, int oldPath, int newPath)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 <Give Device Power Status> command. 316 * 317 * @param src source address of command 318 * @param dest destination address of command 319 * @return newly created {@link HdmiCecMessage} 320 */ buildGiveDevicePowerStatus(int src, int dest)321 static HdmiCecMessage buildGiveDevicePowerStatus(int src, int dest) { 322 return buildCommand(src, dest, Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS); 323 } 324 325 /** 326 * Build <Report Power Status> 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 */ buildReportPowerStatus(int src, int dest, int powerStatus)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 <Report Menu Status> 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 */ buildReportMenuStatus(int src, int dest, int menuStatus)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 <System Audio Mode Request> 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 */ buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress, boolean enableSystemAudio)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 <Give Audio Status> command. 376 * 377 * @param src source address of command 378 * @param dest destination address of command 379 * @return newly created {@link HdmiCecMessage} 380 */ buildGiveAudioStatus(int src, int dest)381 static HdmiCecMessage buildGiveAudioStatus(int src, int dest) { 382 return buildCommand(src, dest, Constants.MESSAGE_GIVE_AUDIO_STATUS); 383 } 384 385 /** 386 * Build <User Control Pressed> 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 */ buildUserControlPressed(int src, int dest, int uiCommand)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 <User Control Pressed> 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 */ buildUserControlPressed(int src, int dest, byte[] commandParam)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 <User Control Released> command. 411 * 412 * @param src source address of command 413 * @param dest destination address of command 414 * @return newly created {@link HdmiCecMessage} 415 */ buildUserControlReleased(int src, int dest)416 static HdmiCecMessage buildUserControlReleased(int src, int dest) { 417 return buildCommand(src, dest, Constants.MESSAGE_USER_CONTROL_RELEASED); 418 } 419 420 /** 421 * Build <Give System Audio Mode Status> command. 422 * 423 * @param src source address of command 424 * @param dest destination address of command 425 * @return newly created {@link HdmiCecMessage} 426 */ buildGiveSystemAudioModeStatus(int src, int dest)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 <Standby> command. 433 * 434 * @param src source address of command 435 * @param dest destination address of command 436 * @return newly created {@link HdmiCecMessage} 437 */ buildStandby(int src, int dest)438 public static HdmiCecMessage buildStandby(int src, int dest) { 439 return buildCommand(src, dest, Constants.MESSAGE_STANDBY); 440 } 441 442 /** 443 * Build <Vendor Command> 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 */ buildVendorCommand(int src, int dest, byte[] params)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 <Vendor Command With ID> 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 */ buildVendorCommandWithId(int src, int dest, int vendorId, byte[] operands)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 <Record On> 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 */ buildRecordOn(int src, int dest, byte[] params)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 <Record Off> command. 487 * 488 * @param src source address of command 489 * @param dest destination address of command 490 * @return newly created {@link HdmiCecMessage} 491 */ buildRecordOff(int src, int dest)492 static HdmiCecMessage buildRecordOff(int src, int dest) { 493 return buildCommand(src, dest, Constants.MESSAGE_RECORD_OFF); 494 } 495 496 /** 497 * Build <Set Digital Timer> 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 */ buildSetDigitalTimer(int src, int dest, byte[] params)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 <Set Analogue Timer> 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 */ buildSetAnalogueTimer(int src, int dest, byte[] params)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 <Set External Timer> 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 */ buildSetExternalTimer(int src, int dest, byte[] params)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 <Clear Digital Timer> 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 */ buildClearDigitalTimer(int src, int dest, byte[] params)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 <Clear Analog Timer> 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 */ buildClearAnalogueTimer(int src, int dest, byte[] params)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 <Clear Digital Timer> 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 */ buildClearExternalTimer(int src, int dest, byte[] params)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 */ buildCommand(int src, int dest, int opcode)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 */ buildCommand(int src, int dest, int opcode, byte[] params)591 private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) { 592 return new HdmiCecMessage(src, dest, opcode, params); 593 } 594 physicalAddressToParam(int physicalAddress)595 private static byte[] physicalAddressToParam(int physicalAddress) { 596 return new byte[] { 597 (byte) ((physicalAddress >> 8) & 0xFF), 598 (byte) (physicalAddress & 0xFF) 599 }; 600 } 601 } 602