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