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.Nullable; 20 import android.annotation.RequiresFeature; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.annotation.SuppressLint; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.annotation.SystemApi; 28 import android.annotation.SystemService; 29 import android.os.RemoteException; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 33 /** 34 * The {@link HdmiControlManager} class is used to send HDMI control messages 35 * to attached CEC devices. 36 * 37 * <p>Provides various HDMI client instances that represent HDMI-CEC logical devices 38 * hosted in the system. {@link #getTvClient()}, for instance will return an 39 * {@link HdmiTvClient} object if the system is configured to host one. Android system 40 * can host more than one logical CEC devices. If multiple types are configured they 41 * all work as if they were independent logical devices running in the system. 42 * 43 * @hide 44 */ 45 @SystemApi 46 @SystemService(Context.HDMI_CONTROL_SERVICE) 47 @RequiresFeature(PackageManager.FEATURE_HDMI_CEC) 48 public final class HdmiControlManager { 49 private static final String TAG = "HdmiControlManager"; 50 51 @Nullable private final IHdmiControlService mService; 52 53 /** 54 * Broadcast Action: Display OSD message. 55 * <p>Send when the service has a message to display on screen for events 56 * that need user's attention such as ARC status change. 57 * <p>Always contains the extra fields {@link #EXTRA_MESSAGE_ID}. 58 * <p>Requires {@link android.Manifest.permission#HDMI_CEC} to receive. 59 */ 60 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 61 public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE"; 62 63 // --- Messages for ACTION_OSD_MESSAGE --- 64 /** 65 * Message that ARC enabled device is connected to invalid port (non-ARC port). 66 */ 67 public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; 68 69 /** 70 * Message used by TV to receive volume status from Audio Receiver. It should check volume value 71 * that is retrieved from extra value with the key {@link #EXTRA_MESSAGE_EXTRA_PARAM1}. If the 72 * value is in range of [0,100], it is current volume of Audio Receiver. And there is another 73 * value, {@link #AVR_VOLUME_MUTED}, which is used to inform volume mute. 74 */ 75 public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; 76 77 /** 78 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of 79 * the message to display on screen. 80 */ 81 public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID"; 82 /** 83 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the extra value 84 * of the message. 85 */ 86 public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = 87 "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1"; 88 89 /** 90 * Volume value for mute state. 91 */ 92 public static final int AVR_VOLUME_MUTED = 101; 93 94 public static final int POWER_STATUS_UNKNOWN = -1; 95 public static final int POWER_STATUS_ON = 0; 96 public static final int POWER_STATUS_STANDBY = 1; 97 public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; 98 public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; 99 100 public static final int RESULT_SUCCESS = 0; 101 public static final int RESULT_TIMEOUT = 1; 102 public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; 103 public static final int RESULT_TARGET_NOT_AVAILABLE = 3; 104 105 @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; 106 public static final int RESULT_EXCEPTION = 5; 107 public static final int RESULT_INCORRECT_MODE = 6; 108 public static final int RESULT_COMMUNICATION_FAILED = 7; 109 110 public static final int DEVICE_EVENT_ADD_DEVICE = 1; 111 public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; 112 public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; 113 114 // --- One Touch Recording success result 115 /** Recording currently selected source. Indicates the status of a recording. */ 116 public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x01; 117 /** Recording Digital Service. Indicates the status of a recording. */ 118 public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 0x02; 119 /** Recording Analogue Service. Indicates the status of a recording. */ 120 public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 0x03; 121 /** Recording External input. Indicates the status of a recording. */ 122 public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 0x04; 123 124 // --- One Touch Record failure result 125 /** No recording – unable to record Digital Service. No suitable tuner. */ 126 public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 0x05; 127 /** No recording – unable to record Analogue Service. No suitable tuner. */ 128 public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 0x06; 129 /** 130 * No recording – unable to select required service. as suitable tuner, but the requested 131 * parameters are invalid or out of range for that tuner. 132 */ 133 public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 0x07; 134 /** No recording – invalid External plug number */ 135 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 0x09; 136 /** No recording – invalid External Physical Address */ 137 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x0A; 138 /** No recording – CA system not supported */ 139 public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 0x0B; 140 /** No Recording – No or Insufficient CA Entitlements” */ 141 public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x0C; 142 /** No recording – Not allowed to copy source. Source is “copy never”. */ 143 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 0x0D; 144 /** No recording – No further copies allowed */ 145 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 0x0E; 146 /** No recording – No media */ 147 public static final int ONE_TOUCH_RECORD_NO_MEDIA = 0x10; 148 /** No recording – playing */ 149 public static final int ONE_TOUCH_RECORD_PLAYING = 0x11; 150 /** No recording – already recording */ 151 public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 0x12; 152 /** No recording – media protected */ 153 public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 0x13; 154 /** No recording – no source signal */ 155 public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 0x14; 156 /** No recording – media problem */ 157 public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 0x15; 158 /** No recording – not enough space available */ 159 public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 0x16; 160 /** No recording – Parental Lock On */ 161 public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 0x17; 162 /** Recording terminated normally */ 163 public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 0x1A; 164 /** Recording has already terminated */ 165 public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 0x1B; 166 /** No recording – other reason */ 167 public static final int ONE_TOUCH_RECORD_OTHER_REASON = 0x1F; 168 // From here extra message for recording that is not mentioned in CEC spec 169 /** No recording. Previous recording request in progress. */ 170 public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 0x30; 171 /** No recording. Please check recorder and connection. */ 172 public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 0x31; 173 /** Cannot record currently displayed source. */ 174 public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x32; 175 /** CEC is disabled. */ 176 public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 0x33; 177 178 // --- Types for timer recording 179 /** Timer recording type for digital service source. */ 180 public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; 181 /** Timer recording type for analogue service source. */ 182 public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; 183 /** Timer recording type for external source. */ 184 public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; 185 186 // --- Timer Status Data 187 /** [Timer Status Data/Media Info] - Media present and not protected. */ 188 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0x0; 189 /** [Timer Status Data/Media Info] - Media present, but protected. */ 190 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 0x1; 191 /** [Timer Status Data/Media Info] - Media not present. */ 192 public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 0x2; 193 194 /** [Timer Status Data/Programmed Info] - Enough space available for recording. */ 195 public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 0x8; 196 /** [Timer Status Data/Programmed Info] - Not enough space available for recording. */ 197 public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 0x9; 198 /** [Timer Status Data/Programmed Info] - Might not enough space available for recording. */ 199 public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 0xB; 200 /** [Timer Status Data/Programmed Info] - No media info available. */ 201 public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 0xA; 202 203 /** [Timer Status Data/Not Programmed Error Info] - No free timer available. */ 204 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 0x1; 205 /** [Timer Status Data/Not Programmed Error Info] - Date out of range. */ 206 public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 0x2; 207 /** [Timer Status Data/Not Programmed Error Info] - Recording Sequence error. */ 208 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 0x3; 209 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Plug Number. */ 210 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 0x4; 211 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Physical Address. */ 212 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 0x5; 213 /** [Timer Status Data/Not Programmed Error Info] - CA system not supported. */ 214 public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 0x6; 215 /** [Timer Status Data/Not Programmed Error Info] - No or insufficient CA Entitlements. */ 216 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 0x7; 217 /** [Timer Status Data/Not Programmed Error Info] - Does not support resolution. */ 218 public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 0x8; 219 /** [Timer Status Data/Not Programmed Error Info] - Parental Lock On. */ 220 public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON= 0x9; 221 /** [Timer Status Data/Not Programmed Error Info] - Clock Failure. */ 222 public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 0xA; 223 /** [Timer Status Data/Not Programmed Error Info] - Duplicate: already programmed. */ 224 public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 0xE; 225 226 // --- Extra result value for timer recording. 227 /** No extra error. */ 228 public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0x00; 229 /** No timer recording - check recorder and connection. */ 230 public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 0x01; 231 /** No timer recording - cannot record selected source. */ 232 public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 0x02; 233 /** CEC is disabled. */ 234 public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 0x03; 235 236 // -- Timer cleared status data code used for result of onClearTimerRecordingResult. 237 /** Timer not cleared – recording. */ 238 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0x00; 239 /** Timer not cleared – no matching. */ 240 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 0x01; 241 /** Timer not cleared – no info available. */ 242 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 0x02; 243 /** Timer cleared. */ 244 public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 0x80; 245 /** Clear timer error - check recorder and connection. */ 246 public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 0xA0; 247 /** Clear timer error - cannot clear timer for selected source. */ 248 public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 0xA1; 249 /** Clear timer error - CEC is disabled. */ 250 public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 0xA2; 251 252 /** The HdmiControlService is started. */ 253 public static final int CONTROL_STATE_CHANGED_REASON_START = 0; 254 /** The state of HdmiControlService is changed by changing of settings. */ 255 public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; 256 /** The HdmiControlService is enabled to wake up. */ 257 public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; 258 /** The HdmiControlService will be disabled to standby. */ 259 public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; 260 261 // True if we have a logical device of type playback hosted in the system. 262 private final boolean mHasPlaybackDevice; 263 // True if we have a logical device of type TV hosted in the system. 264 private final boolean mHasTvDevice; 265 266 /** 267 * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService, 268 * which is a system private class. The right way to create an instance of this class is 269 * using the factory Context.getSystemService. 270 */ HdmiControlManager(IHdmiControlService service)271 public HdmiControlManager(IHdmiControlService service) { 272 mService = service; 273 int[] types = null; 274 if (mService != null) { 275 try { 276 types = mService.getSupportedTypes(); 277 } catch (RemoteException e) { 278 throw e.rethrowFromSystemServer(); 279 } 280 } 281 mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV); 282 mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK); 283 } 284 hasDeviceType(int[] types, int type)285 private static boolean hasDeviceType(int[] types, int type) { 286 if (types == null) { 287 return false; 288 } 289 for (int t : types) { 290 if (t == type) { 291 return true; 292 } 293 } 294 return false; 295 } 296 297 /** 298 * Gets an object that represents an HDMI-CEC logical device of a specified type. 299 * 300 * @param type CEC device type 301 * @return {@link HdmiClient} instance. {@code null} on failure. 302 * See {@link HdmiDeviceInfo#DEVICE_PLAYBACK} 303 * See {@link HdmiDeviceInfo#DEVICE_TV} 304 */ 305 @Nullable 306 @SuppressLint("Doclava125") getClient(int type)307 public HdmiClient getClient(int type) { 308 if (mService == null) { 309 return null; 310 } 311 switch (type) { 312 case HdmiDeviceInfo.DEVICE_TV: 313 return mHasTvDevice ? new HdmiTvClient(mService) : null; 314 case HdmiDeviceInfo.DEVICE_PLAYBACK: 315 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null; 316 default: 317 return null; 318 } 319 } 320 321 /** 322 * Gets an object that represents an HDMI-CEC logical device of type playback on the system. 323 * 324 * <p>Used to send HDMI control messages to other devices like TV or audio amplifier through 325 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same 326 * system if the system is configured to host more than one type of HDMI-CEC logical devices. 327 * 328 * @return {@link HdmiPlaybackClient} instance. {@code null} on failure. 329 */ 330 @Nullable 331 @SuppressLint("Doclava125") getPlaybackClient()332 public HdmiPlaybackClient getPlaybackClient() { 333 return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK); 334 } 335 336 /** 337 * Gets an object that represents an HDMI-CEC logical device of type TV on the system. 338 * 339 * <p>Used to send HDMI control messages to other devices and manage them through 340 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same 341 * system if the system is configured to host more than one type of HDMI-CEC logical devices. 342 * 343 * @return {@link HdmiTvClient} instance. {@code null} on failure. 344 */ 345 @Nullable 346 @SuppressLint("Doclava125") getTvClient()347 public HdmiTvClient getTvClient() { 348 return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV); 349 } 350 351 /** 352 * Controls standby mode of the system. It will also try to turn on/off the connected devices if 353 * necessary. 354 * 355 * @param isStandbyModeOn target status of the system's standby mode 356 */ 357 @RequiresPermission(android.Manifest.permission.HDMI_CEC) setStandbyMode(boolean isStandbyModeOn)358 public void setStandbyMode(boolean isStandbyModeOn) { 359 try { 360 mService.setStandbyMode(isStandbyModeOn); 361 } catch (RemoteException e) { 362 throw e.rethrowFromSystemServer(); 363 } 364 } 365 366 /** 367 * Listener used to get hotplug event from HDMI port. 368 */ 369 public interface HotplugEventListener { onReceived(HdmiHotplugEvent event)370 void onReceived(HdmiHotplugEvent event); 371 } 372 373 private final ArrayMap<HotplugEventListener, IHdmiHotplugEventListener> 374 mHotplugEventListeners = new ArrayMap<>(); 375 376 /** 377 * Listener used to get vendor-specific commands. 378 */ 379 public interface VendorCommandListener { 380 /** 381 * Called when a vendor command is received. 382 * 383 * @param srcAddress source logical address 384 * @param destAddress destination logical address 385 * @param params vendor-specific parameters 386 * @param hasVendorId {@code true} if the command is <Vendor Command 387 * With ID>. The first 3 bytes of params is vendor id. 388 */ onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId)389 void onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId); 390 391 /** 392 * The callback is called: 393 * <ul> 394 * <li> before HdmiControlService is disabled. 395 * <li> after HdmiControlService is enabled and the local address is assigned. 396 * </ul> 397 * The client shouldn't hold the thread too long since this is a blocking call. 398 * 399 * @param enabled {@code true} if HdmiControlService is enabled. 400 * @param reason the reason code why the state of HdmiControlService is changed. 401 * @see #CONTROL_STATE_CHANGED_REASON_START 402 * @see #CONTROL_STATE_CHANGED_REASON_SETTING 403 * @see #CONTROL_STATE_CHANGED_REASON_WAKEUP 404 * @see #CONTROL_STATE_CHANGED_REASON_STANDBY 405 */ onControlStateChanged(boolean enabled, int reason)406 void onControlStateChanged(boolean enabled, int reason); 407 } 408 409 /** 410 * Adds a listener to get informed of {@link HdmiHotplugEvent}. 411 * 412 * <p>To stop getting the notification, 413 * use {@link #removeHotplugEventListener(HotplugEventListener)}. 414 * 415 * @param listener {@link HotplugEventListener} instance 416 * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener) 417 */ 418 @RequiresPermission(android.Manifest.permission.HDMI_CEC) addHotplugEventListener(HotplugEventListener listener)419 public void addHotplugEventListener(HotplugEventListener listener) { 420 if (mService == null) { 421 Log.e(TAG, "HdmiControlService is not available"); 422 return; 423 } 424 if (mHotplugEventListeners.containsKey(listener)) { 425 Log.e(TAG, "listener is already registered"); 426 return; 427 } 428 IHdmiHotplugEventListener wrappedListener = getHotplugEventListenerWrapper(listener); 429 mHotplugEventListeners.put(listener, wrappedListener); 430 try { 431 mService.addHotplugEventListener(wrappedListener); 432 } catch (RemoteException e) { 433 throw e.rethrowFromSystemServer(); 434 } 435 } 436 437 /** 438 * Removes a listener to stop getting informed of {@link HdmiHotplugEvent}. 439 * 440 * @param listener {@link HotplugEventListener} instance to be removed 441 */ 442 @RequiresPermission(android.Manifest.permission.HDMI_CEC) removeHotplugEventListener(HotplugEventListener listener)443 public void removeHotplugEventListener(HotplugEventListener listener) { 444 if (mService == null) { 445 Log.e(TAG, "HdmiControlService is not available"); 446 return; 447 } 448 IHdmiHotplugEventListener wrappedListener = mHotplugEventListeners.remove(listener); 449 if (wrappedListener == null) { 450 Log.e(TAG, "tried to remove not-registered listener"); 451 return; 452 } 453 try { 454 mService.removeHotplugEventListener(wrappedListener); 455 } catch (RemoteException e) { 456 throw e.rethrowFromSystemServer(); 457 } 458 } 459 getHotplugEventListenerWrapper( final HotplugEventListener listener)460 private IHdmiHotplugEventListener getHotplugEventListenerWrapper( 461 final HotplugEventListener listener) { 462 return new IHdmiHotplugEventListener.Stub() { 463 @Override 464 public void onReceived(HdmiHotplugEvent event) { 465 listener.onReceived(event);; 466 } 467 }; 468 } 469 } 470