stackoverflow.com/questions/49488944/...

лкнулся с проблемой доступа к HDMI CEC наэто ключ для Android.

Я пытаюсь включить телевизор и изменить источник входного сигнала телевизора, но я не смог этого сделать.

Подход Android API

Я запускаю системное приложение, и я решил

<uses-permission android:name="android.permission.HDMI_CEC" /> 

на AndroidManifest.xml.

Я получаю доступ к услуге HDMI через рефлексию, так как не смог получить к ней прямой доступ, даже будучи системным приложением.

public class HdmiHelper {

    public HdmiHelper(Context context) {
        init(context);
    }

    public void init(Context context) {

        try {

            //Interface Callback Proxy
            Class<?> hotplugEventListenerClass = Class.forName("android.hardware.hdmi.HdmiControlManager$HotplugEventListener");
            Class<?> vendorCommandListenerClass = Class.forName("android.hardware.hdmi.HdmiControlManager$VendorCommandListener");
            Class<?> oneTouchPlayCallbackClass = Class.forName("android.hardware.hdmi.HdmiPlaybackClient$OneTouchPlayCallback");
            Class<?> displayStatusCallbackClass = Class.forName("android.hardware.hdmi.HdmiPlaybackClient$DisplayStatusCallback");

            Object interfaceOneTouchPlaybackCallback = Proxy.newProxyInstance(oneTouchPlayCallbackClass.getClassLoader(),
                    new Class<?>[]{ oneTouchPlayCallbackClass } , new callbackProxyListener() );

            Object interfaceHotplugEventCallback = Proxy.newProxyInstance(hotplugEventListenerClass.getClassLoader(),
                    new Class<?>[]{ hotplugEventListenerClass } , new callbackProxyListener() );

            Object interfaceDisplayStatusCallbackClass = Proxy.newProxyInstance(displayStatusCallbackClass.getClassLoader(),
                    new Class<?>[]{ displayStatusCallbackClass } , new callbackProxyListener() );


            Method m = context.getClass().getMethod("getSystemService", String.class);
            Object obj_HdmiControlManager = m.invoke(context, (Object) "hdmi_control");

            Log.d("HdmiHelper", "obj: " + obj_HdmiControlManager + " | " + obj_HdmiControlManager.getClass());

            for( Method method : obj_HdmiControlManager.getClass().getMethods()) {
                Log.d("HdmiHelper", "   method: " + method.getName() );
            }


            Method method_addHotplugEventListener = obj_HdmiControlManager.getClass().getMethod("addHotplugEventListener", hotplugEventListenerClass);
            method_addHotplugEventListener.invoke(obj_HdmiControlManager, interfaceHotplugEventCallback);


            Method m2 = obj_HdmiControlManager.getClass().getMethod("getPlaybackClient");
            Object obj_HdmiPlaybackClient = m2.invoke( obj_HdmiControlManager );
            Log.d("HdmiHelper", "obj_HdmiPlaybackClient: " + obj_HdmiPlaybackClient + " | " + obj_HdmiPlaybackClient.getClass());

            Method method_oneTouchPlay = obj_HdmiPlaybackClient.getClass().getMethod("oneTouchPlay", oneTouchPlayCallbackClass);
            method_oneTouchPlay.invoke( obj_HdmiPlaybackClient, interfaceOneTouchPlaybackCallback);


            Method method_queryDisplayStatus = obj_HdmiPlaybackClient.getClass().getMethod("queryDisplayStatus", displayStatusCallbackClass);

            method_queryDisplayStatus.invoke( obj_HdmiPlaybackClient, interfaceDisplayStatusCallbackClass);

            Method method_getActiveSource = obj_HdmiPlaybackClient.getClass().getMethod("getActiveSource");
            Log.d("HdmiHelper", "getActiveSource: " + method_getActiveSource.invoke(obj_HdmiPlaybackClient));


        }catch (Exception e) {
            e.printStackTrace();
        }

    }

    public class callbackProxyListener implements java.lang.reflect.InvocationHandler {

