• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &lt;Vendor Command
698          *        With ID&gt;. 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