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 android.hardware.hdmi; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresFeature; 24 import android.annotation.RequiresPermission; 25 import android.annotation.SdkConstant; 26 import android.annotation.SdkConstant.SdkConstantType; 27 import android.annotation.StringDef; 28 import android.annotation.SuppressLint; 29 import android.annotation.SystemApi; 30 import android.annotation.SystemService; 31 import android.content.Context; 32 import android.content.pm.PackageManager; 33 import android.os.Binder; 34 import android.os.RemoteException; 35 import android.sysprop.HdmiProperties; 36 import android.util.ArrayMap; 37 import android.util.Log; 38 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.util.ConcurrentUtils; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.List; 47 import java.util.Objects; 48 import java.util.concurrent.Executor; 49 import java.util.stream.Collectors; 50 51 /** 52 * The {@link HdmiControlManager} class is used to send HDMI control messages 53 * to attached CEC devices. 54 * 55 * <p>Provides various HDMI client instances that represent HDMI-CEC logical devices 56 * hosted in the system. {@link #getTvClient()}, for instance will return an 57 * {@link HdmiTvClient} object if the system is configured to host one. Android system 58 * can host more than one logical CEC devices. If multiple types are configured they 59 * all work as if they were independent logical devices running in the system. 60 * 61 * @hide 62 */ 63 @SystemApi 64 @SystemService(Context.HDMI_CONTROL_SERVICE) 65 @RequiresFeature(PackageManager.FEATURE_HDMI_CEC) 66 public final class HdmiControlManager { 67 private static final String TAG = "HdmiControlManager"; 68 69 @Nullable private final IHdmiControlService mService; 70 71 private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; 72 73 /** 74 * A cache of the current device's physical address. When device's HDMI out port 75 * is not connected to any device, it is set to {@link #INVALID_PHYSICAL_ADDRESS}. 76 * 77 * <p>Otherwise it is updated by the {@link ClientHotplugEventListener} registered 78 * with {@link com.android.server.hdmi.HdmiControlService} by the 79 * {@link #addHotplugEventListener(HotplugEventListener)} and the address is from 80 * {@link com.android.server.hdmi.HdmiControlService#getPortInfo()} 81 */ 82 @GuardedBy("mLock") 83 private int mLocalPhysicalAddress = INVALID_PHYSICAL_ADDRESS; 84 setLocalPhysicalAddress(int physicalAddress)85 private void setLocalPhysicalAddress(int physicalAddress) { 86 synchronized (mLock) { 87 mLocalPhysicalAddress = physicalAddress; 88 } 89 } 90 getLocalPhysicalAddress()91 private int getLocalPhysicalAddress() { 92 synchronized (mLock) { 93 return mLocalPhysicalAddress; 94 } 95 } 96 97 private final Object mLock = new Object(); 98 99 /** 100 * Broadcast Action: Display OSD message. 101 * <p>Send when the service has a message to display on screen for events 102 * that need user's attention such as ARC status change. 103 * <p>Always contains the extra fields {@link #EXTRA_MESSAGE_ID}. 104 * <p>Requires {@link android.Manifest.permission#HDMI_CEC} to receive. 105 */ 106 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 107 public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE"; 108 109 // --- Messages for ACTION_OSD_MESSAGE --- 110 /** 111 * Message that ARC enabled device is connected to invalid port (non-ARC port). 112 */ 113 public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; 114 115 /** 116 * Message used by TV to receive volume status from Audio Receiver. It should check volume value 117 * that is retrieved from extra value with the key {@link #EXTRA_MESSAGE_EXTRA_PARAM1}. If the 118 * value is in range of [0,100], it is current volume of Audio Receiver. And there is another 119 * value, {@link #AVR_VOLUME_MUTED}, which is used to inform volume mute. 120 */ 121 public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; 122 123 /** 124 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of 125 * the message to display on screen. 126 */ 127 public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID"; 128 /** 129 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the extra value 130 * of the message. 131 */ 132 public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = 133 "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1"; 134 135 /** 136 * Volume value for mute state. 137 */ 138 public static final int AVR_VOLUME_MUTED = 101; 139 140 public static final int POWER_STATUS_UNKNOWN = -1; 141 public static final int POWER_STATUS_ON = 0; 142 public static final int POWER_STATUS_STANDBY = 1; 143 public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; 144 public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; 145 146 /** @hide */ 147 @SystemApi 148 @IntDef ({ 149 RESULT_SUCCESS, 150 RESULT_TIMEOUT, 151 RESULT_SOURCE_NOT_AVAILABLE, 152 RESULT_TARGET_NOT_AVAILABLE, 153 RESULT_ALREADY_IN_PROGRESS, 154 RESULT_EXCEPTION, 155 RESULT_INCORRECT_MODE, 156 RESULT_COMMUNICATION_FAILED, 157 }) 158 public @interface ControlCallbackResult {} 159 160 /** Control operation is successfully handled by the framework. */ 161 public static final int RESULT_SUCCESS = 0; 162 public static final int RESULT_TIMEOUT = 1; 163 /** Source device that the application is using is not available. */ 164 public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; 165 /** Target device that the application is controlling is not available. */ 166 public static final int RESULT_TARGET_NOT_AVAILABLE = 3; 167 168 @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; 169 public static final int RESULT_EXCEPTION = 5; 170 public static final int RESULT_INCORRECT_MODE = 6; 171 public static final int RESULT_COMMUNICATION_FAILED = 7; 172 173 public static final int DEVICE_EVENT_ADD_DEVICE = 1; 174 public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; 175 public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; 176 177 // --- One Touch Recording success result 178 /** Recording currently selected source. Indicates the status of a recording. */ 179 public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x01; 180 /** Recording Digital Service. Indicates the status of a recording. */ 181 public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 0x02; 182 /** Recording Analogue Service. Indicates the status of a recording. */ 183 public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 0x03; 184 /** Recording External input. Indicates the status of a recording. */ 185 public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 0x04; 186 187 // --- One Touch Record failure result 188 /** No recording – unable to record Digital Service. No suitable tuner. */ 189 public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 0x05; 190 /** No recording – unable to record Analogue Service. No suitable tuner. */ 191 public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 0x06; 192 /** 193 * No recording – unable to select required service. as suitable tuner, but the requested 194 * parameters are invalid or out of range for that tuner. 195 */ 196 public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 0x07; 197 /** No recording – invalid External plug number */ 198 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 0x09; 199 /** No recording – invalid External Physical Address */ 200 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x0A; 201 /** No recording – CA system not supported */ 202 public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 0x0B; 203 /** No Recording – No or Insufficient CA Entitlements” */ 204 public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x0C; 205 /** No recording – Not allowed to copy source. Source is “copy never”. */ 206 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 0x0D; 207 /** No recording – No further copies allowed */ 208 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 0x0E; 209 /** No recording – No media */ 210 public static final int ONE_TOUCH_RECORD_NO_MEDIA = 0x10; 211 /** No recording – playing */ 212 public static final int ONE_TOUCH_RECORD_PLAYING = 0x11; 213 /** No recording – already recording */ 214 public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 0x12; 215 /** No recording – media protected */ 216 public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 0x13; 217 /** No recording – no source signal */ 218 public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 0x14; 219 /** No recording – media problem */ 220 public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 0x15; 221 /** No recording – not enough space available */ 222 public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 0x16; 223 /** No recording – Parental Lock On */ 224 public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 0x17; 225 /** Recording terminated normally */ 226 public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 0x1A; 227 /** Recording has already terminated */ 228 public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 0x1B; 229 /** No recording – other reason */ 230 public static final int ONE_TOUCH_RECORD_OTHER_REASON = 0x1F; 231 // From here extra message for recording that is not mentioned in CEC spec 232 /** No recording. Previous recording request in progress. */ 233 public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 0x30; 234 /** No recording. Please check recorder and connection. */ 235 public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 0x31; 236 /** Cannot record currently displayed source. */ 237 public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x32; 238 /** CEC is disabled. */ 239 public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 0x33; 240 241 // --- Types for timer recording 242 /** Timer recording type for digital service source. */ 243 public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; 244 /** Timer recording type for analogue service source. */ 245 public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; 246 /** Timer recording type for external source. */ 247 public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; 248 249 // --- Timer Status Data 250 /** [Timer Status Data/Media Info] - Media present and not protected. */ 251 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0x0; 252 /** [Timer Status Data/Media Info] - Media present, but protected. */ 253 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 0x1; 254 /** [Timer Status Data/Media Info] - Media not present. */ 255 public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 0x2; 256 257 /** [Timer Status Data/Programmed Info] - Enough space available for recording. */ 258 public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 0x8; 259 /** [Timer Status Data/Programmed Info] - Not enough space available for recording. */ 260 public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 0x9; 261 /** [Timer Status Data/Programmed Info] - Might not enough space available for recording. */ 262 public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 0xB; 263 /** [Timer Status Data/Programmed Info] - No media info available. */ 264 public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 0xA; 265 266 /** [Timer Status Data/Not Programmed Error Info] - No free timer available. */ 267 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 0x1; 268 /** [Timer Status Data/Not Programmed Error Info] - Date out of range. */ 269 public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 0x2; 270 /** [Timer Status Data/Not Programmed Error Info] - Recording Sequence error. */ 271 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 0x3; 272 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Plug Number. */ 273 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 0x4; 274 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Physical Address. */ 275 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 0x5; 276 /** [Timer Status Data/Not Programmed Error Info] - CA system not supported. */ 277 public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 0x6; 278 /** [Timer Status Data/Not Programmed Error Info] - No or insufficient CA Entitlements. */ 279 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 0x7; 280 /** [Timer Status Data/Not Programmed Error Info] - Does not support resolution. */ 281 public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 0x8; 282 /** [Timer Status Data/Not Programmed Error Info] - Parental Lock On. */ 283 public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON= 0x9; 284 /** [Timer Status Data/Not Programmed Error Info] - Clock Failure. */ 285 public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 0xA; 286 /** [Timer Status Data/Not Programmed Error Info] - Duplicate: already programmed. */ 287 public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 0xE; 288 289 // --- Extra result value for timer recording. 290 /** No extra error. */ 291 public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0x00; 292 /** No timer recording - check recorder and connection. */ 293 public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 0x01; 294 /** No timer recording - cannot record selected source. */ 295 public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 0x02; 296 /** CEC is disabled. */ 297 public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 0x03; 298 299 // -- Timer cleared status data code used for result of onClearTimerRecordingResult. 300 /** Timer not cleared – recording. */ 301 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0x00; 302 /** Timer not cleared – no matching. */ 303 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 0x01; 304 /** Timer not cleared – no info available. */ 305 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 0x02; 306 /** Timer cleared. */ 307 public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 0x80; 308 /** Clear timer error - check recorder and connection. */ 309 public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 0xA0; 310 /** Clear timer error - cannot clear timer for selected source. */ 311 public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 0xA1; 312 /** Clear timer error - CEC is disabled. */ 313 public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 0xA2; 314 315 /** The HdmiControlService is started. */ 316 public static final int CONTROL_STATE_CHANGED_REASON_START = 0; 317 /** The state of HdmiControlService is changed by changing of settings. */ 318 public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; 319 /** The HdmiControlService is enabled to wake up. */ 320 public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; 321 /** The HdmiControlService will be disabled to standby. */ 322 public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; 323 324 // -- Whether the HDMI CEC is enabled or disabled. 325 /** 326 * HDMI CEC enabled. 327 * 328 * @hide 329 */ 330 @SystemApi 331 public static final int HDMI_CEC_CONTROL_ENABLED = 1; 332 /** 333 * HDMI CEC disabled. 334 * 335 * @hide 336 */ 337 @SystemApi 338 public static final int HDMI_CEC_CONTROL_DISABLED = 0; 339 /** 340 * @hide 341 */ 342 @IntDef(prefix = { "HDMI_CEC_CONTROL_" }, value = { 343 HDMI_CEC_CONTROL_ENABLED, 344 HDMI_CEC_CONTROL_DISABLED 345 }) 346 @Retention(RetentionPolicy.SOURCE) 347 public @interface HdmiCecControl {} 348 349 // -- Supported HDM-CEC versions. 350 /** 351 * Version constant for HDMI-CEC v1.4b. 352 * 353 * @hide 354 */ 355 @SystemApi 356 public static final int HDMI_CEC_VERSION_1_4_B = 0x05; 357 /** 358 * Version constant for HDMI-CEC v2.0. 359 * 360 * @hide 361 */ 362 @SystemApi 363 public static final int HDMI_CEC_VERSION_2_0 = 0x06; 364 /** 365 * @hide 366 */ 367 @IntDef(prefix = { "HDMI_CEC_VERSION_" }, value = { 368 HDMI_CEC_VERSION_1_4_B, 369 HDMI_CEC_VERSION_2_0 370 }) 371 @Retention(RetentionPolicy.SOURCE) 372 public @interface HdmiCecVersion {} 373 374 // -- Scope of CEC power control messages sent by a playback device. 375 /** 376 * Send CEC power control messages to TV only. 377 * 378 * @hide 379 */ 380 @SystemApi 381 public static final String POWER_CONTROL_MODE_TV = "to_tv"; 382 /** 383 * Broadcast CEC power control messages to all devices in the network. 384 * 385 * @hide 386 */ 387 @SystemApi 388 public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast"; 389 /** 390 * Don't send any CEC power control messages. 391 * 392 * @hide 393 */ 394 @SystemApi 395 public static final String POWER_CONTROL_MODE_NONE = "none"; 396 /** 397 * @hide 398 */ 399 @StringDef(prefix = { "POWER_CONTROL_MODE_" }, value = { 400 POWER_CONTROL_MODE_TV, 401 POWER_CONTROL_MODE_BROADCAST, 402 POWER_CONTROL_MODE_NONE 403 }) 404 @Retention(RetentionPolicy.SOURCE) 405 public @interface PowerControlMode {} 406 407 // -- Which power state action should be taken when Active Source is lost. 408 /** 409 * No action to be taken. 410 * 411 * @hide 412 */ 413 @SystemApi 414 public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none"; 415 /** 416 * Go to standby immediately. 417 * 418 * @hide 419 */ 420 @SystemApi 421 public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now"; 422 /** 423 * @hide 424 */ 425 @StringDef(prefix = { "POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_" }, value = { 426 POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE, 427 POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW 428 }) 429 @Retention(RetentionPolicy.SOURCE) 430 public @interface ActiveSourceLostBehavior {} 431 432 // -- Whether System Audio Mode muting is enabled or disabled. 433 /** 434 * System Audio Mode muting enabled. 435 * 436 * @hide 437 */ 438 @SystemApi 439 public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1; 440 /** 441 * System Audio Mode muting disabled. 442 * 443 * @hide 444 */ 445 @SystemApi 446 public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0; 447 /** 448 * @hide 449 */ 450 @IntDef(prefix = { "SYSTEM_AUDIO_MODE_MUTING_" }, value = { 451 SYSTEM_AUDIO_MODE_MUTING_ENABLED, 452 SYSTEM_AUDIO_MODE_MUTING_DISABLED 453 }) 454 @Retention(RetentionPolicy.SOURCE) 455 public @interface SystemAudioModeMuting {} 456 457 // -- Whether the HDMI CEC volume control is enabled or disabled. 458 /** 459 * HDMI CEC enabled. 460 * 461 * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE 462 * @hide 463 */ 464 @SystemApi 465 public static final int VOLUME_CONTROL_ENABLED = 1; 466 /** 467 * HDMI CEC disabled. 468 * 469 * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE 470 * @hide 471 */ 472 @SystemApi 473 public static final int VOLUME_CONTROL_DISABLED = 0; 474 /** 475 * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE 476 * @hide 477 */ 478 @IntDef(prefix = { "VOLUME_CONTROL_" }, value = { 479 VOLUME_CONTROL_ENABLED, 480 VOLUME_CONTROL_DISABLED 481 }) 482 @Retention(RetentionPolicy.SOURCE) 483 public @interface VolumeControl {} 484 485 // -- Whether TV Wake on One Touch Play is enabled or disabled. 486 /** 487 * TV Wake on One Touch Play enabled. 488 * 489 * @hide 490 */ 491 @SystemApi 492 public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED = 1; 493 /** 494 * TV Wake on One Touch Play disabled. 495 * 496 * @hide 497 */ 498 @SystemApi 499 public static final int TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED = 0; 500 /** 501 * @hide 502 */ 503 @IntDef(prefix = { "TV_WAKE_ON_ONE_TOUCH_PLAY_" }, value = { 504 TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED, 505 TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED 506 }) 507 @Retention(RetentionPolicy.SOURCE) 508 public @interface TvWakeOnOneTouchPlay {} 509 510 // -- Whether TV should send <Standby> on sleep. 511 /** 512 * Sending <Standby> on sleep. 513 * 514 * @hide 515 */ 516 @SystemApi 517 public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1; 518 /** 519 * Not sending <Standby> on sleep. 520 * 521 * @hide 522 */ 523 @SystemApi 524 public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0; 525 /** 526 * @hide 527 */ 528 @IntDef(prefix = { "TV_SEND_STANDBY_ON_SLEEP_" }, value = { 529 TV_SEND_STANDBY_ON_SLEEP_ENABLED, 530 TV_SEND_STANDBY_ON_SLEEP_DISABLED 531 }) 532 @Retention(RetentionPolicy.SOURCE) 533 public @interface TvSendStandbyOnSleep {} 534 535 // -- The RC profile of a TV panel. 536 /** 537 * RC profile none. 538 * 539 * @hide 540 */ 541 public static final int RC_PROFILE_TV_NONE = 0x0; 542 /** 543 * RC profile 1. 544 * 545 * @hide 546 */ 547 public static final int RC_PROFILE_TV_ONE = 0x2; 548 /** 549 * RC profile 2. 550 * 551 * @hide 552 */ 553 public static final int RC_PROFILE_TV_TWO = 0x6; 554 /** 555 * RC profile 3. 556 * 557 * @hide 558 */ 559 public static final int RC_PROFILE_TV_THREE = 0xA; 560 /** 561 * RC profile 4. 562 * 563 * @hide 564 */ 565 public static final int RC_PROFILE_TV_FOUR = 0xE; 566 /** 567 * @hide 568 */ 569 @IntDef(prefix = { "RC_PROFILE_TV_" }, value = { 570 RC_PROFILE_TV_NONE, 571 RC_PROFILE_TV_ONE, 572 RC_PROFILE_TV_TWO, 573 RC_PROFILE_TV_THREE, 574 RC_PROFILE_TV_FOUR 575 }) 576 @Retention(RetentionPolicy.SOURCE) 577 public @interface RcProfileTv {} 578 579 // -- RC profile parameter defining if a source handles the root menu. 580 /** 581 * Handles the root menu. 582 * 583 * @hide 584 */ 585 public static final int RC_PROFILE_SOURCE_ROOT_MENU_HANDLED = 1; 586 /** 587 * Doesn't handle the root menu. 588 * 589 * @hide 590 */ 591 public static final int RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED = 0; 592 /** 593 * @hide 594 */ 595 @IntDef(prefix = { "RC_PROFILE_SOURCE_ROOT_MENU_" }, value = { 596 RC_PROFILE_SOURCE_ROOT_MENU_HANDLED, 597 RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED 598 }) 599 @Retention(RetentionPolicy.SOURCE) 600 public @interface RcProfileSourceHandlesRootMenu {} 601 602 // -- RC profile parameter defining if a source handles the setup menu. 603 /** 604 * Handles the setup menu. 605 * 606 * @hide 607 */ 608 public static final int RC_PROFILE_SOURCE_SETUP_MENU_HANDLED = 1; 609 /** 610 * Doesn't handle the setup menu. 611 * 612 * @hide 613 */ 614 public static final int RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED = 0; 615 /** 616 * @hide 617 */ 618 @IntDef(prefix = { "RC_PROFILE_SOURCE_SETUP_MENU_" }, value = { 619 RC_PROFILE_SOURCE_SETUP_MENU_HANDLED, 620 RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED 621 }) 622 @Retention(RetentionPolicy.SOURCE) 623 public @interface RcProfileSourceHandlesSetupMenu {} 624 625 626 // -- RC profile parameter defining if a source handles the contents menu. 627 /** 628 * Handles the contents menu. 629 * 630 * @hide 631 */ 632 public static final int RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED = 1; 633 /** 634 * Doesn't handle the contents menu. 635 * 636 * @hide 637 */ 638 public static final int RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED = 0; 639 /** 640 * @hide 641 */ 642 @IntDef(prefix = { "RC_PROFILE_SOURCE_CONTENTS_MENU_" }, value = { 643 RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED, 644 RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED 645 }) 646 @Retention(RetentionPolicy.SOURCE) 647 public @interface RcProfileSourceHandlesContentsMenu {} 648 649 650 // -- RC profile parameter defining if a source handles the top menu. 651 /** 652 * Handles the top menu. 653 * 654 * @hide 655 */ 656 public static final int RC_PROFILE_SOURCE_TOP_MENU_HANDLED = 1; 657 /** 658 * Doesn't handle the top menu. 659 * 660 * @hide 661 */ 662 public static final int RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED = 0; 663 /** 664 * @hide 665 */ 666 @IntDef(prefix = { "RC_PROFILE_SOURCE_TOP_MENU_" }, value = { 667 RC_PROFILE_SOURCE_TOP_MENU_HANDLED, 668 RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED 669 }) 670 @Retention(RetentionPolicy.SOURCE) 671 public @interface RcProfileSourceHandlesTopMenu {} 672 673 674 // -- RC profile parameter defining if a source handles the media context sensitive menu. 675 /** 676 * Handles the media context sensitive menu. 677 * 678 * @hide 679 */ 680 public static final int RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED = 1; 681 /** 682 * Doesn't handle the media context sensitive menu. 683 * 684 * @hide 685 */ 686 public static final int RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED = 0; 687 /** 688 * @hide 689 */ 690 @IntDef(prefix = { "RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_" }, value = { 691 RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED, 692 RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED 693 }) 694 @Retention(RetentionPolicy.SOURCE) 695 public @interface RcProfileSourceHandlesMediaContextSensitiveMenu {} 696 697 // -- Settings available in the CEC Configuration. 698 /** 699 * Name of a setting deciding whether the CEC is enabled. 700 * 701 * @hide 702 */ 703 @SystemApi 704 public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled"; 705 /** 706 * Name of a setting controlling the version of HDMI-CEC used. 707 * 708 * @hide 709 */ 710 @SystemApi 711 public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version"; 712 /** 713 * Name of a setting deciding on the power control mode. 714 * 715 * @hide 716 */ 717 @SystemApi 718 public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode"; 719 /** 720 * Name of a setting deciding on power state action when losing Active Source. 721 * 722 * @hide 723 */ 724 @SystemApi 725 public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = 726 "power_state_change_on_active_source_lost"; 727 /** 728 * Name of a setting deciding whether System Audio Muting is allowed. 729 * 730 * @hide 731 */ 732 @SystemApi 733 public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = 734 "system_audio_mode_muting"; 735 /** 736 * Controls whether volume control commands via HDMI CEC are enabled. 737 * 738 * <p>Effects on different device types: 739 * <table> 740 * <tr><th>HDMI CEC device type</th><th>0: disabled</th><th>1: enabled</th></tr> 741 * <tr> 742 * <td>TV (type: 0)</td> 743 * <td>Per CEC specification.</td> 744 * <td>TV changes system volume. TV no longer reacts to incoming volume changes 745 * via {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio 746 * Status>}.</td> 747 * </tr> 748 * <tr> 749 * <td>Playback device (type: 4)</td> 750 * <td>Device sends volume commands to TV/Audio system via {@code <User Control 751 * Pressed>}</td> 752 * <td>Device does not send volume commands via {@code <User Control Pressed>}.</td> 753 * </tr> 754 * <tr> 755 * <td>Audio device (type: 5)</td> 756 * <td>Full "System Audio Control" capabilities.</td> 757 * <td>Audio device no longer reacts to incoming {@code <User Control Pressed>} 758 * volume commands. Audio device no longer reports volume changes via {@code 759 * <Report Audio Status>}.</td> 760 * </tr> 761 * </table> 762 * 763 * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged. 764 * 765 * @hide 766 * @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(int) 767 */ 768 @SystemApi 769 public static final String CEC_SETTING_NAME_VOLUME_CONTROL_MODE = 770 "volume_control_enabled"; 771 /** 772 * Name of a setting deciding whether the TV will automatically turn on upon reception 773 * of the CEC command <Text View On> or <Image View On>. 774 * 775 * @hide 776 */ 777 @SystemApi 778 public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = 779 "tv_wake_on_one_touch_play"; 780 /** 781 * Name of a setting deciding whether the device will also turn off other CEC devices 782 * when it goes to standby mode. 783 * 784 * @hide 785 */ 786 @SystemApi 787 public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = 788 "tv_send_standby_on_sleep"; 789 /** 790 * Name of a setting representing the RC profile of a TV panel. 791 * 792 * @hide 793 */ 794 public static final String CEC_SETTING_NAME_RC_PROFILE_TV = 795 "rc_profile_tv"; 796 /** 797 * Name of a setting representing the RC profile parameter defining if a source handles the root 798 * menu. 799 * 800 * @hide 801 */ 802 public static final String CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU = 803 "rc_profile_source_handles_root_menu"; 804 /** 805 * Name of a setting representing the RC profile parameter defining if a source handles the 806 * setup menu. 807 * 808 * @hide 809 */ 810 public static final String CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU = 811 "rc_profile_source_handles_setup_menu"; 812 /** 813 * Name of a setting representing the RC profile parameter defining if a source handles the 814 * contents menu. 815 * 816 * @hide 817 */ 818 public static final String CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU = 819 "rc_profile_source_handles_contents_menu"; 820 /** 821 * Name of a setting representing the RC profile parameter defining if a source handles the top 822 * menu. 823 * 824 * @hide 825 */ 826 public static final String CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU = 827 "rc_profile_source_handles_top_menu"; 828 /** 829 * Name of a setting representing the RC profile parameter defining if a source handles the 830 * media context sensitive menu. 831 * 832 * @hide 833 */ 834 public static final String 835 CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU = 836 "rc_profile_source_handles_media_context_sensitive_menu"; 837 /** 838 * @hide 839 */ 840 @StringDef(prefix = { "CEC_SETTING_NAME_" }, value = { 841 CEC_SETTING_NAME_HDMI_CEC_ENABLED, 842 CEC_SETTING_NAME_HDMI_CEC_VERSION, 843 CEC_SETTING_NAME_POWER_CONTROL_MODE, 844 CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, 845 CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, 846 CEC_SETTING_NAME_VOLUME_CONTROL_MODE, 847 CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, 848 CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, 849 CEC_SETTING_NAME_RC_PROFILE_TV, 850 CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, 851 CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, 852 CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, 853 CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU, 854 CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU, 855 }) 856 public @interface CecSettingName {} 857 858 // True if we have a logical device of type playback hosted in the system. 859 private final boolean mHasPlaybackDevice; 860 // True if we have a logical device of type TV hosted in the system. 861 private final boolean mHasTvDevice; 862 // True if we have a logical device of type audio system hosted in the system. 863 private final boolean mHasAudioSystemDevice; 864 // True if we have a logical device of type audio system hosted in the system. 865 private final boolean mHasSwitchDevice; 866 // True if it's a switch device. 867 private final boolean mIsSwitchDevice; 868 869 /** 870 * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService, 871 * which is a system private class. The right way to create an instance of this class is 872 * using the factory Context.getSystemService. 873 */ HdmiControlManager(IHdmiControlService service)874 public HdmiControlManager(IHdmiControlService service) { 875 mService = service; 876 int[] types = null; 877 if (mService != null) { 878 try { 879 types = mService.getSupportedTypes(); 880 } catch (RemoteException e) { 881 throw e.rethrowFromSystemServer(); 882 } 883 } 884 mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV); 885 mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK); 886 mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); 887 mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH); 888 mIsSwitchDevice = HdmiProperties.is_switch().orElse(false); 889 addHotplugEventListener(new ClientHotplugEventListener()); 890 } 891 892 private final class ClientHotplugEventListener implements HotplugEventListener { 893 894 @Override onReceived(HdmiHotplugEvent event)895 public void onReceived(HdmiHotplugEvent event) { 896 List<HdmiPortInfo> ports = new ArrayList<>(); 897 try { 898 ports = mService.getPortInfo(); 899 } catch (RemoteException e) { 900 throw e.rethrowFromSystemServer(); 901 } 902 if (ports.isEmpty()) { 903 Log.e(TAG, "Can't find port info, not updating connected status. " 904 + "Hotplug event:" + event); 905 return; 906 } 907 // If the HDMI OUT port is plugged or unplugged, update the mLocalPhysicalAddress 908 for (HdmiPortInfo port : ports) { 909 if (port.getId() == event.getPort()) { 910 if (port.getType() == HdmiPortInfo.PORT_OUTPUT) { 911 setLocalPhysicalAddress( 912 event.isConnected() 913 ? port.getAddress() 914 : INVALID_PHYSICAL_ADDRESS); 915 } 916 break; 917 } 918 } 919 } 920 } 921 hasDeviceType(int[] types, int type)922 private static boolean hasDeviceType(int[] types, int type) { 923 if (types == null) { 924 return false; 925 } 926 for (int t : types) { 927 if (t == type) { 928 return true; 929 } 930 } 931 return false; 932 } 933 934 /** 935 * Gets an object that represents an HDMI-CEC logical device of a specified type. 936 * 937 * @param type CEC device type 938 * @return {@link HdmiClient} instance. {@code null} on failure. 939 * See {@link HdmiDeviceInfo#DEVICE_PLAYBACK} 940 * See {@link HdmiDeviceInfo#DEVICE_TV} 941 * See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} 942 * 943 * @hide 944 */ 945 @Nullable 946 @SystemApi 947 @SuppressLint("RequiresPermission") getClient(int type)948 public HdmiClient getClient(int type) { 949 if (mService == null) { 950 return null; 951 } 952 switch (type) { 953 case HdmiDeviceInfo.DEVICE_TV: 954 return mHasTvDevice ? new HdmiTvClient(mService) : null; 955 case HdmiDeviceInfo.DEVICE_PLAYBACK: 956 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null; 957 case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM: 958 return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null; 959 case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH: 960 return (mHasSwitchDevice || mIsSwitchDevice) 961 ? new HdmiSwitchClient(mService) : null; 962 default: 963 return null; 964 } 965 } 966 967 /** 968 * Gets an object that represents an HDMI-CEC logical device of type playback on the system. 969 * 970 * <p>Used to send HDMI control messages to other devices like TV or audio amplifier through 971 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same 972 * system if the system is configured to host more than one type of HDMI-CEC logical devices. 973 * 974 * @return {@link HdmiPlaybackClient} instance. {@code null} on failure. 975 * 976 * @hide 977 */ 978 @Nullable 979 @SystemApi 980 @SuppressLint("RequiresPermission") getPlaybackClient()981 public HdmiPlaybackClient getPlaybackClient() { 982 return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK); 983 } 984 985 /** 986 * Gets an object that represents an HDMI-CEC logical device of type TV on the system. 987 * 988 * <p>Used to send HDMI control messages to other devices and manage them through 989 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same 990 * system if the system is configured to host more than one type of HDMI-CEC logical devices. 991 * 992 * @return {@link HdmiTvClient} instance. {@code null} on failure. 993 * 994 * @hide 995 */ 996 @Nullable 997 @SystemApi 998 @SuppressLint("RequiresPermission") getTvClient()999 public HdmiTvClient getTvClient() { 1000 return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV); 1001 } 1002 1003 /** 1004 * Gets an object that represents an HDMI-CEC logical device of type audio system on the system. 1005 * 1006 * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also 1007 * possible to communicate with other logical devices hosted in the same system if the system is 1008 * configured to host more than one type of HDMI-CEC logical devices. 1009 * 1010 * @return {@link HdmiAudioSystemClient} instance. {@code null} on failure. 1011 * 1012 * @hide 1013 */ 1014 @Nullable 1015 @SuppressLint("RequiresPermission") getAudioSystemClient()1016 public HdmiAudioSystemClient getAudioSystemClient() { 1017 return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); 1018 } 1019 1020 /** 1021 * Gets an object that represents an HDMI-CEC logical device of type switch on the system. 1022 * 1023 * <p>Used to send HDMI control messages to other devices (e.g. TVs) through HDMI bus. 1024 * It is also possible to communicate with other logical devices hosted in the same 1025 * system if the system is configured to host more than one type of HDMI-CEC logical device. 1026 * 1027 * @return {@link HdmiSwitchClient} instance. {@code null} on failure. 1028 */ 1029 @Nullable 1030 @SuppressLint("RequiresPermission") getSwitchClient()1031 public HdmiSwitchClient getSwitchClient() { 1032 return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH); 1033 } 1034 1035 /** 1036 * Get a snapshot of the real-time status of the devices on the CEC bus. 1037 * 1038 * <p>This only applies to devices with switch functionality, which are devices with one 1039 * or more than one HDMI inputs. 1040 * 1041 * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices on the CEC bus. An 1042 * empty list will be returned if there is none. 1043 * 1044 * @hide 1045 */ 1046 @NonNull 1047 @SystemApi getConnectedDevices()1048 public List<HdmiDeviceInfo> getConnectedDevices() { 1049 try { 1050 return mService.getDeviceList(); 1051 } catch (RemoteException e) { 1052 throw e.rethrowFromSystemServer(); 1053 } 1054 } 1055 1056 /** 1057 * @removed 1058 * @hide 1059 * @deprecated Please use {@link #getConnectedDevices()} instead. 1060 */ 1061 @Deprecated 1062 @SystemApi getConnectedDevicesList()1063 public List<HdmiDeviceInfo> getConnectedDevicesList() { 1064 try { 1065 return mService.getDeviceList(); 1066 } catch (RemoteException e) { 1067 throw e.rethrowFromSystemServer(); 1068 } 1069 } 1070 1071 /** 1072 * Power off the target device by sending CEC commands. Note that this device can't be the 1073 * current device itself. 1074 * 1075 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. 1076 * 1077 * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered off. 1078 * 1079 * @hide 1080 */ 1081 @SystemApi powerOffDevice(@onNull HdmiDeviceInfo deviceInfo)1082 public void powerOffDevice(@NonNull HdmiDeviceInfo deviceInfo) { 1083 Objects.requireNonNull(deviceInfo); 1084 try { 1085 mService.powerOffRemoteDevice( 1086 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 1087 } catch (RemoteException e) { 1088 throw e.rethrowFromSystemServer(); 1089 } 1090 } 1091 1092 /** 1093 * @removed 1094 * @hide 1095 * @deprecated Please use {@link #powerOffDevice(deviceInfo)} instead. 1096 */ 1097 @Deprecated 1098 @SystemApi powerOffRemoteDevice(@onNull HdmiDeviceInfo deviceInfo)1099 public void powerOffRemoteDevice(@NonNull HdmiDeviceInfo deviceInfo) { 1100 Objects.requireNonNull(deviceInfo); 1101 try { 1102 mService.powerOffRemoteDevice( 1103 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 1104 } catch (RemoteException e) { 1105 throw e.rethrowFromSystemServer(); 1106 } 1107 } 1108 1109 /** 1110 * Power on the target device by sending CEC commands. Note that this device can't be the 1111 * current device itself. 1112 * 1113 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. 1114 * 1115 * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered on. 1116 * 1117 * @hide 1118 */ powerOnDevice(HdmiDeviceInfo deviceInfo)1119 public void powerOnDevice(HdmiDeviceInfo deviceInfo) { 1120 Objects.requireNonNull(deviceInfo); 1121 try { 1122 mService.powerOnRemoteDevice( 1123 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 1124 } catch (RemoteException e) { 1125 throw e.rethrowFromSystemServer(); 1126 } 1127 } 1128 1129 /** 1130 * @removed 1131 * @hide 1132 * @deprecated Please use {@link #powerOnDevice(deviceInfo)} instead. 1133 */ 1134 @Deprecated 1135 @SystemApi powerOnRemoteDevice(HdmiDeviceInfo deviceInfo)1136 public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) { 1137 Objects.requireNonNull(deviceInfo); 1138 try { 1139 mService.powerOnRemoteDevice( 1140 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 1141 } catch (RemoteException e) { 1142 throw e.rethrowFromSystemServer(); 1143 } 1144 } 1145 1146 /** 1147 * Request the target device to be the new Active Source by sending CEC commands. Note that 1148 * this device can't be the current device itself. 1149 * 1150 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. 1151 * 1152 * <p>If the target device responds to the command, the users should see the target device 1153 * streaming on their TVs. 1154 * 1155 * @param deviceInfo HdmiDeviceInfo of the target device 1156 * 1157 * @hide 1158 */ 1159 @SystemApi setActiveSource(@onNull HdmiDeviceInfo deviceInfo)1160 public void setActiveSource(@NonNull HdmiDeviceInfo deviceInfo) { 1161 Objects.requireNonNull(deviceInfo); 1162 try { 1163 mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); 1164 } catch (RemoteException e) { 1165 throw e.rethrowFromSystemServer(); 1166 } 1167 } 1168 1169 /** 1170 * @removed 1171 * @hide 1172 * @deprecated Please use {@link #setActiveSource(deviceInfo)} instead. 1173 */ 1174 @Deprecated 1175 @SystemApi requestRemoteDeviceToBecomeActiveSource(@onNull HdmiDeviceInfo deviceInfo)1176 public void requestRemoteDeviceToBecomeActiveSource(@NonNull HdmiDeviceInfo deviceInfo) { 1177 Objects.requireNonNull(deviceInfo); 1178 try { 1179 mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); 1180 } catch (RemoteException e) { 1181 throw e.rethrowFromSystemServer(); 1182 } 1183 } 1184 1185 /** 1186 * Controls standby mode of the system. It will also try to turn on/off the connected devices if 1187 * necessary. 1188 * 1189 * @param isStandbyModeOn target status of the system's standby mode 1190 */ 1191 @RequiresPermission(android.Manifest.permission.HDMI_CEC) setStandbyMode(boolean isStandbyModeOn)1192 public void setStandbyMode(boolean isStandbyModeOn) { 1193 try { 1194 mService.setStandbyMode(isStandbyModeOn); 1195 } catch (RemoteException e) { 1196 throw e.rethrowFromSystemServer(); 1197 } 1198 } 1199 1200 /** 1201 * For CEC source devices (OTT/STB/Audio system): toggle the power status of the HDMI-connected 1202 * display and follow the display's new power status. 1203 * For all other devices: no functionality. 1204 * 1205 * @hide 1206 */ 1207 @RequiresPermission(android.Manifest.permission.HDMI_CEC) toggleAndFollowTvPower()1208 public void toggleAndFollowTvPower() { 1209 try { 1210 mService.toggleAndFollowTvPower(); 1211 } catch (RemoteException e) { 1212 throw e.rethrowFromSystemServer(); 1213 } 1214 } 1215 1216 /** 1217 * Determines whether the HDMI CEC stack should handle KEYCODE_TV_POWER. 1218 * 1219 * @hide 1220 */ 1221 @RequiresPermission(android.Manifest.permission.HDMI_CEC) shouldHandleTvPowerKey()1222 public boolean shouldHandleTvPowerKey() { 1223 try { 1224 return mService.shouldHandleTvPowerKey(); 1225 } catch (RemoteException e) { 1226 throw e.rethrowFromSystemServer(); 1227 } 1228 } 1229 1230 /** 1231 * Controls whether volume control commands via HDMI CEC are enabled. 1232 * 1233 * <p>When disabled: 1234 * <ul> 1235 * <li>the device will not send any HDMI CEC audio messages 1236 * <li>received HDMI CEC audio messages are responded to with {@code <Feature Abort>} 1237 * </ul> 1238 * 1239 * <p>Effects on different device types: 1240 * <table> 1241 * <tr><th>HDMI CEC device type</th><th>enabled</th><th>disabled</th></tr> 1242 * <tr> 1243 * <td>TV (type: 0)</td> 1244 * <td>Per CEC specification.</td> 1245 * <td>TV changes system volume. TV no longer reacts to incoming volume changes via 1246 * {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio Status>} 1247 * .</td> 1248 * </tr> 1249 * <tr> 1250 * <td>Playback device (type: 4)</td> 1251 * <td>Device sends volume commands to TV/Audio system via {@code <User Control 1252 * Pressed>}</td><td>Device does not send volume commands via {@code <User Control 1253 * Pressed>}.</td> 1254 * </tr> 1255 * <tr> 1256 * <td>Audio device (type: 5)</td> 1257 * <td>Full "System Audio Control" capabilities.</td> 1258 * <td>Audio device no longer reacts to incoming {@code <User Control Pressed>} 1259 * volume commands. Audio device no longer reports volume changes via {@code <Report 1260 * Audio Status>}.</td> 1261 * </tr> 1262 * </table> 1263 * 1264 * <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged. 1265 * 1266 * @param hdmiCecVolumeControlEnabled target state of HDMI CEC volume control. 1267 * @see HdmiControlManager#CEC_SETTING_NAME_VOLUME_CONTROL_MODE 1268 * @hide 1269 */ 1270 @SystemApi 1271 @RequiresPermission(android.Manifest.permission.HDMI_CEC) setHdmiCecVolumeControlEnabled( @olumeControl int hdmiCecVolumeControlEnabled)1272 public void setHdmiCecVolumeControlEnabled( 1273 @VolumeControl int hdmiCecVolumeControlEnabled) { 1274 try { 1275 mService.setCecSettingIntValue(CEC_SETTING_NAME_VOLUME_CONTROL_MODE, 1276 hdmiCecVolumeControlEnabled); 1277 } catch (RemoteException e) { 1278 throw e.rethrowFromSystemServer(); 1279 } 1280 } 1281 1282 /** 1283 * Returns whether volume changes via HDMI CEC are enabled. 1284 * @hide 1285 */ 1286 @SystemApi 1287 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1288 @VolumeControl getHdmiCecVolumeControlEnabled()1289 public int getHdmiCecVolumeControlEnabled() { 1290 try { 1291 return mService.getCecSettingIntValue(CEC_SETTING_NAME_VOLUME_CONTROL_MODE); 1292 } catch (RemoteException e) { 1293 throw e.rethrowFromSystemServer(); 1294 } 1295 } 1296 1297 /** 1298 * Gets whether the system is in system audio mode. 1299 * 1300 * @hide 1301 */ getSystemAudioMode()1302 public boolean getSystemAudioMode() { 1303 try { 1304 return mService.getSystemAudioMode(); 1305 } catch (RemoteException e) { 1306 throw e.rethrowFromSystemServer(); 1307 } 1308 } 1309 1310 /** 1311 * Get the physical address of the device. 1312 * 1313 * <p>Physical address needs to be automatically adjusted when devices are phyiscally or 1314 * electrically added or removed from the device tree. Please see HDMI Specification Version 1315 * 1.4b 8.7 Physical Address for more details on the address discovery proccess. 1316 * 1317 * @hide 1318 */ 1319 @SystemApi getPhysicalAddress()1320 public int getPhysicalAddress() { 1321 return getLocalPhysicalAddress(); 1322 } 1323 1324 /** 1325 * Check if the target device is connected to the current device. 1326 * 1327 * <p>The API also returns true if the current device is the target. 1328 * 1329 * @param targetDevice {@link HdmiDeviceInfo} of the target device. 1330 * @return true if {@code targetDevice} is directly or indirectly 1331 * connected to the current device. 1332 * 1333 * @hide 1334 */ 1335 @SystemApi isDeviceConnected(@onNull HdmiDeviceInfo targetDevice)1336 public boolean isDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) { 1337 Objects.requireNonNull(targetDevice); 1338 int physicalAddress = getLocalPhysicalAddress(); 1339 if (physicalAddress == INVALID_PHYSICAL_ADDRESS) { 1340 return false; 1341 } 1342 int targetPhysicalAddress = targetDevice.getPhysicalAddress(); 1343 if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { 1344 return false; 1345 } 1346 return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, physicalAddress) 1347 != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; 1348 } 1349 1350 /** 1351 * @removed 1352 * @hide 1353 * @deprecated Please use {@link #isDeviceConnected(targetDevice)} instead. 1354 */ 1355 @Deprecated 1356 @SystemApi isRemoteDeviceConnected(@onNull HdmiDeviceInfo targetDevice)1357 public boolean isRemoteDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) { 1358 Objects.requireNonNull(targetDevice); 1359 int physicalAddress = getLocalPhysicalAddress(); 1360 if (physicalAddress == INVALID_PHYSICAL_ADDRESS) { 1361 return false; 1362 } 1363 int targetPhysicalAddress = targetDevice.getPhysicalAddress(); 1364 if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { 1365 return false; 1366 } 1367 return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, physicalAddress) 1368 != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; 1369 } 1370 1371 /** 1372 * Listener used to get hotplug event from HDMI port. 1373 * 1374 * @hide 1375 */ 1376 @SystemApi 1377 public interface HotplugEventListener { onReceived(HdmiHotplugEvent event)1378 void onReceived(HdmiHotplugEvent event); 1379 } 1380 1381 private final ArrayMap<HotplugEventListener, IHdmiHotplugEventListener> 1382 mHotplugEventListeners = new ArrayMap<>(); 1383 1384 /** 1385 * Listener used to get HDMI Control (CEC) status (enabled/disabled) and the connected display 1386 * status. 1387 * @hide 1388 */ 1389 public interface HdmiControlStatusChangeListener { 1390 /** 1391 * Called when HDMI Control (CEC) is enabled/disabled. 1392 * 1393 * @param isCecEnabled status of HDMI Control 1394 * {@link android.provider.Settings.Global#HDMI_CONTROL_ENABLED}: {@code true} if enabled. 1395 * @param isCecAvailable status of CEC support of the connected display (the TV). 1396 * {@code true} if supported. 1397 * 1398 * Note: Value of isCecAvailable is only valid when isCecEnabled is true. 1399 **/ onStatusChange(@dmiControlManager.HdmiCecControl int isCecEnabled, boolean isCecAvailable)1400 void onStatusChange(@HdmiControlManager.HdmiCecControl int isCecEnabled, 1401 boolean isCecAvailable); 1402 } 1403 1404 private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener> 1405 mHdmiControlStatusChangeListeners = new ArrayMap<>(); 1406 1407 /** 1408 * Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled). 1409 * @hide 1410 */ 1411 public interface HdmiCecVolumeControlFeatureListener { 1412 /** 1413 * Called when the HDMI Control (CEC) volume control feature is enabled/disabled. 1414 * 1415 * @param hdmiCecVolumeControl status of HDMI CEC volume control feature 1416 * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(int)} ()} 1417 **/ onHdmiCecVolumeControlFeature(@olumeControl int hdmiCecVolumeControl)1418 void onHdmiCecVolumeControlFeature(@VolumeControl int hdmiCecVolumeControl); 1419 } 1420 1421 private final ArrayMap<HdmiCecVolumeControlFeatureListener, 1422 IHdmiCecVolumeControlFeatureListener> 1423 mHdmiCecVolumeControlFeatureListeners = new ArrayMap<>(); 1424 1425 /** 1426 * Listener used to get vendor-specific commands. 1427 * 1428 * @hide 1429 */ 1430 @SystemApi 1431 public interface VendorCommandListener { 1432 /** 1433 * Called when a vendor command is received. 1434 * 1435 * @param srcAddress source logical address 1436 * @param destAddress destination logical address 1437 * @param params vendor-specific parameters 1438 * @param hasVendorId {@code true} if the command is <Vendor Command 1439 * With ID>. The first 3 bytes of params is vendor id. 1440 */ onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId)1441 void onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId); 1442 1443 /** 1444 * The callback is called: 1445 * <ul> 1446 * <li> before HdmiControlService is disabled. 1447 * <li> after HdmiControlService is enabled and the local address is assigned. 1448 * </ul> 1449 * The client shouldn't hold the thread too long since this is a blocking call. 1450 * 1451 * @param enabled {@code true} if HdmiControlService is enabled. 1452 * @param reason the reason code why the state of HdmiControlService is changed. 1453 * @see #CONTROL_STATE_CHANGED_REASON_START 1454 * @see #CONTROL_STATE_CHANGED_REASON_SETTING 1455 * @see #CONTROL_STATE_CHANGED_REASON_WAKEUP 1456 * @see #CONTROL_STATE_CHANGED_REASON_STANDBY 1457 */ onControlStateChanged(boolean enabled, int reason)1458 void onControlStateChanged(boolean enabled, int reason); 1459 } 1460 1461 /** 1462 * Adds a listener to get informed of {@link HdmiHotplugEvent}. 1463 * 1464 * <p>To stop getting the notification, 1465 * use {@link #removeHotplugEventListener(HotplugEventListener)}. 1466 * 1467 * Note that each invocation of the callback will be executed on an arbitrary 1468 * Binder thread. This means that all callback implementations must be 1469 * thread safe. To specify the execution thread, use 1470 * {@link addHotplugEventListener(Executor, HotplugEventListener)}. 1471 * 1472 * @param listener {@link HotplugEventListener} instance 1473 * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener) 1474 * 1475 * @hide 1476 */ 1477 @SystemApi 1478 @RequiresPermission(android.Manifest.permission.HDMI_CEC) addHotplugEventListener(HotplugEventListener listener)1479 public void addHotplugEventListener(HotplugEventListener listener) { 1480 addHotplugEventListener(ConcurrentUtils.DIRECT_EXECUTOR, listener); 1481 } 1482 1483 /** 1484 * Adds a listener to get informed of {@link HdmiHotplugEvent}. 1485 * 1486 * <p>To stop getting the notification, 1487 * use {@link #removeHotplugEventListener(HotplugEventListener)}. 1488 * 1489 * @param listener {@link HotplugEventListener} instance 1490 * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener) 1491 * 1492 * @hide 1493 */ 1494 @SystemApi 1495 @RequiresPermission(android.Manifest.permission.HDMI_CEC) addHotplugEventListener(@onNull @allbackExecutor Executor executor, @NonNull HotplugEventListener listener)1496 public void addHotplugEventListener(@NonNull @CallbackExecutor Executor executor, 1497 @NonNull HotplugEventListener listener) { 1498 if (mService == null) { 1499 Log.e(TAG, "HdmiControlService is not available"); 1500 return; 1501 } 1502 if (mHotplugEventListeners.containsKey(listener)) { 1503 Log.e(TAG, "listener is already registered"); 1504 return; 1505 } 1506 IHdmiHotplugEventListener wrappedListener = 1507 getHotplugEventListenerWrapper(executor, listener); 1508 mHotplugEventListeners.put(listener, wrappedListener); 1509 try { 1510 mService.addHotplugEventListener(wrappedListener); 1511 } catch (RemoteException e) { 1512 throw e.rethrowFromSystemServer(); 1513 } 1514 } 1515 1516 /** 1517 * Removes a listener to stop getting informed of {@link HdmiHotplugEvent}. 1518 * 1519 * @param listener {@link HotplugEventListener} instance to be removed 1520 * 1521 * @hide 1522 */ 1523 @SystemApi 1524 @RequiresPermission(android.Manifest.permission.HDMI_CEC) removeHotplugEventListener(HotplugEventListener listener)1525 public void removeHotplugEventListener(HotplugEventListener listener) { 1526 if (mService == null) { 1527 Log.e(TAG, "HdmiControlService is not available"); 1528 return; 1529 } 1530 IHdmiHotplugEventListener wrappedListener = mHotplugEventListeners.remove(listener); 1531 if (wrappedListener == null) { 1532 Log.e(TAG, "tried to remove not-registered listener"); 1533 return; 1534 } 1535 try { 1536 mService.removeHotplugEventListener(wrappedListener); 1537 } catch (RemoteException e) { 1538 throw e.rethrowFromSystemServer(); 1539 } 1540 } 1541 getHotplugEventListenerWrapper( Executor executor, final HotplugEventListener listener)1542 private IHdmiHotplugEventListener getHotplugEventListenerWrapper( 1543 Executor executor, final HotplugEventListener listener) { 1544 return new IHdmiHotplugEventListener.Stub() { 1545 @Override 1546 public void onReceived(HdmiHotplugEvent event) { 1547 final long token = Binder.clearCallingIdentity(); 1548 try { 1549 executor.execute(() -> listener.onReceived(event)); 1550 } finally { 1551 Binder.restoreCallingIdentity(token); 1552 } 1553 } 1554 }; 1555 } 1556 1557 /** 1558 * Adds a listener to get informed of {@link HdmiControlStatusChange}. 1559 * 1560 * <p>To stop getting the notification, 1561 * use {@link #removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener)}. 1562 * 1563 * Note that each invocation of the callback will be executed on an arbitrary 1564 * Binder thread. This means that all callback implementations must be 1565 * thread safe. To specify the execution thread, use 1566 * {@link addHdmiControlStatusChangeListener(Executor, HdmiControlStatusChangeListener)}. 1567 * 1568 * @param listener {@link HdmiControlStatusChangeListener} instance 1569 * @see HdmiControlManager#removeHdmiControlStatusChangeListener( 1570 * HdmiControlStatusChangeListener) 1571 * 1572 * @hide 1573 */ 1574 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1575 public void addHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) { 1576 addHdmiControlStatusChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener); 1577 } 1578 1579 /** 1580 * Adds a listener to get informed of {@link HdmiControlStatusChange}. 1581 * 1582 * <p>To stop getting the notification, 1583 * use {@link #removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener)}. 1584 * 1585 * @param listener {@link HdmiControlStatusChangeListener} instance 1586 * @see HdmiControlManager#removeHdmiControlStatusChangeListener( 1587 * HdmiControlStatusChangeListener) 1588 * 1589 * @hide 1590 */ 1591 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1592 public void addHdmiControlStatusChangeListener(@NonNull @CallbackExecutor Executor executor, 1593 @NonNull HdmiControlStatusChangeListener listener) { 1594 if (mService == null) { 1595 Log.e(TAG, "HdmiControlService is not available"); 1596 return; 1597 } 1598 if (mHdmiControlStatusChangeListeners.containsKey(listener)) { 1599 Log.e(TAG, "listener is already registered"); 1600 return; 1601 } 1602 IHdmiControlStatusChangeListener wrappedListener = 1603 getHdmiControlStatusChangeListenerWrapper(executor, listener); 1604 mHdmiControlStatusChangeListeners.put(listener, wrappedListener); 1605 try { 1606 mService.addHdmiControlStatusChangeListener(wrappedListener); 1607 } catch (RemoteException e) { 1608 throw e.rethrowFromSystemServer(); 1609 } 1610 } 1611 1612 /** 1613 * Removes a listener to stop getting informed of {@link HdmiControlStatusChange}. 1614 * 1615 * @param listener {@link HdmiControlStatusChangeListener} instance to be removed 1616 * 1617 * @hide 1618 */ 1619 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1620 public void removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) { 1621 if (mService == null) { 1622 Log.e(TAG, "HdmiControlService is not available"); 1623 return; 1624 } 1625 IHdmiControlStatusChangeListener wrappedListener = 1626 mHdmiControlStatusChangeListeners.remove(listener); 1627 if (wrappedListener == null) { 1628 Log.e(TAG, "tried to remove not-registered listener"); 1629 return; 1630 } 1631 try { 1632 mService.removeHdmiControlStatusChangeListener(wrappedListener); 1633 } catch (RemoteException e) { 1634 throw e.rethrowFromSystemServer(); 1635 } 1636 } 1637 1638 private IHdmiControlStatusChangeListener getHdmiControlStatusChangeListenerWrapper( 1639 Executor executor, final HdmiControlStatusChangeListener listener) { 1640 return new IHdmiControlStatusChangeListener.Stub() { 1641 @Override 1642 public void onStatusChange(@HdmiCecControl int isCecEnabled, boolean isCecAvailable) { 1643 final long token = Binder.clearCallingIdentity(); 1644 try { 1645 executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable)); 1646 } finally { 1647 Binder.restoreCallingIdentity(token); 1648 } 1649 } 1650 }; 1651 } 1652 1653 /** 1654 * Adds a listener to get informed of changes to the state of the HDMI CEC volume control 1655 * feature. 1656 * 1657 * Upon adding a listener, the current state of the HDMI CEC volume control feature will be 1658 * sent immediately. 1659 * 1660 * <p>To stop getting the notification, 1661 * use {@link #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)}. 1662 * 1663 * @param listener {@link HdmiCecVolumeControlFeatureListener} instance 1664 * @hide 1665 * @see #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener) 1666 */ 1667 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1668 public void addHdmiCecVolumeControlFeatureListener(@NonNull @CallbackExecutor Executor executor, 1669 @NonNull HdmiCecVolumeControlFeatureListener listener) { 1670 if (mService == null) { 1671 Log.e(TAG, "HdmiControlService is not available"); 1672 return; 1673 } 1674 if (mHdmiCecVolumeControlFeatureListeners.containsKey(listener)) { 1675 Log.e(TAG, "listener is already registered"); 1676 return; 1677 } 1678 IHdmiCecVolumeControlFeatureListener wrappedListener = 1679 createHdmiCecVolumeControlFeatureListenerWrapper(executor, listener); 1680 mHdmiCecVolumeControlFeatureListeners.put(listener, wrappedListener); 1681 try { 1682 mService.addHdmiCecVolumeControlFeatureListener(wrappedListener); 1683 } catch (RemoteException e) { 1684 throw e.rethrowFromSystemServer(); 1685 } 1686 } 1687 1688 /** 1689 * Removes a listener to stop getting informed of changes to the state of the HDMI CEC volume 1690 * control feature. 1691 * 1692 * @param listener {@link HdmiCecVolumeControlFeatureListener} instance to be removed 1693 * @hide 1694 */ 1695 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1696 public void removeHdmiCecVolumeControlFeatureListener( 1697 HdmiCecVolumeControlFeatureListener listener) { 1698 if (mService == null) { 1699 Log.e(TAG, "HdmiControlService is not available"); 1700 return; 1701 } 1702 IHdmiCecVolumeControlFeatureListener wrappedListener = 1703 mHdmiCecVolumeControlFeatureListeners.remove(listener); 1704 if (wrappedListener == null) { 1705 Log.e(TAG, "tried to remove not-registered listener"); 1706 return; 1707 } 1708 try { 1709 mService.removeHdmiCecVolumeControlFeatureListener(wrappedListener); 1710 } catch (RemoteException e) { 1711 throw e.rethrowFromSystemServer(); 1712 } 1713 } 1714 1715 private IHdmiCecVolumeControlFeatureListener createHdmiCecVolumeControlFeatureListenerWrapper( 1716 Executor executor, final HdmiCecVolumeControlFeatureListener listener) { 1717 return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() { 1718 @Override 1719 public void onHdmiCecVolumeControlFeature(int enabled) { 1720 final long token = Binder.clearCallingIdentity(); 1721 try { 1722 executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled)); 1723 } finally { 1724 Binder.restoreCallingIdentity(token); 1725 } 1726 } 1727 }; 1728 } 1729 1730 /** 1731 * Listener used to get setting change notification. 1732 * 1733 * @hide 1734 */ 1735 @SystemApi 1736 public interface CecSettingChangeListener { 1737 /** 1738 * Called when value of a setting changes. 1739 * 1740 * @param setting name of a CEC setting that changed 1741 */ 1742 void onChange(@NonNull @CecSettingName String setting); 1743 } 1744 1745 private final ArrayMap<String, 1746 ArrayMap<CecSettingChangeListener, IHdmiCecSettingChangeListener>> 1747 mCecSettingChangeListeners = new ArrayMap<>(); 1748 1749 private void addCecSettingChangeListener( 1750 @NonNull @CecSettingName String setting, 1751 @NonNull @CallbackExecutor Executor executor, 1752 @NonNull CecSettingChangeListener listener) { 1753 if (mService == null) { 1754 Log.e(TAG, "HdmiControlService is not available"); 1755 return; 1756 } 1757 if (mCecSettingChangeListeners.containsKey(setting) 1758 && mCecSettingChangeListeners.get(setting).containsKey(listener)) { 1759 Log.e(TAG, "listener is already registered"); 1760 return; 1761 } 1762 IHdmiCecSettingChangeListener wrappedListener = 1763 getCecSettingChangeListenerWrapper(executor, listener); 1764 if (!mCecSettingChangeListeners.containsKey(setting)) { 1765 mCecSettingChangeListeners.put(setting, new ArrayMap<>()); 1766 } 1767 mCecSettingChangeListeners.get(setting).put(listener, wrappedListener); 1768 try { 1769 mService.addCecSettingChangeListener(setting, wrappedListener); 1770 } catch (RemoteException e) { 1771 throw e.rethrowFromSystemServer(); 1772 } 1773 } 1774 1775 private void removeCecSettingChangeListener( 1776 @NonNull @CecSettingName String setting, 1777 @NonNull CecSettingChangeListener listener) { 1778 if (mService == null) { 1779 Log.e(TAG, "HdmiControlService is not available"); 1780 return; 1781 } 1782 IHdmiCecSettingChangeListener wrappedListener = 1783 !mCecSettingChangeListeners.containsKey(setting) ? null : 1784 mCecSettingChangeListeners.get(setting).remove(listener); 1785 if (wrappedListener == null) { 1786 Log.e(TAG, "tried to remove not-registered listener"); 1787 return; 1788 } 1789 try { 1790 mService.removeCecSettingChangeListener(setting, wrappedListener); 1791 } catch (RemoteException e) { 1792 throw e.rethrowFromSystemServer(); 1793 } 1794 } 1795 1796 private IHdmiCecSettingChangeListener getCecSettingChangeListenerWrapper( 1797 Executor executor, final CecSettingChangeListener listener) { 1798 return new IHdmiCecSettingChangeListener.Stub() { 1799 @Override 1800 public void onChange(String setting) { 1801 final long token = Binder.clearCallingIdentity(); 1802 try { 1803 executor.execute(() -> listener.onChange(setting)); 1804 } finally { 1805 Binder.restoreCallingIdentity(token); 1806 } 1807 } 1808 }; 1809 } 1810 1811 /** 1812 * Get a set of user-modifiable settings. 1813 * 1814 * @return a set of user-modifiable settings. 1815 * @throws RuntimeException when the HdmiControlService is not available. 1816 * 1817 * @hide 1818 */ 1819 @SystemApi 1820 @NonNull 1821 @CecSettingName 1822 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1823 public List<String> getUserCecSettings() { 1824 if (mService == null) { 1825 Log.e(TAG, "HdmiControlService is not available"); 1826 throw new RuntimeException("HdmiControlService is not available"); 1827 } 1828 try { 1829 return mService.getUserCecSettings(); 1830 } catch (RemoteException e) { 1831 throw e.rethrowFromSystemServer(); 1832 } 1833 } 1834 1835 /** 1836 * Get a set of allowed values for a setting (string value-type). 1837 * 1838 * @param name name of the setting 1839 * @return a set of allowed values for a settings. {@code null} on failure. 1840 * @throws IllegalArgumentException when setting {@code name} does not exist. 1841 * @throws IllegalArgumentException when setting {@code name} value type is invalid. 1842 * @throws RuntimeException when the HdmiControlService is not available. 1843 * 1844 * @hide 1845 */ 1846 @SystemApi 1847 @NonNull 1848 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1849 public List<String> getAllowedCecSettingStringValues(@NonNull @CecSettingName String name) { 1850 if (mService == null) { 1851 Log.e(TAG, "HdmiControlService is not available"); 1852 throw new RuntimeException("HdmiControlService is not available"); 1853 } 1854 try { 1855 return mService.getAllowedCecSettingStringValues(name); 1856 } catch (RemoteException e) { 1857 throw e.rethrowFromSystemServer(); 1858 } 1859 } 1860 1861 /** 1862 * Get a set of allowed values for a setting (int value-type). 1863 * 1864 * @param name name of the setting 1865 * @return a set of allowed values for a settings. {@code null} on failure. 1866 * @throws IllegalArgumentException when setting {@code name} does not exist. 1867 * @throws IllegalArgumentException when setting {@code name} value type is invalid. 1868 * @throws RuntimeException when the HdmiControlService is not available. 1869 * 1870 * @hide 1871 */ 1872 @SystemApi 1873 @NonNull 1874 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1875 public List<Integer> getAllowedCecSettingIntValues(@NonNull @CecSettingName String name) { 1876 if (mService == null) { 1877 Log.e(TAG, "HdmiControlService is not available"); 1878 throw new RuntimeException("HdmiControlService is not available"); 1879 } 1880 try { 1881 int[] allowedValues = mService.getAllowedCecSettingIntValues(name); 1882 return Arrays.stream(allowedValues).boxed().collect(Collectors.toList()); 1883 } catch (RemoteException e) { 1884 throw e.rethrowFromSystemServer(); 1885 } 1886 } 1887 1888 /** 1889 * Set the global status of HDMI CEC. 1890 * 1891 * <p>This allows to enable/disable HDMI CEC on the device. 1892 * 1893 * @hide 1894 */ 1895 @SystemApi 1896 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1897 public void setHdmiCecEnabled(@NonNull @HdmiCecControl int value) { 1898 if (mService == null) { 1899 Log.e(TAG, "HdmiControlService is not available"); 1900 throw new RuntimeException("HdmiControlService is not available"); 1901 } 1902 try { 1903 mService.setCecSettingIntValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED, value); 1904 } catch (RemoteException e) { 1905 throw e.rethrowFromSystemServer(); 1906 } 1907 } 1908 1909 /** 1910 * Get the current global status of HDMI CEC. 1911 * 1912 * <p>Reflects whether HDMI CEC is currently enabled on the device. 1913 * 1914 * @hide 1915 */ 1916 @SystemApi 1917 @NonNull 1918 @HdmiCecControl 1919 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1920 public int getHdmiCecEnabled() { 1921 if (mService == null) { 1922 Log.e(TAG, "HdmiControlService is not available"); 1923 throw new RuntimeException("HdmiControlService is not available"); 1924 } 1925 try { 1926 return mService.getCecSettingIntValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED); 1927 } catch (RemoteException e) { 1928 throw e.rethrowFromSystemServer(); 1929 } 1930 } 1931 1932 /** 1933 * Add change listener for global status of HDMI CEC. 1934 * 1935 * <p>To stop getting the notification, 1936 * use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}. 1937 * 1938 * Note that each invocation of the callback will be executed on an arbitrary 1939 * Binder thread. This means that all callback implementations must be 1940 * thread safe. To specify the execution thread, use 1941 * {@link addHdmiCecEnabledChangeListener(Executor, CecSettingChangeListener)}. 1942 * 1943 * @hide 1944 */ 1945 @SystemApi 1946 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1947 public void addHdmiCecEnabledChangeListener(@NonNull CecSettingChangeListener listener) { 1948 addHdmiCecEnabledChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener); 1949 } 1950 1951 /** 1952 * Add change listener for global status of HDMI CEC. 1953 * 1954 * <p>To stop getting the notification, 1955 * use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}. 1956 * 1957 * @hide 1958 */ 1959 @SystemApi 1960 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1961 public void addHdmiCecEnabledChangeListener( 1962 @NonNull @CallbackExecutor Executor executor, 1963 @NonNull CecSettingChangeListener listener) { 1964 addCecSettingChangeListener(CEC_SETTING_NAME_HDMI_CEC_ENABLED, executor, listener); 1965 } 1966 1967 /** 1968 * Remove change listener for global status of HDMI CEC. 1969 * 1970 * @hide 1971 */ 1972 @SystemApi 1973 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1974 public void removeHdmiCecEnabledChangeListener( 1975 @NonNull CecSettingChangeListener listener) { 1976 removeCecSettingChangeListener(CEC_SETTING_NAME_HDMI_CEC_ENABLED, listener); 1977 } 1978 1979 /** 1980 * Set the version of the HDMI CEC specification currently used. 1981 * 1982 * <p>Allows to select either CEC 1.4b or 2.0 to be used by the device. 1983 * 1984 * @hide 1985 */ 1986 @SystemApi 1987 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 1988 public void setHdmiCecVersion(@NonNull @HdmiCecVersion int value) { 1989 if (mService == null) { 1990 Log.e(TAG, "HdmiControlService is not available"); 1991 throw new RuntimeException("HdmiControlService is not available"); 1992 } 1993 try { 1994 mService.setCecSettingIntValue(CEC_SETTING_NAME_HDMI_CEC_VERSION, value); 1995 } catch (RemoteException e) { 1996 throw e.rethrowFromSystemServer(); 1997 } 1998 } 1999 2000 /** 2001 * Get the version of the HDMI CEC specification currently used. 2002 * 2003 * <p>Reflects which CEC version 1.4b or 2.0 is currently used by the device. 2004 * 2005 * @hide 2006 */ 2007 @SystemApi 2008 @NonNull 2009 @HdmiCecVersion 2010 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2011 public int getHdmiCecVersion() { 2012 if (mService == null) { 2013 Log.e(TAG, "HdmiControlService is not available"); 2014 throw new RuntimeException("HdmiControlService is not available"); 2015 } 2016 try { 2017 return mService.getCecSettingIntValue(CEC_SETTING_NAME_HDMI_CEC_VERSION); 2018 } catch (RemoteException e) { 2019 throw e.rethrowFromSystemServer(); 2020 } 2021 } 2022 2023 /** 2024 * Set the status of Power Control. 2025 * 2026 * <p>Specifies to which devices Power Control messages should be sent: 2027 * only to the TV, broadcast to all devices, no power control messages. 2028 * 2029 * @hide 2030 */ 2031 @SystemApi 2032 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2033 public void setPowerControlMode(@NonNull @PowerControlMode String value) { 2034 if (mService == null) { 2035 Log.e(TAG, "HdmiControlService is not available"); 2036 throw new RuntimeException("HdmiControlService is not available"); 2037 } 2038 try { 2039 mService.setCecSettingStringValue(CEC_SETTING_NAME_POWER_CONTROL_MODE, value); 2040 } catch (RemoteException e) { 2041 throw e.rethrowFromSystemServer(); 2042 } 2043 } 2044 2045 /** 2046 * Get the status of Power Control. 2047 * 2048 * <p>Reflects to which devices Power Control messages should be sent: 2049 * only to the TV, broadcast to all devices, no power control messages. 2050 * 2051 * @hide 2052 */ 2053 @SystemApi 2054 @NonNull 2055 @PowerControlMode 2056 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2057 public String getPowerControlMode() { 2058 if (mService == null) { 2059 Log.e(TAG, "HdmiControlService is not available"); 2060 throw new RuntimeException("HdmiControlService is not available"); 2061 } 2062 try { 2063 return mService.getCecSettingStringValue(CEC_SETTING_NAME_POWER_CONTROL_MODE); 2064 } catch (RemoteException e) { 2065 throw e.rethrowFromSystemServer(); 2066 } 2067 } 2068 2069 /** 2070 * Set the current power state behaviour when Active Source is lost. 2071 * 2072 * <p>Sets the action taken: do nothing or go to sleep immediately. 2073 * 2074 * @hide 2075 */ 2076 @SystemApi 2077 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2078 public void setPowerStateChangeOnActiveSourceLost( 2079 @NonNull @ActiveSourceLostBehavior String value) { 2080 if (mService == null) { 2081 Log.e(TAG, "HdmiControlService is not available"); 2082 throw new RuntimeException("HdmiControlService is not available"); 2083 } 2084 try { 2085 mService.setCecSettingStringValue( 2086 CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, value); 2087 } catch (RemoteException e) { 2088 throw e.rethrowFromSystemServer(); 2089 } 2090 } 2091 2092 /** 2093 * Get the current power state behaviour when Active Source is lost. 2094 * 2095 * <p>Reflects the action taken: do nothing or go to sleep immediately. 2096 * 2097 * @hide 2098 */ 2099 @SystemApi 2100 @NonNull 2101 @ActiveSourceLostBehavior 2102 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2103 public String getPowerStateChangeOnActiveSourceLost() { 2104 if (mService == null) { 2105 Log.e(TAG, "HdmiControlService is not available"); 2106 throw new RuntimeException("HdmiControlService is not available"); 2107 } 2108 try { 2109 return mService.getCecSettingStringValue( 2110 CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST); 2111 } catch (RemoteException e) { 2112 throw e.rethrowFromSystemServer(); 2113 } 2114 } 2115 2116 /** 2117 * Set the current status of System Audio Mode muting. 2118 * 2119 * <p>Sets whether the device should be muted when System Audio Mode is turned off. 2120 * 2121 * @hide 2122 */ 2123 @SystemApi 2124 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2125 public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting int value) { 2126 if (mService == null) { 2127 Log.e(TAG, "HdmiControlService is not available"); 2128 throw new RuntimeException("HdmiControlService is not available"); 2129 } 2130 try { 2131 mService.setCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value); 2132 } catch (RemoteException e) { 2133 throw e.rethrowFromSystemServer(); 2134 } 2135 } 2136 2137 /** 2138 * Get the current status of System Audio Mode muting. 2139 * 2140 * <p>Reflects whether the device should be muted when System Audio Mode is turned off. 2141 * 2142 * @hide 2143 */ 2144 @SystemApi 2145 @NonNull 2146 @SystemAudioModeMuting 2147 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2148 public int getSystemAudioModeMuting() { 2149 if (mService == null) { 2150 Log.e(TAG, "HdmiControlService is not available"); 2151 throw new RuntimeException("HdmiControlService is not available"); 2152 } 2153 try { 2154 return mService.getCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); 2155 } catch (RemoteException e) { 2156 throw e.rethrowFromSystemServer(); 2157 } 2158 } 2159 2160 /** 2161 * Set the current status of TV Wake on One Touch Play. 2162 * 2163 * <p>Sets whether the TV should wake up upon reception of <Text View On> 2164 * or <Image View On>. 2165 * 2166 * @hide 2167 */ 2168 @SystemApi 2169 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2170 public void setTvWakeOnOneTouchPlay(@NonNull @TvWakeOnOneTouchPlay int value) { 2171 if (mService == null) { 2172 Log.e(TAG, "HdmiControlService is not available"); 2173 throw new RuntimeException("HdmiControlService is not available"); 2174 } 2175 try { 2176 mService.setCecSettingIntValue(CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, value); 2177 } catch (RemoteException e) { 2178 throw e.rethrowFromSystemServer(); 2179 } 2180 } 2181 2182 /** 2183 * Get the current status of TV Wake on One Touch Play. 2184 * 2185 * <p>Reflects whether the TV should wake up upon reception of <Text View On> 2186 * or <Image View On>. 2187 * 2188 * @hide 2189 */ 2190 @SystemApi 2191 @NonNull 2192 @TvWakeOnOneTouchPlay 2193 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2194 public int getTvWakeOnOneTouchPlay() { 2195 if (mService == null) { 2196 Log.e(TAG, "HdmiControlService is not available"); 2197 throw new RuntimeException("HdmiControlService is not available"); 2198 } 2199 try { 2200 return mService.getCecSettingIntValue(CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY); 2201 } catch (RemoteException e) { 2202 throw e.rethrowFromSystemServer(); 2203 } 2204 } 2205 2206 /** 2207 * Set the current status of TV send <Standby> on Sleep. 2208 * 2209 * <p>Sets whether the device will also turn off other CEC devices 2210 * when it goes to standby mode. 2211 * 2212 * @hide 2213 */ 2214 @SystemApi 2215 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2216 public void setTvSendStandbyOnSleep(@NonNull @TvSendStandbyOnSleep int value) { 2217 if (mService == null) { 2218 Log.e(TAG, "HdmiControlService is not available"); 2219 throw new RuntimeException("HdmiControlService is not available"); 2220 } 2221 try { 2222 mService.setCecSettingIntValue(CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, value); 2223 } catch (RemoteException e) { 2224 throw e.rethrowFromSystemServer(); 2225 } 2226 } 2227 2228 /** 2229 * Get the current status of TV send <Standby> on Sleep. 2230 * 2231 * <p>Reflects whether the device will also turn off other CEC devices 2232 * when it goes to standby mode. 2233 * 2234 * @hide 2235 */ 2236 @SystemApi 2237 @NonNull 2238 @TvSendStandbyOnSleep 2239 @RequiresPermission(android.Manifest.permission.HDMI_CEC) 2240 public int getTvSendStandbyOnSleep() { 2241 if (mService == null) { 2242 Log.e(TAG, "HdmiControlService is not available"); 2243 throw new RuntimeException("HdmiControlService is not available"); 2244 } 2245 try { 2246 return mService.getCecSettingIntValue(CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP); 2247 } catch (RemoteException e) { 2248 throw e.rethrowFromSystemServer(); 2249 } 2250 } 2251 } 2252