        public callbackProxyListener() {

        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            try {
                Log.d("HdmiHelper", "Start method " + method.getName() + " | " + proxy.getClass() + " | " + method.getDeclaringClass() );

                if ( args != null ) {

                    // Prints the method being invoked
                    for (int i = 0; i != args.length; i++) {
                        Log.d("HdmiHelper", "  - Arg(" + i + "): " + args[i].toString());
                    }

                }

                if (method.getName().equals("onReceived")) {

                    if (args.length == 1) {
                        onReceived(args[0]);
                    }else
                    if (args.length == 3) {
                        onReceived( (int) args[0], BytesUtil.toByteArray( args[1] ), (boolean) args[2]  );
                    }


                }else
                if (method.getName().equals("onComplete")) {
                    onComplete( (int) args[0] );
                }else
                if (method.getName().equals("toString")) {
                    return this.toString();
                }else {
                    return method.invoke(this, args);
                }

            }catch (Exception e) {
                e.printStackTrace();
            }

            return null;
        }

        void onComplete(int result) {
            Log.d("HdmiHelper", "onComplete: " + result);
        }

        void onReceived(Object event) {

            Class eventClass = event.getClass();

            Log.d("HdmiHelper", "onReceived(1): " + event.toString() + " | " + eventClass);

            try {
                Method method_getPort = eventClass.getMethod("getPort");
                Method method_isConnected = eventClass.getMethod("isConnected");
                Method method_describeContents = eventClass.getMethod("describeContents");

                Log.d("HdmiHelper", "    - " + method_getPort.invoke(event) + " | " + method_isConnected.invoke(event) + " | " + method_describeContents.invoke(event) );

            }catch (Exception e) {

                e.printStackTrace();

            }


        }


