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