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 static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresFeature; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SdkConstant; 27 import android.annotation.SdkConstant.SdkConstantType; 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.RemoteException; 34 import android.os.SystemProperties; 35 import android.util.ArrayMap; 36 import android.util.Log; 37 38 import com.android.internal.util.Preconditions; 39 40 import java.util.List; 41 42 /** 43 * The {@link HdmiControlManager} class is used to send HDMI control messages 44 * to attached CEC devices. 45 * 46 * <p>Provides various HDMI client instances that represent HDMI-CEC logical devices 47 * hosted in the system. {@link #getTvClient()}, for instance will return an 48 * {@link HdmiTvClient} object if the system is configured to host one. Android system 49 * can host more than one logical CEC devices. If multiple types are configured they 50 * all work as if they were independent logical devices running in the system. 51 * 52 * @hide 53 */ 54 @SystemApi 55 @SystemService(Context.HDMI_CONTROL_SERVICE) 56 @RequiresFeature(PackageManager.FEATURE_HDMI_CEC) 57 public final class HdmiControlManager { 58 private static final String TAG = "HdmiControlManager"; 59 60 @Nullable private final IHdmiControlService mService; 61 62 private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; 63 64 private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; 65 66 /** 67 * Broadcast Action: Display OSD message. 68 * <p>Send when the service has a message to display on screen for events 69 * that need user's attention such as ARC status change. 70 * <p>Always contains the extra fields {@link #EXTRA_MESSAGE_ID}. 71 * <p>Requires {@link android.Manifest.permission#HDMI_CEC} to receive. 72 */ 73 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 74 public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE"; 75 76 // --- Messages for ACTION_OSD_MESSAGE --- 77 /** 78 * Message that ARC enabled device is connected to invalid port (non-ARC port). 79 */ 80 public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; 81 82 /** 83 * Message used by TV to receive volume status from Audio Receiver. It should check volume value 84 * that is retrieved from extra value with the key {@link #EXTRA_MESSAGE_EXTRA_PARAM1}. If the 85 * value is in range of [0,100], it is current volume of Audio Receiver. And there is another 86 * value, {@link #AVR_VOLUME_MUTED}, which is used to inform volume mute. 87 */ 88 public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; 89 90 /** 91 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of 92 * the message to display on screen. 93 */ 94 public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID"; 95 /** 96 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the extra value 97 * of the message. 98 */ 99 public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = 100 "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1"; 101 102 /** 103 * Volume value for mute state. 104 */ 105 public static final int AVR_VOLUME_MUTED = 101; 106 107 public static final int POWER_STATUS_UNKNOWN = -1; 108 public static final int POWER_STATUS_ON = 0; 109 public static final int POWER_STATUS_STANDBY = 1; 110 public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; 111 public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; 112 113 @IntDef ({ 114 RESULT_SUCCESS, 115 RESULT_TIMEOUT, 116 RESULT_SOURCE_NOT_AVAILABLE, 117 RESULT_TARGET_NOT_AVAILABLE, 118 RESULT_ALREADY_IN_PROGRESS, 119 RESULT_EXCEPTION, 120 RESULT_INCORRECT_MODE, 121 RESULT_COMMUNICATION_FAILED, 122 }) 123 public @interface ControlCallbackResult {} 124 125 /** Control operation is successfully handled by the framework. */ 126 public static final int RESULT_SUCCESS = 0; 127 public static final int RESULT_TIMEOUT = 1; 128 /** Source device that the application is using is not available. */ 129 public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; 130 /** Target device that the application is controlling is not available. */ 131 public static final int RESULT_TARGET_NOT_AVAILABLE = 3; 132 133 @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; 134 public static final int RESULT_EXCEPTION = 5; 135 public static final int RESULT_INCORRECT_MODE = 6; 136 public static final int RESULT_COMMUNICATION_FAILED = 7; 137 138 public static final int DEVICE_EVENT_ADD_DEVICE = 1; 139 public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; 140 public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; 141 142 // --- One Touch Recording success result 143 /** Recording currently selected source. Indicates the status of a recording. */ 144 public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x01; 145 /** Recording Digital Service. Indicates the status of a recording. */ 146 public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 0x02; 147 /** Recording Analogue Service. Indicates the status of a recording. */ 148 public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 0x03; 149 /** Recording External input. Indicates the status of a recording. */ 150 public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 0x04; 151 152 // --- One Touch Record failure result 153 /** No recording – unable to record Digital Service. No suitable tuner. */ 154 public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 0x05; 155 /** No recording – unable to record Analogue Service. No suitable tuner. */ 156 public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 0x06; 157 /** 158 * No recording – unable to select required service. as suitable tuner, but the requested 159 * parameters are invalid or out of range for that tuner. 160 */ 161 public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 0x07; 162 /** No recording – invalid External plug number */ 163 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 0x09; 164 /** No recording – invalid External Physical Address */ 165 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x0A; 166 /** No recording – CA system not supported */ 167 public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 0x0B; 168 /** No Recording – No or Insufficient CA Entitlements” */ 169 public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x0C; 170 /** No recording – Not allowed to copy source. Source is “copy never”. */ 171 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 0x0D; 172 /** No recording – No further copies allowed */ 173 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 0x0E; 174 /** No recording – No media */ 175 public static final int ONE_TOUCH_RECORD_NO_MEDIA = 0x10; 176 /** No recording – playing */ 177 public static final int ONE_TOUCH_RECORD_PLAYING = 0x11; 178 /** No recording – already recording */ 179 public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 0x12; 180 /** No recording – media protected */ 181 public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 0x13; 182 /** No recording – no source signal */ 183 public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 0x14; 184 /** No recording – media problem */ 185 public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 0x15; 186 /** No recording – not enough space available */ 187 public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 0x16; 188 /** No recording – Parental Lock On */ 189 public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 0x17; 190 /** Recording terminated normally */ 191 public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 0x1A; 192 /** Recording has already terminated */ 193 public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 0x1B; 194 /** No recording – other reason */ 195 public static final int ONE_TOUCH_RECORD_OTHER_REASON = 0x1F; 196 // From here extra message for recording that is not mentioned in CEC spec 197 /** No recording. Previous recording request in progress. */ 198 public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 0x30; 199 /** No recording. Please check recorder and connection. */ 200 public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 0x31; 201 /** Cannot record currently displayed source. */ 202 public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x32; 203 /** CEC is disabled. */ 204 public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 0x33; 205 206 // --- Types for timer recording 207 /** Timer recording type for digital service source. */ 208 public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; 209 /** Timer recording type for analogue service source. */ 210 public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; 211 /** Timer recording type for external source. */ 212 public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; 213 214 // --- Timer Status Data 215 /** [Timer Status Data/Media Info] - Media present and not protected. */ 216 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0x0; 217 /** [Timer Status Data/Media Info] - Media present, but protected. */ 218 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 0x1; 219 /** [Timer Status Data/Media Info] - Media not present. */ 220 public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 0x2; 221 222 /** [Timer Status Data/Programmed Info] - Enough space available for recording. */ 223 public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 0x8; 224 /** [Timer Status Data/Programmed Info] - Not enough space available for recording. */ 225 public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 0x9; 226 /** [Timer Status Data/Programmed Info] - Might not enough space available for recording. */ 227 public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 0xB; 228 /** [Timer Status Data/Programmed Info] - No media info available. */ 229 public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 0xA; 230 231 /** [Timer Status Data/Not Programmed Error Info] - No free timer available. */ 232 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 0x1; 233 /** [Timer Status Data/Not Programmed Error Info] - Date out of range. */ 234 public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 0x2; 235 /** [Timer Status Data/Not Programmed Error Info] - Recording Sequence error. */ 236 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 0x3; 237 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Plug Number. */ 238 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 0x4; 239 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Physical Address. */ 240 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 0x5; 241 /** [Timer Status Data/Not Programmed Error Info] - CA system not supported. */ 242 public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 0x6; 243 /** [Timer Status Data/Not Programmed Error Info] - No or insufficient CA Entitlements. */ 244 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 0x7; 245 /** [Timer Status Data/Not Programmed Error Info] - Does not support resolution. */ 246 public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 0x8; 247 /** [Timer Status Data/Not Programmed Error Info] - Parental Lock On. */ 248 public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON= 0x9; 249 /** [Timer Status Data/Not Programmed Error Info] - Clock Failure. */ 250 public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 0xA; 251 /** [Timer Status Data/Not Programmed Error Info] - Duplicate: already programmed. */ 252 public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 0xE; 253 254 // --- Extra result value for timer recording. 255 /** No extra error. */ 256 public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0x00; 257 /** No timer recording - check recorder and connection. */ 258 public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 0x01; 259 /** No timer recording - cannot record selected source. */ 260 public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 0x02; 261 /** CEC is disabled. */ 262 public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 0x03; 263 264 // -- Timer cleared status data code used for result of onClearTimerRecordingResult. 265 /** Timer not cleared – recording. */ 266 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0x00; 267 /** Timer not cleared – no matching. */ 268 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 0x01; 269 /** Timer not cleared – no info available. */ 270 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 0x02; 271 /** Timer cleared. */ 272 public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 0x80; 273 /** Clear timer error - check recorder and connection. */ 274 public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 0xA0; 275 /** Clear timer error - cannot clear timer for selected source. */ 276 public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 0xA1; 277 /** Clear timer error - CEC is disabled. */ 278 public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 0xA2; 279 280 /** The HdmiControlService is started. */ 281 public static final int CONTROL_STATE_CHANGED_REASON_START = 0; 282 /** The state of HdmiControlService is changed by changing of settings. */ 283 public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; 284 /** The HdmiControlService is enabled to wake up. */ 285 public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; 286 /** The HdmiControlService will be disabled to standby. */ 287 public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; 288 289 // True if we have a logical device of type playback hosted in the system. 290 private final boolean mHasPlaybackDevice; 291 // True if we have a logical device of type TV hosted in the system. 292 private final boolean mHasTvDevice; 293 // True if we have a logical device of type audio system hosted in the system. 294 private final boolean mHasAudioSystemDevice; 295 // True if we have a logical device of type audio system hosted in the system. 296 private final boolean mHasSwitchDevice; 297 // True if it's a switch device. 298 private final boolean mIsSwitchDevice; 299 300 /** 301 * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService, 302 * which is a system private class. The right way to create an instance of this class is 303 * using the factory Context.getSystemService. 304 */ HdmiControlManager(IHdmiControlService service)305 public HdmiControlManager(IHdmiControlService service) { 306 mService = service; 307 int[] types = null; 308 if (mService != null) { 309 try { 310 types = mService.getSupportedTypes(); 311 } catch (RemoteException e) { 312 throw e.rethrowFromSystemServer(); 313 } 314 } 315 mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV); 316 mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK); 317 mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); 318 mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH); 319 mIsSwitchDevice = SystemProperties.getBoolean( 320 PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false); 321 } 322 hasDeviceType(int[] types, int type)323 private static boolean hasDeviceType(int[] types, int type) { 324 if (types == null) { 325 return false; 326 } 327 for (int t : types) { 328 if (t == type) { 329 return true; 330 } 331 } 332 return false; 333 } 334 335 /** 336 * Gets an object that represents an HDMI-CEC logical device of a specified type. 337 * 338 * @param type CEC device type 339 * @return {@link HdmiClient} instance. {@code null} on failure. 340 * See {@link HdmiDeviceInfo#DEVICE_PLAYBACK} 341 * See {@link HdmiDeviceInfo#DEVICE_TV} 342 * See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} 343 */ 344 @Nullable 345 @SuppressLint("Doclava125") getClient(int type)346 public HdmiClient getClient(int type) { 347 if (mService == null) { 348 return null; 349 } 350 switch (type) { 351 case HdmiDeviceInfo.DEVICE_TV: 352 return mHasTvDevice ? new HdmiTvClient(mService) : null; 353 case HdmiDeviceInfo.DEVICE_PLAYBACK: 354 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null; 355 case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM: 356 return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null; 357 case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH: 358 return (mHasSwitchDevice || mIsSwitchDevice) 359 ? new HdmiSwitchClient(mService) : null; 360 default: 361 return null; 362 } 363 } 364 365 /** 366 * Gets an object that represents an HDMI-CEC logical device of type playback on the system. 367 * 368 * <p>Used to send HDMI control messages to other devices like TV or audio amplifier through 369 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same 370 * system if the system is configured to host more than one type of HDMI-CEC logical devices. 371 * 372 * @return {@link HdmiPlaybackClient} instance. {@code null} on failure. 373 */ 374 @Nullable 375 @SuppressLint("Doclava125") getPlaybackClient()376 public HdmiPlaybackClient getPlaybackClient() { 377 return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK); 378 } 379 380 /** 381 * Gets an object that represents an HDMI-CEC logical device of type TV on the system. 382 * 383 * <p>Used to send HDMI control messages to other devices and manage them through 384 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same 385 * system if the system is configured to host more than one type of HDMI-CEC logical devices. 386 * 387 * @return {@link HdmiTvClient} instance. {@code null} on failure. 388 */ 389 @Nullable 390 @SuppressLint("Doclava125") getTvClient()391 public HdmiTvClient getTvClient() { 392 return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV); 393 } 394 395 /** 396 * Gets an object that represents an HDMI-CEC logical device of type audio system on the system. 397 * 398 * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also 399 * possible to communicate with other logical devices hosted in the same system if the system is 400 * configured to host more than one type of HDMI-CEC logical devices. 401 * 402 * @return {@link HdmiAudioSystemClient} instance. {@code null} on failure. 403 * 404 * TODO(b/110094868): unhide for Q 405 * @hide 406 */ 407 @Nullable 408 @SuppressLint("Doclava125") getAudioSystemClient()409 public HdmiAudioSystemClient getAudioSystemClient() { 410 return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); 411 } 412 413 /** 414 * Gets an object that represents an HDMI-CEC logical device of type switch on the system. 415 * 416 * <p>Used to send HDMI control messages to other devices (e.g. TVs) through HDMI bus. 417 * It is also possible to communicate with other logical devices hosted in the same 418 * system if the system is configured to host more than one type of HDMI-CEC logical device. 419 * 420 * @return {@link HdmiSwitchClient} instance. {@code null} on failure. 421 * @hide 422 */ 423 @Nullable 424 @SystemApi 425 @SuppressLint("Doclava125") getSwitchClient()426 public HdmiSwitchClient getSwitchClient() { 427 return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH); 428 } 429 430 /** 431 * Get a snapshot of the real-time status of the devices on the CEC bus. 432 * 433 * <p>This only applies to devices with switch functionality, which are devices with one 434 * or more than one HDMI inputs. 435 * 436 * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices on the CEC bus. An 437 * empty list will be returned if there is none. 438 * 439 * @hide 440 */ 441 @NonNull 442 @SystemApi getConnectedDevices()443 public List<HdmiDeviceInfo> getConnectedDevices() { 444 try { 445 return mService.getDeviceList(); 446 } catch (RemoteException e) { 447 throw e.rethrowFromSystemServer(); 448 } 449 } 450 451 /** 452 * @removed 453 * @hide 454 * @deprecated Please use {@link #getConnectedDevices()} instead. 455 */ 456 @Deprecated 457 @SystemApi getConnectedDevicesList()458 public List<HdmiDeviceInfo> getConnectedDevicesList() { 459 try { 460 return mService.getDeviceList(); 461 } catch (RemoteException e) { 462 throw e.rethrowFromSystemServer(); 463 } 464 } 465 466 /** 467 * Power off the target device by sending CEC commands. Note that this device can't be the 468 * current device itself. 469 * 470 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. 471 * 472 * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered off. 473 * 474 * @hide 475 */ 476 @SystemApi powerOffDevice(@onNull HdmiDeviceInfo deviceInfo)477 public void powerOffDevice(@NonNull HdmiDeviceInfo deviceInfo) { 478 Preconditions.checkNotNull(deviceInfo); 479 try { 480 mService.powerOffRemoteDevice( 481 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 482 } catch (RemoteException e) { 483 throw e.rethrowFromSystemServer(); 484 } 485 } 486 487 /** 488 * @removed 489 * @hide 490 * @deprecated Please use {@link #powerOffDevice(deviceInfo)} instead. 491 */ 492 @Deprecated 493 @SystemApi powerOffRemoteDevice(@onNull HdmiDeviceInfo deviceInfo)494 public void powerOffRemoteDevice(@NonNull HdmiDeviceInfo deviceInfo) { 495 Preconditions.checkNotNull(deviceInfo); 496 try { 497 mService.powerOffRemoteDevice( 498 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 499 } catch (RemoteException e) { 500 throw e.rethrowFromSystemServer(); 501 } 502 } 503 504 /** 505 * Power on the target device by sending CEC commands. Note that this device can't be the 506 * current device itself. 507 * 508 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. 509 * 510 * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered on. 511 * 512 * @hide 513 */ powerOnDevice(HdmiDeviceInfo deviceInfo)514 public void powerOnDevice(HdmiDeviceInfo deviceInfo) { 515 Preconditions.checkNotNull(deviceInfo); 516 try { 517 mService.powerOnRemoteDevice( 518 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 519 } catch (RemoteException e) { 520 throw e.rethrowFromSystemServer(); 521 } 522 } 523 524 /** 525 * @removed 526 * @hide 527 * @deprecated Please use {@link #powerOnDevice(deviceInfo)} instead. 528 */ 529 @Deprecated 530 @SystemApi powerOnRemoteDevice(HdmiDeviceInfo deviceInfo)531 public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) { 532 Preconditions.checkNotNull(deviceInfo); 533 try { 534 mService.powerOnRemoteDevice( 535 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); 536 } catch (RemoteException e) { 537 throw e.rethrowFromSystemServer(); 538 } 539 } 540 541 /** 542 * Request the target device to be the new Active Source by sending CEC commands. Note that 543 * this device can't be the current device itself. 544 * 545 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}. 546 * 547 * <p>If the target device responds to the command, the users should see the target device 548 * streaming on their TVs. 549 * 550 * @param deviceInfo HdmiDeviceInfo of the target device 551 * 552 * @hide 553 */ 554 @SystemApi setActiveSource(@onNull HdmiDeviceInfo deviceInfo)555 public void setActiveSource(@NonNull HdmiDeviceInfo deviceInfo) { 556 Preconditions.checkNotNull(deviceInfo); 557 try { 558 mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); 559 } catch (RemoteException e) { 560 throw e.rethrowFromSystemServer(); 561 } 562 } 563 564 /** 565 * @removed 566 * @hide 567 * @deprecated Please use {@link #setActiveSource(deviceInfo)} instead. 568 */ 569 @Deprecated 570 @SystemApi requestRemoteDeviceToBecomeActiveSource(@onNull HdmiDeviceInfo deviceInfo)571 public void requestRemoteDeviceToBecomeActiveSource(@NonNull HdmiDeviceInfo deviceInfo) { 572 Preconditions.checkNotNull(deviceInfo); 573 try { 574 mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); 575 } catch (RemoteException e) { 576 throw e.rethrowFromSystemServer(); 577 } 578 } 579 580 /** 581 * Controls standby mode of the system. It will also try to turn on/off the connected devices if 582 * necessary. 583 * 584 * @param isStandbyModeOn target status of the system's standby mode 585 */ 586 @RequiresPermission(android.Manifest.permission.HDMI_CEC) setStandbyMode(boolean isStandbyModeOn)587 public void setStandbyMode(boolean isStandbyModeOn) { 588 try { 589 mService.setStandbyMode(isStandbyModeOn); 590 } catch (RemoteException e) { 591 throw e.rethrowFromSystemServer(); 592 } 593 } 594 595 /** 596 * Gets whether the system is in system audio mode. 597 * 598 * @hide 599 */ getSystemAudioMode()600 public boolean getSystemAudioMode() { 601 try { 602 return mService.getSystemAudioMode(); 603 } catch (RemoteException e) { 604 throw e.rethrowFromSystemServer(); 605 } 606 } 607 608 /** 609 * Get the physical address of the device. 610 * 611 * <p>Physical address needs to be automatically adjusted when devices are phyiscally or 612 * electrically added or removed from the device tree. Please see HDMI Specification Version 613 * 1.4b 8.7 Physical Address for more details on the address discovery proccess. 614 * 615 * @hide 616 */ 617 @SystemApi getPhysicalAddress()618 public int getPhysicalAddress() { 619 if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) { 620 return mPhysicalAddress; 621 } 622 try { 623 mPhysicalAddress = mService.getPhysicalAddress(); 624 return mPhysicalAddress; 625 } catch (RemoteException e) { 626 throw e.rethrowFromSystemServer(); 627 } 628 } 629 630 /** 631 * Check if the target device is connected to the current device. 632 * 633 * <p>The API also returns true if the current device is the target. 634 * 635 * @param targetDevice {@link HdmiDeviceInfo} of the target device. 636 * @return true if {@code targetDevice} is directly or indirectly 637 * connected to the current device. 638 * 639 * @hide 640 */ 641 @SystemApi isDeviceConnected(@onNull HdmiDeviceInfo targetDevice)642 public boolean isDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) { 643 Preconditions.checkNotNull(targetDevice); 644 mPhysicalAddress = getPhysicalAddress(); 645 if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { 646 return false; 647 } 648 int targetPhysicalAddress = targetDevice.getPhysicalAddress(); 649 if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { 650 return false; 651 } 652 return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress) 653 != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; 654 } 655 656 /** 657 * @removed 658 * @hide 659 * @deprecated Please use {@link #isDeviceConnected(targetDevice)} instead. 660 */ 661 @Deprecated 662 @SystemApi isRemoteDeviceConnected(@onNull HdmiDeviceInfo targetDevice)663 public boolean isRemoteDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) { 664 Preconditions.checkNotNull(targetDevice); 665 mPhysicalAddress = getPhysicalAddress(); 666 if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { 667 return false; 668 } 669 int targetPhysicalAddress = targetDevice.getPhysicalAddress(); 670 if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { 671 return false; 672 } 673 return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress) 674 != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE; 675 } 676 677 /** 678 * Listener used to get hotplug event from HDMI port. 679 */ 680 public interface HotplugEventListener { onReceived(HdmiHotplugEvent event)681 void onReceived(HdmiHotplugEvent event); 682 } 683 684 private final ArrayMap<HotplugEventListener, IHdmiHotplugEventListener> 685 mHotplugEventListeners = new ArrayMap<>(); 686 687 /** 688 * Listener used to get vendor-specific commands. 689 */ 690 public interface VendorCommandListener { 691 /** 692 * Called when a vendor command is received. 693 * 694 * @param srcAddress source logical address 695 * @param destAddress destination logical address 696 * @param params vendor-specific parameters 697 * @param hasVendorId {@code true} if the command is <Vendor Command 698 * With ID>. The first 3 bytes of params is vendor id. 699 */ onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId)700 void onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId); 701 702 /** 703 * The callback is called: 704 * <ul> 705 * <li> before HdmiControlService is disabled. 706 * <li> after HdmiControlService is enabled and the local address is assigned. 707 * </ul> 708 * The client shouldn't hold the thread too long since this is a blocking call. 709 * 710 * @param enabled {@code true} if HdmiControlService is enabled. 711 * @param reason the reason code why the state of HdmiControlService is changed. 712 * @see #CONTROL_STATE_CHANGED_REASON_START 713 * @see #CONTROL_STATE_CHANGED_REASON_SETTING 714 * @see #CONTROL_STATE_CHANGED_REASON_WAKEUP 715 * @see #CONTROL_STATE_CHANGED_REASON_STANDBY 716 */ onControlStateChanged(boolean enabled, int reason)717 void onControlStateChanged(boolean enabled, int reason); 718 } 719 720 /** 721 * Adds a listener to get informed of {@link HdmiHotplugEvent}. 722 * 723 * <p>To stop getting the notification, 724 * use {@link #removeHotplugEventListener(HotplugEventListener)}. 725 * 726 * @param listener {@link HotplugEventListener} instance 727 * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener) 728 */ 729 @RequiresPermission(android.Manifest.permission.HDMI_CEC) addHotplugEventListener(HotplugEventListener listener)730 public void addHotplugEventListener(HotplugEventListener listener) { 731 if (mService == null) { 732 Log.e(TAG, "HdmiControlService is not available"); 733 return; 734 } 735 if (mHotplugEventListeners.containsKey(listener)) { 736 Log.e(TAG, "listener is already registered"); 737 return; 738 } 739 IHdmiHotplugEventListener wrappedListener = getHotplugEventListenerWrapper(listener); 740 mHotplugEventListeners.put(listener, wrappedListener); 741 try { 742 mService.addHotplugEventListener(wrappedListener); 743 } catch (RemoteException e) { 744 throw e.rethrowFromSystemServer(); 745 } 746 } 747 748 /** 749 * Removes a listener to stop getting informed of {@link HdmiHotplugEvent}. 750 * 751 * @param listener {@link HotplugEventListener} instance to be removed 752 */ 753 @RequiresPermission(android.Manifest.permission.HDMI_CEC) removeHotplugEventListener(HotplugEventListener listener)754 public void removeHotplugEventListener(HotplugEventListener listener) { 755 if (mService == null) { 756 Log.e(TAG, "HdmiControlService is not available"); 757 return; 758 } 759 IHdmiHotplugEventListener wrappedListener = mHotplugEventListeners.remove(listener); 760 if (wrappedListener == null) { 761 Log.e(TAG, "tried to remove not-registered listener"); 762 return; 763 } 764 try { 765 mService.removeHotplugEventListener(wrappedListener); 766 } catch (RemoteException e) { 767 throw e.rethrowFromSystemServer(); 768 } 769 } 770 getHotplugEventListenerWrapper( final HotplugEventListener listener)771 private IHdmiHotplugEventListener getHotplugEventListenerWrapper( 772 final HotplugEventListener listener) { 773 return new IHdmiHotplugEventListener.Stub() { 774 @Override 775 public void onReceived(HdmiHotplugEvent event) { 776 listener.onReceived(event);; 777 } 778 }; 779 } 780 } 781