        void onReceived(int srcAddress, byte[] params, boolean hasVendorId) {
            Log.d("HdmiHelper", "onReceived(3): " + srcAddress + " | " + params + " | " + hasVendorId);
        }


    }

Журнал ответов:

D/HdmiHelper: obj: android.hardware.hdmi.HdmiControlManager@7bca63c | class android.hardware.hdmi.HdmiControlManager

D/HdmiHelper: obj_HdmiPlaybackClient: android.hardware.hdmi.HdmiPlaybackClient@6345d1a | class android.hardware.hdmi.HdmiPlaybackClient

D/HdmiHelper: Start method onReceived | class $Proxy2 | interface android.hardware.hdmi.HdmiControlManager$HotplugEventListener
D/HdmiHelper: onReceived(1): android.hardware.hdmi.HdmiHotplugEvent@4c5c04b | class android.hardware.hdmi.HdmiHotplugEvent
D/HdmiHelper:     - 1 | true | 0  
Вопрос 1:

Я получил правду: это означает, что телевидение на том, что правда. Если телевизор выключен, я получаю false. Это похоже на работу.

Хотя я ожидал, что получу обратный вызов каждый раз, когда меняю состояние телевизора, чего не происходит. Любая идея?

Вопрос 2:

продолжить с журналами для OneTouchPlayCallback:

D/HdmiHelper: Start method onComplete | class $Proxy1 | interface android.hardware.hdmi.HdmiPlaybackClient$OneTouchPlayCallback
D/HdmiHelper: onComplete: 2

Смотреть в классHdmiPlaybackClient.java если все прошло хорошо, ответом будет 0 (@param result результат операции. {@link HdmiControlManager # RESULT_SUCCESS. Вы можете найти эту переменную вHdmiControlManager.java класс}. Вместо этого я получаю 2, которые я предполагаю, что это RESULT_SOURCE_NOT_AVAILABLE.

Есть идеи почему?

Вопрос 3

Продолжаем теперь с журналами для DisplayStatusCallback:

D/HdmiHelper: Start method onComplete | class $Proxy3 | interface android.hardware.hdmi.HdmiPlaybackClient$DisplayStatusCallback
D/HdmiHelper: onComplete: 2

Согласно определению этого обратного вызова:

/**
     * Listener used by the client to get display device status.
     */
    public interface DisplayStatusCallback {
        /**
         * Called when display device status is reported.
         *
         * @param status display device status. It should be one of the following values.
         *            <ul>
         *            <li>{@link HdmiControlManager#POWER_STATUS_ON}
         *            <li>{@link HdmiControlManager#POWER_STATUS_STANDBY}
         *            <li>{@link HdmiControlManager#POWER_STATUS_TRANSIENT_TO_ON}
         *            <li>{@link HdmiControlManager#POWER_STATUS_TRANSIENT_TO_STANDBY}
         *            <li>{@link HdmiControlManager#POWER_STATUS_UNKNOWN}
         *            </ul>
         */
        public void onComplete(int status);
    }

и глядя в HdmiControlManager я получаю 2, что означает:

public static final int POWER_STATUS_TRANSIENT_TO_ON = 2;

Что является странным результатом, потому что это не то, что происходит.

Продолжая с журналами для вашей информации:

Ответ за:

getActiveSource равен нулю

Я также протестировал этот код, который вызывает метод getTvClient ():

Method method_getTvClient = obj_HdmiControlManager.getClass().getMethod("getTvClient");
Object obj_HdmiTvClient = method_getTvClient.invoke( obj_HdmiControlManager );
Log.d("HdmiHelper", "obj_HdmiTvClient: " + obj_HdmiTvClient);

и результат ноль.

Я также попробовал подход отправки команды вендора послеСайт CEC-O-MATIC но я не смог добиться успеха. Если у вас есть какие-либо инструкции по этому поводу, пожалуйста, дайте мне несколько указаний, и я проверю это.

Подход LibCEC:

Благодаря этому я смог скомпилировать libcec на androidПочта, Но libcec всегда отвечает мне: «Команда« PING »не была подтверждена контроллером».

Я добавил флаги -DHAVE_EXYNOS_API = 1 и -DHAVE_AOCEC_API = 1 в libcec.

Системная информация

Устройство / dev / cec устанавливается:

q8723bs:/ # ls -l /dev/cec                                                                                                                                              
crw-rw-rw- 1 root root 218,   0 2017-12-19 16:33 /dev/cec

Я также могу найти его в / sys / class / cec:

q8723bs:/ # ls -laht /sys/class/cec/                                                                                                                                    
total 0
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 arc_port
lrwxrwxrwx   1 root root    0 2017-12-19 16:45 cec -> ../../devices/aocec/cec
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 cec_version
--w-------   1 root root 4.0K 2017-12-19 16:45 cmd
-rw-rw-r--   1 root root 4.0K 2017-12-19 16:45 dbg_en
-rw-rw-r--   1 root root 4.0K 2017-12-19 16:45 device_type
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 dump_reg
-rw-rw-r--   1 root root 4.0K 2017-12-19 16:45 fun_cfg
-rw-rw-r--   1 root root 4.0K 2017-12-19 16:45 menu_language
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 osd_name
-rw-rw-r--   1 root root 4.0K ,2017-12-19 16:45 physical_addr
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 pin_status
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 port_num
-rw-rw-r--   1 root root 4.0K 2017-12-19 16:45 port_seq
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 port_status
-rw-rw-r--   1 root root 4.0K 2017-12-19 16:45 vendor_id
-r--r--r--   1 root root 4.0K 2017-12-19 16:45 wake_up

Но когда я запускаю cec-client, я получаю такой ответ:

q8723bs:/ # id
uid=0(root) gid=0(root) groups=0(root) context=u:r:toolbox:s0
q8723bs:/ # cec-client -s /dev/cec                                                                                                                                      
opening a connection to the CEC adapter...
DEBUG:   [               1] Broadcast (F): osd name set to 'Broadcast'
DEBUG:   [               2] connection opened, clearing any previous input and waiting for active transmissions to end before starting
DEBUG:   [             396] communication thread started
DEBUG:   [            1396] command 'PING' was not acked by the controller

Как примечание, у меня также есть устройство / dev / input / event2, которое доступно только для чтения cec_input:

q8723bs:/ # ls -l /dev/input/event2                                                                                                                                     
crw-rw---- 1 root input 13,  66 2017-12-19 16:33 /dev/input/event2
q8723bs:/ # ls /sys/devices/virtual/input/input2/                                                                                                                      
capabilities/  event2/        id/            modalias       name           phys           power/         properties     subsystem/     uevent         uniq
q8723bs:/ # cat /sys/devices/virtual/input/input2/name                                                                                                                  
cec_input

Я попытался запустить его на / dev / input / event2, но, очевидно, это не сработало, потому что не удалось открыть соединение:

q8723bs:/ # cec-client /dev/input/event2                                                                                                                                
No device type given. Using 'recording device'
CEC Parser created - libCEC version 4.0.2
opening a connection to the CEC adapter...
DEBUG:   [               1] Broadcast (F): osd name set to 'Broadcast'
ERROR:   [            3335] error opening serial port '/dev/input/event2': Couldn't lock the serial port
ERROR:   [            3335] could not open a connection (try 1)

В итоге:

Я не мог заставить команду включить или изменить источник входного сигнала телевизора, работающего в любом из случаев. Любое направление будет очень полезным. Заранее спасибо.

примечание: я смог сделать это с libcec и raspberry pi на одном телевизоре

Ответы на вопрос(1)

Ваш ответ на вопрос