blob: 56020b24c5567482008857d2aada0778b44ee048 [file] [log] [blame]
Jinsuk Kim91120c52014-05-08 17:12:51 +09001/*
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
17package android.hardware.hdmi;
18
Amy17ee20f2018-10-11 11:08:23 -070019import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
20
Amy9444cdd2019-01-16 13:42:30 -080021import android.annotation.IntDef;
Jinsuk Kim91120c52014-05-08 17:12:51 +090022import android.annotation.Nullable;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070023import android.annotation.RequiresFeature;
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -060024import android.annotation.RequiresPermission;
Jinsuk Kimc7eba0f2014-07-07 14:18:02 +090025import android.annotation.SdkConstant;
26import android.annotation.SdkConstant.SdkConstantType;
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -060027import android.annotation.SuppressLint;
Jinsuk Kim66d1eb22014-06-06 16:12:18 +090028import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060029import android.annotation.SystemService;
Shubang67373192018-06-08 18:30:15 -070030import android.content.Context;
31import android.content.pm.PackageManager;
Jinsuk Kim78d695d2014-05-13 16:36:15 +090032import android.os.RemoteException;
Amy17ee20f2018-10-11 11:08:23 -070033import android.os.SystemProperties;
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +090034import android.util.ArrayMap;
35import android.util.Log;
Jinsuk Kim78d695d2014-05-13 16:36:15 +090036
Amy9444cdd2019-01-16 13:42:30 -080037import com.android.internal.util.Preconditions;
38
Amy6f031af2018-10-30 16:38:33 -070039import java.util.List;
40
Jinsuk Kim91120c52014-05-08 17:12:51 +090041/**
42 * The {@link HdmiControlManager} class is used to send HDMI control messages
43 * to attached CEC devices.
44 *
45 * <p>Provides various HDMI client instances that represent HDMI-CEC logical devices
46 * hosted in the system. {@link #getTvClient()}, for instance will return an
47 * {@link HdmiTvClient} object if the system is configured to host one. Android system
48 * can host more than one logical CEC devices. If multiple types are configured they
49 * all work as if they were independent logical devices running in the system.
Jinsuk Kim66d1eb22014-06-06 16:12:18 +090050 *
51 * @hide
Jinsuk Kim91120c52014-05-08 17:12:51 +090052 */
Jinsuk Kim66d1eb22014-06-06 16:12:18 +090053@SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060054@SystemService(Context.HDMI_CONTROL_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -070055@RequiresFeature(PackageManager.FEATURE_HDMI_CEC)
Jinsuk Kim91120c52014-05-08 17:12:51 +090056public final class HdmiControlManager {
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +090057 private static final String TAG = "HdmiControlManager";
58
Jinsuk Kim91120c52014-05-08 17:12:51 +090059 @Nullable private final IHdmiControlService mService;
60
Amyff115f12018-11-21 14:26:16 -080061 private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
62
63 private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
64
Jinsuk Kimc7eba0f2014-07-07 14:18:02 +090065 /**
66 * Broadcast Action: Display OSD message.
67 * <p>Send when the service has a message to display on screen for events
68 * that need user's attention such as ARC status change.
Jungshik Jange5a93372014-07-25 13:41:14 +090069 * <p>Always contains the extra fields {@link #EXTRA_MESSAGE_ID}.
Jinsuk Kimc7eba0f2014-07-07 14:18:02 +090070 * <p>Requires {@link android.Manifest.permission#HDMI_CEC} to receive.
71 */
72 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
73 public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
74
Jungshik Jang339227d2014-08-25 15:37:20 +090075 // --- Messages for ACTION_OSD_MESSAGE ---
76 /**
77 * Message that ARC enabled device is connected to invalid port (non-ARC port).
78 */
79 public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1;
80
Jinsuk Kimc7eba0f2014-07-07 14:18:02 +090081 /**
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090082 * Message used by TV to receive volume status from Audio Receiver. It should check volume value
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +090083 * that is retrieved from extra value with the key {@link #EXTRA_MESSAGE_EXTRA_PARAM1}. If the
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090084 * value is in range of [0,100], it is current volume of Audio Receiver. And there is another
85 * value, {@link #AVR_VOLUME_MUTED}, which is used to inform volume mute.
86 */
87 public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2;
88
89 /**
Jinsuk Kimc7eba0f2014-07-07 14:18:02 +090090 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of
91 * the message to display on screen.
92 */
93 public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090094 /**
95 * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the extra value
96 * of the message.
97 */
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +090098 public static final String EXTRA_MESSAGE_EXTRA_PARAM1 =
Jungshik Jang2e8f1b62014-09-03 08:28:02 +090099 "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
100
101 /**
102 * Volume value for mute state.
103 */
104 public static final int AVR_VOLUME_MUTED = 101;
Jinsuk Kimc7eba0f2014-07-07 14:18:02 +0900105
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900106 public static final int POWER_STATUS_UNKNOWN = -1;
107 public static final int POWER_STATUS_ON = 0;
108 public static final int POWER_STATUS_STANDBY = 1;
109 public static final int POWER_STATUS_TRANSIENT_TO_ON = 2;
110 public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3;
111
Amy9444cdd2019-01-16 13:42:30 -0800112 @IntDef ({
113 RESULT_SUCCESS,
114 RESULT_TIMEOUT,
115 RESULT_SOURCE_NOT_AVAILABLE,
116 RESULT_TARGET_NOT_AVAILABLE,
117 RESULT_ALREADY_IN_PROGRESS,
118 RESULT_EXCEPTION,
119 RESULT_INCORRECT_MODE,
120 RESULT_COMMUNICATION_FAILED,
121 })
122 public @interface ControlCallbackResult {}
123
124 /** Control operation is successfully handled by the framework. */
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900125 public static final int RESULT_SUCCESS = 0;
126 public static final int RESULT_TIMEOUT = 1;
Amy9444cdd2019-01-16 13:42:30 -0800127 /** Source device that the application is using is not available. */
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900128 public static final int RESULT_SOURCE_NOT_AVAILABLE = 2;
Amy9444cdd2019-01-16 13:42:30 -0800129 /** Target device that the application is controlling is not available. */
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900130 public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
Jinsuk Kimcb802872015-10-13 08:22:09 +0900131
132 @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900133 public static final int RESULT_EXCEPTION = 5;
134 public static final int RESULT_INCORRECT_MODE = 6;
Jinsuk Kimb38cd682014-07-07 08:05:03 +0900135 public static final int RESULT_COMMUNICATION_FAILED = 7;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900136
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900137 public static final int DEVICE_EVENT_ADD_DEVICE = 1;
138 public static final int DEVICE_EVENT_REMOVE_DEVICE = 2;
139 public static final int DEVICE_EVENT_UPDATE_DEVICE = 3;
140
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900141 // --- One Touch Recording success result
Jungshik Jangb6591b82014-07-23 16:10:23 +0900142 /** Recording currently selected source. Indicates the status of a recording. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900143 public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 0x01;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900144 /** Recording Digital Service. Indicates the status of a recording. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900145 public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 0x02;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900146 /** Recording Analogue Service. Indicates the status of a recording. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900147 public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 0x03;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900148 /** Recording External input. Indicates the status of a recording. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900149 public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 0x04;
150
151 // --- One Touch Record failure result
Jungshik Jangb6591b82014-07-23 16:10:23 +0900152 /** No recording – unable to record Digital Service. No suitable tuner. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900153 public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 0x05;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900154 /** No recording – unable to record Analogue Service. No suitable tuner. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900155 public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 0x06;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900156 /**
157 * No recording – unable to select required service. as suitable tuner, but the requested
158 * parameters are invalid or out of range for that tuner.
159 */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900160 public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 0x07;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900161 /** No recording – invalid External plug number */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900162 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 0x09;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900163 /** No recording – invalid External Physical Address */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900164 public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 0x0A;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900165 /** No recording – CA system not supported */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900166 public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 0x0B;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900167 /** No Recording – No or Insufficient CA Entitlements” */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900168 public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 0x0C;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900169 /** No recording – Not allowed to copy source. Source is “copy never”. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900170 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 0x0D;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900171 /** No recording – No further copies allowed */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900172 public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 0x0E;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900173 /** No recording – No media */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900174 public static final int ONE_TOUCH_RECORD_NO_MEDIA = 0x10;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900175 /** No recording – playing */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900176 public static final int ONE_TOUCH_RECORD_PLAYING = 0x11;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900177 /** No recording – already recording */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900178 public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 0x12;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900179 /** No recording – media protected */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900180 public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 0x13;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900181 /** No recording – no source signal */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900182 public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 0x14;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900183 /** No recording – media problem */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900184 public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 0x15;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900185 /** No recording – not enough space available */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900186 public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 0x16;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900187 /** No recording – Parental Lock On */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900188 public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 0x17;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900189 /** Recording terminated normally */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900190 public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 0x1A;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900191 /** Recording has already terminated */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900192 public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 0x1B;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900193 /** No recording – other reason */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900194 public static final int ONE_TOUCH_RECORD_OTHER_REASON = 0x1F;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900195 // From here extra message for recording that is not mentioned in CEC spec
196 /** No recording. Previous recording request in progress. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900197 public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 0x30;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900198 /** No recording. Please check recorder and connection. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900199 public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 0x31;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900200 /** Cannot record currently displayed source. */
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900201 public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 0x32;
202 /** CEC is disabled. */
203 public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 0x33;
Jungshik Jangb6591b82014-07-23 16:10:23 +0900204
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900205 // --- Types for timer recording
Jungshik Jangb6591b82014-07-23 16:10:23 +0900206 /** Timer recording type for digital service source. */
207 public static final int TIMER_RECORDING_TYPE_DIGITAL = 1;
208 /** Timer recording type for analogue service source. */
209 public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2;
210 /** Timer recording type for external source. */
211 public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3;
212
Jungshik Jange5a93372014-07-25 13:41:14 +0900213 // --- Timer Status Data
214 /** [Timer Status Data/Media Info] - Media present and not protected. */
215 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0x0;
216 /** [Timer Status Data/Media Info] - Media present, but protected. */
217 public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 0x1;
218 /** [Timer Status Data/Media Info] - Media not present. */
219 public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 0x2;
220
221 /** [Timer Status Data/Programmed Info] - Enough space available for recording. */
222 public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 0x8;
223 /** [Timer Status Data/Programmed Info] - Not enough space available for recording. */
224 public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 0x9;
225 /** [Timer Status Data/Programmed Info] - Might not enough space available for recording. */
226 public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 0xB;
227 /** [Timer Status Data/Programmed Info] - No media info available. */
228 public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 0xA;
229
230 /** [Timer Status Data/Not Programmed Error Info] - No free timer available. */
231 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 0x1;
232 /** [Timer Status Data/Not Programmed Error Info] - Date out of range. */
233 public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 0x2;
234 /** [Timer Status Data/Not Programmed Error Info] - Recording Sequence error. */
235 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 0x3;
236 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Plug Number. */
237 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 0x4;
238 /** [Timer Status Data/Not Programmed Error Info] - Invalid External Physical Address. */
239 public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 0x5;
240 /** [Timer Status Data/Not Programmed Error Info] - CA system not supported. */
241 public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 0x6;
242 /** [Timer Status Data/Not Programmed Error Info] - No or insufficient CA Entitlements. */
243 public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 0x7;
244 /** [Timer Status Data/Not Programmed Error Info] - Does not support resolution. */
245 public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 0x8;
246 /** [Timer Status Data/Not Programmed Error Info] - Parental Lock On. */
247 public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON= 0x9;
248 /** [Timer Status Data/Not Programmed Error Info] - Clock Failure. */
249 public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 0xA;
250 /** [Timer Status Data/Not Programmed Error Info] - Duplicate: already programmed. */
251 public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 0xE;
252
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900253 // --- Extra result value for timer recording.
Jungshik Jange5a93372014-07-25 13:41:14 +0900254 /** No extra error. */
255 public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0x00;
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900256 /** No timer recording - check recorder and connection. */
Jungshik Jange5a93372014-07-25 13:41:14 +0900257 public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 0x01;
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900258 /** No timer recording - cannot record selected source. */
Jungshik Jange5a93372014-07-25 13:41:14 +0900259 public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 0x02;
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900260 /** CEC is disabled. */
Jungshik Jange5a93372014-07-25 13:41:14 +0900261 public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 0x03;
262
263 // -- Timer cleared status data code used for result of onClearTimerRecordingResult.
264 /** Timer not cleared – recording. */
265 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0x00;
266 /** Timer not cleared – no matching. */
267 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 0x01;
268 /** Timer not cleared – no info available. */
269 public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 0x02;
270 /** Timer cleared. */
271 public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 0x80;
272 /** Clear timer error - check recorder and connection. */
273 public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 0xA0;
274 /** Clear timer error - cannot clear timer for selected source. */
275 public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 0xA1;
276 /** Clear timer error - CEC is disabled. */
277 public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 0xA2;
Jungshik Jang12e5dce2014-07-24 15:27:44 +0900278
Yuncheol Heo0608b932014-10-13 16:39:18 +0900279 /** The HdmiControlService is started. */
280 public static final int CONTROL_STATE_CHANGED_REASON_START = 0;
281 /** The state of HdmiControlService is changed by changing of settings. */
282 public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1;
283 /** The HdmiControlService is enabled to wake up. */
284 public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2;
285 /** The HdmiControlService will be disabled to standby. */
286 public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3;
287
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900288 // True if we have a logical device of type playback hosted in the system.
289 private final boolean mHasPlaybackDevice;
290 // True if we have a logical device of type TV hosted in the system.
291 private final boolean mHasTvDevice;
Shubang67373192018-06-08 18:30:15 -0700292 // True if we have a logical device of type audio system hosted in the system.
293 private final boolean mHasAudioSystemDevice;
Amy17ee20f2018-10-11 11:08:23 -0700294 // True if we have a logical device of type audio system hosted in the system.
295 private final boolean mHasSwitchDevice;
296 // True if it's a switch device.
297 private final boolean mIsSwitchDevice;
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900298
Jinsuk Kim91120c52014-05-08 17:12:51 +0900299 /**
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900300 * {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
301 * which is a system private class. The right way to create an instance of this class is
302 * using the factory Context.getSystemService.
Jinsuk Kim91120c52014-05-08 17:12:51 +0900303 */
304 public HdmiControlManager(IHdmiControlService service) {
305 mService = service;
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900306 int[] types = null;
307 if (mService != null) {
308 try {
309 types = mService.getSupportedTypes();
310 } catch (RemoteException e) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700311 throw e.rethrowFromSystemServer();
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900312 }
313 }
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900314 mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
315 mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
Shubang67373192018-06-08 18:30:15 -0700316 mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
Amy17ee20f2018-10-11 11:08:23 -0700317 mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
318 mIsSwitchDevice = SystemProperties.getBoolean(
319 PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900320 }
321
322 private static boolean hasDeviceType(int[] types, int type) {
323 if (types == null) {
324 return false;
325 }
326 for (int t : types) {
327 if (t == type) {
328 return true;
329 }
330 }
331 return false;
Jinsuk Kim91120c52014-05-08 17:12:51 +0900332 }
333
334 /**
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900335 * Gets an object that represents an HDMI-CEC logical device of a specified type.
336 *
337 * @param type CEC device type
338 * @return {@link HdmiClient} instance. {@code null} on failure.
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900339 * See {@link HdmiDeviceInfo#DEVICE_PLAYBACK}
340 * See {@link HdmiDeviceInfo#DEVICE_TV}
Shubang67373192018-06-08 18:30:15 -0700341 * See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900342 */
343 @Nullable
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -0600344 @SuppressLint("Doclava125")
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900345 public HdmiClient getClient(int type) {
346 if (mService == null) {
347 return null;
348 }
349 switch (type) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900350 case HdmiDeviceInfo.DEVICE_TV:
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900351 return mHasTvDevice ? new HdmiTvClient(mService) : null;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900352 case HdmiDeviceInfo.DEVICE_PLAYBACK:
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900353 return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
Shubang67373192018-06-08 18:30:15 -0700354 case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
355 return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
Amy17ee20f2018-10-11 11:08:23 -0700356 case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
357 return (mHasSwitchDevice || mIsSwitchDevice)
358 ? new HdmiSwitchClient(mService) : null;
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900359 default:
360 return null;
361 }
362 }
363
364 /**
365 * Gets an object that represents an HDMI-CEC logical device of type playback on the system.
Jinsuk Kim91120c52014-05-08 17:12:51 +0900366 *
367 * <p>Used to send HDMI control messages to other devices like TV or audio amplifier through
368 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same
369 * system if the system is configured to host more than one type of HDMI-CEC logical devices.
370 *
371 * @return {@link HdmiPlaybackClient} instance. {@code null} on failure.
372 */
373 @Nullable
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -0600374 @SuppressLint("Doclava125")
Jinsuk Kim91120c52014-05-08 17:12:51 +0900375 public HdmiPlaybackClient getPlaybackClient() {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900376 return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK);
Jinsuk Kim91120c52014-05-08 17:12:51 +0900377 }
378
379 /**
Jinsuk Kim6ffb0382014-08-01 19:13:53 +0900380 * Gets an object that represents an HDMI-CEC logical device of type TV on the system.
Jinsuk Kim91120c52014-05-08 17:12:51 +0900381 *
382 * <p>Used to send HDMI control messages to other devices and manage them through
383 * HDMI bus. It is also possible to communicate with other logical devices hosted in the same
384 * system if the system is configured to host more than one type of HDMI-CEC logical devices.
385 *
386 * @return {@link HdmiTvClient} instance. {@code null} on failure.
387 */
388 @Nullable
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -0600389 @SuppressLint("Doclava125")
Jinsuk Kim91120c52014-05-08 17:12:51 +0900390 public HdmiTvClient getTvClient() {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900391 return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV);
Jinsuk Kim91120c52014-05-08 17:12:51 +0900392 }
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900393
394 /**
Shubang67373192018-06-08 18:30:15 -0700395 * Gets an object that represents an HDMI-CEC logical device of type audio system on the system.
396 *
397 * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
398 * possible to communicate with other logical devices hosted in the same system if the system is
399 * configured to host more than one type of HDMI-CEC logical devices.
400 *
401 * @return {@link HdmiAudioSystemClient} instance. {@code null} on failure.
402 *
403 * TODO(b/110094868): unhide for Q
404 * @hide
405 */
406 @Nullable
407 @SuppressLint("Doclava125")
408 public HdmiAudioSystemClient getAudioSystemClient() {
409 return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
410 }
411
412 /**
Amy17ee20f2018-10-11 11:08:23 -0700413 * Gets an object that represents an HDMI-CEC logical device of type switch on the system.
414 *
Amy9444cdd2019-01-16 13:42:30 -0800415 * <p>Used to send HDMI control messages to other devices (e.g. TVs) through HDMI bus.
416 * It is also possible to communicate with other logical devices hosted in the same
417 * system if the system is configured to host more than one type of HDMI-CEC logical device.
Amy17ee20f2018-10-11 11:08:23 -0700418 *
419 * @return {@link HdmiSwitchClient} instance. {@code null} on failure.
Amy17ee20f2018-10-11 11:08:23 -0700420 * @hide
421 */
422 @Nullable
Amy9444cdd2019-01-16 13:42:30 -0800423 @SystemApi
Amy17ee20f2018-10-11 11:08:23 -0700424 @SuppressLint("Doclava125")
425 public HdmiSwitchClient getSwitchClient() {
426 return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
427 }
428
429 /**
Amy6f031af2018-10-30 16:38:33 -0700430 * Get a snapshot of the real-time status of the remote devices.
431 *
Amy9444cdd2019-01-16 13:42:30 -0800432 * <p>This only applies to devices with multiple HDMI inputs.
Amy6f031af2018-10-30 16:38:33 -0700433 *
Amy9444cdd2019-01-16 13:42:30 -0800434 * @return a list of {@link HdmiDeviceInfo} of the connected CEC devices. An empty
435 * list will be returned if there is none.
436 *
Amy6f031af2018-10-30 16:38:33 -0700437 * @hide
438 */
Amy9444cdd2019-01-16 13:42:30 -0800439 @SystemApi
440 @Nullable
Amy6f031af2018-10-30 16:38:33 -0700441 public List<HdmiDeviceInfo> getConnectedDevicesList() {
442 try {
443 return mService.getDeviceList();
444 } catch (RemoteException e) {
445 throw e.rethrowFromSystemServer();
446 }
447 }
448
449 /**
Amy9444cdd2019-01-16 13:42:30 -0800450 * Power off the target device by sending CEC commands.
Amy6f031af2018-10-30 16:38:33 -0700451 *
Amy9444cdd2019-01-16 13:42:30 -0800452 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
Amy6f031af2018-10-30 16:38:33 -0700453 *
Amy9444cdd2019-01-16 13:42:30 -0800454 * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered off.
455 *
Amy6f031af2018-10-30 16:38:33 -0700456 * @hide
457 */
Amy9444cdd2019-01-16 13:42:30 -0800458 @SystemApi
Amy6f031af2018-10-30 16:38:33 -0700459 public void powerOffRemoteDevice(HdmiDeviceInfo deviceInfo) {
Amy9444cdd2019-01-16 13:42:30 -0800460 Preconditions.checkNotNull(deviceInfo);
Amy6f031af2018-10-30 16:38:33 -0700461 try {
462 mService.powerOffRemoteDevice(
463 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
464 } catch (RemoteException e) {
465 throw e.rethrowFromSystemServer();
466 }
467 }
468
469 /**
Amy9444cdd2019-01-16 13:42:30 -0800470 * Power on the target device by sending CEC commands.
Amy6f031af2018-10-30 16:38:33 -0700471 *
Amy9444cdd2019-01-16 13:42:30 -0800472 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
Amy6f031af2018-10-30 16:38:33 -0700473 *
Amy9444cdd2019-01-16 13:42:30 -0800474 * @param deviceInfo {@link HdmiDeviceInfo} of the device to be powered on.
475 *
Amy6f031af2018-10-30 16:38:33 -0700476 * @hide
477 */
478 public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) {
Amy9444cdd2019-01-16 13:42:30 -0800479 Preconditions.checkNotNull(deviceInfo);
Amy6f031af2018-10-30 16:38:33 -0700480 try {
481 mService.powerOnRemoteDevice(
482 deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus());
483 } catch (RemoteException e) {
484 throw e.rethrowFromSystemServer();
485 }
486 }
487
488 /**
Amy9444cdd2019-01-16 13:42:30 -0800489 * Request the target device to be the new Active Source by sending CEC commands.
490 *
491 * <p>The target device info can be obtained by calling {@link #getConnectedDevicesList()}.
Amy6f031af2018-10-30 16:38:33 -0700492 *
493 * @param deviceInfo HdmiDeviceInfo of the target device
494 *
Amy6f031af2018-10-30 16:38:33 -0700495 * @hide
496 */
Amy9444cdd2019-01-16 13:42:30 -0800497 @SystemApi
498 public void requestRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) {
499 Preconditions.checkNotNull(deviceInfo);
Amy6f031af2018-10-30 16:38:33 -0700500 try {
501 mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress());
502 } catch (RemoteException e) {
503 throw e.rethrowFromSystemServer();
504 }
505 }
506
507 /**
Donghyun Chob3515642017-03-02 13:47:40 +0900508 * Controls standby mode of the system. It will also try to turn on/off the connected devices if
509 * necessary.
510 *
511 * @param isStandbyModeOn target status of the system's standby mode
512 */
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -0600513 @RequiresPermission(android.Manifest.permission.HDMI_CEC)
Donghyun Chob3515642017-03-02 13:47:40 +0900514 public void setStandbyMode(boolean isStandbyModeOn) {
515 try {
516 mService.setStandbyMode(isStandbyModeOn);
517 } catch (RemoteException e) {
518 throw e.rethrowFromSystemServer();
519 }
520 }
521
522 /**
Shubang Lu00b976a2018-08-01 18:11:46 -0700523 * Gets whether the system is in system audio mode.
524 *
525 * @hide
526 */
527 public boolean getSystemAudioMode() {
528 try {
529 return mService.getSystemAudioMode();
530 } catch (RemoteException e) {
531 throw e.rethrowFromSystemServer();
532 }
533 }
534
535 /**
Amyd58d0aa2018-10-18 14:08:57 -0700536 * Get the physical address of the device.
537 *
Amy9444cdd2019-01-16 13:42:30 -0800538 * <p>Physical address needs to be automatically adjusted when devices are phyiscally or
539 * electrically added or removed from the device tree. Please see HDMI Specification Version
540 * 1.4b 8.7 Physical Address for more details on the address discovery proccess.
541 *
Amyd58d0aa2018-10-18 14:08:57 -0700542 * @hide
543 */
Amy9444cdd2019-01-16 13:42:30 -0800544 @SystemApi
Amyd58d0aa2018-10-18 14:08:57 -0700545 public int getPhysicalAddress() {
Amyff115f12018-11-21 14:26:16 -0800546 if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
547 return mPhysicalAddress;
548 }
Amyd58d0aa2018-10-18 14:08:57 -0700549 try {
Amyff115f12018-11-21 14:26:16 -0800550 mPhysicalAddress = mService.getPhysicalAddress();
551 return mPhysicalAddress;
Amyd58d0aa2018-10-18 14:08:57 -0700552 } catch (RemoteException e) {
553 throw e.rethrowFromSystemServer();
554 }
555 }
556
557 /**
Amy9444cdd2019-01-16 13:42:30 -0800558 * Check if the target remote device is connected to the current device.
559 *
560 * <p>The API also returns true if the current device is the target.
Amyff115f12018-11-21 14:26:16 -0800561 *
562 * @param targetDevice {@link HdmiDeviceInfo} of the target device.
Amy9444cdd2019-01-16 13:42:30 -0800563 * @return true if {@code targetDevice} is directly or indirectly
564 * connected to the current device.
Amyff115f12018-11-21 14:26:16 -0800565 *
Amyff115f12018-11-21 14:26:16 -0800566 * @hide
567 */
Amy9444cdd2019-01-16 13:42:30 -0800568 @SystemApi
569 public boolean isRemoteDeviceConnected(HdmiDeviceInfo targetDevice) {
570 Preconditions.checkNotNull(targetDevice);
Amyff115f12018-11-21 14:26:16 -0800571 mPhysicalAddress = getPhysicalAddress();
572 if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
573 return false;
574 }
575 int targetPhysicalAddress = targetDevice.getPhysicalAddress();
576 if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
577 return false;
578 }
579 return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress)
580 != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE;
581 }
582
583 /**
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900584 * Listener used to get hotplug event from HDMI port.
585 */
586 public interface HotplugEventListener {
587 void onReceived(HdmiHotplugEvent event);
588 }
589
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900590 private final ArrayMap<HotplugEventListener, IHdmiHotplugEventListener>
591 mHotplugEventListeners = new ArrayMap<>();
592
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900593 /**
Jinsuk Kim119160a2014-07-07 18:48:10 +0900594 * Listener used to get vendor-specific commands.
595 */
596 public interface VendorCommandListener {
597 /**
598 * Called when a vendor command is received.
599 *
600 * @param srcAddress source logical address
Yuncheol Heo0608b932014-10-13 16:39:18 +0900601 * @param destAddress destination logical address
Jinsuk Kim119160a2014-07-07 18:48:10 +0900602 * @param params vendor-specific parameters
603 * @param hasVendorId {@code true} if the command is &lt;Vendor Command
604 * With ID&gt;. The first 3 bytes of params is vendor id.
605 */
Yuncheol Heo0608b932014-10-13 16:39:18 +0900606 void onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId);
607
608 /**
609 * The callback is called:
610 * <ul>
611 * <li> before HdmiControlService is disabled.
612 * <li> after HdmiControlService is enabled and the local address is assigned.
613 * </ul>
614 * The client shouldn't hold the thread too long since this is a blocking call.
615 *
616 * @param enabled {@code true} if HdmiControlService is enabled.
617 * @param reason the reason code why the state of HdmiControlService is changed.
618 * @see #CONTROL_STATE_CHANGED_REASON_START
619 * @see #CONTROL_STATE_CHANGED_REASON_SETTING
620 * @see #CONTROL_STATE_CHANGED_REASON_WAKEUP
621 * @see #CONTROL_STATE_CHANGED_REASON_STANDBY
622 */
623 void onControlStateChanged(boolean enabled, int reason);
Jinsuk Kim119160a2014-07-07 18:48:10 +0900624 }
625
626 /**
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900627 * Adds a listener to get informed of {@link HdmiHotplugEvent}.
628 *
629 * <p>To stop getting the notification,
Jinsuk Kim9302a732014-05-22 13:24:55 +0900630 * use {@link #removeHotplugEventListener(HotplugEventListener)}.
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900631 *
632 * @param listener {@link HotplugEventListener} instance
Jinsuk Kim9302a732014-05-22 13:24:55 +0900633 * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900634 */
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -0600635 @RequiresPermission(android.Manifest.permission.HDMI_CEC)
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900636 public void addHotplugEventListener(HotplugEventListener listener) {
637 if (mService == null) {
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900638 Log.e(TAG, "HdmiControlService is not available");
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900639 return;
640 }
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900641 if (mHotplugEventListeners.containsKey(listener)) {
642 Log.e(TAG, "listener is already registered");
643 return;
644 }
645 IHdmiHotplugEventListener wrappedListener = getHotplugEventListenerWrapper(listener);
646 mHotplugEventListeners.put(listener, wrappedListener);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900647 try {
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900648 mService.addHotplugEventListener(wrappedListener);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900649 } catch (RemoteException e) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700650 throw e.rethrowFromSystemServer();
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900651 }
652 }
653
654 /**
655 * Removes a listener to stop getting informed of {@link HdmiHotplugEvent}.
656 *
657 * @param listener {@link HotplugEventListener} instance to be removed
658 */
Jeff Sharkeybfc4fcd2017-06-05 17:38:17 -0600659 @RequiresPermission(android.Manifest.permission.HDMI_CEC)
Jinsuk Kim9302a732014-05-22 13:24:55 +0900660 public void removeHotplugEventListener(HotplugEventListener listener) {
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900661 if (mService == null) {
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900662 Log.e(TAG, "HdmiControlService is not available");
663 return;
664 }
665 IHdmiHotplugEventListener wrappedListener = mHotplugEventListeners.remove(listener);
666 if (wrappedListener == null) {
667 Log.e(TAG, "tried to remove not-registered listener");
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900668 return;
669 }
670 try {
Yuncheol Heo2b0da5c2014-10-22 14:32:27 +0900671 mService.removeHotplugEventListener(wrappedListener);
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900672 } catch (RemoteException e) {
Jeff Sharkeyc53962d2016-03-01 19:27:23 -0700673 throw e.rethrowFromSystemServer();
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900674 }
675 }
676
677 private IHdmiHotplugEventListener getHotplugEventListenerWrapper(
678 final HotplugEventListener listener) {
679 return new IHdmiHotplugEventListener.Stub() {
Jungshik Jange5a93372014-07-25 13:41:14 +0900680 @Override
Jinsuk Kim78d695d2014-05-13 16:36:15 +0900681 public void onReceived(HdmiHotplugEvent event) {
682 listener.onReceived(event);;
683 }
684 };
685 }
Jinsuk Kim91120c52014-05-08 17:12:51 +0900686}