Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3fe569bc authored by Marvin Ramin's avatar Marvin Ramin
Browse files

Implement CEC HAL v1.1

Add bindings to the CEC HAL v1.1. v1.1 is preferred and used if present.
Otherwise fallback to v1.0.

Bug: 169121290
Test: run on cf_tv

Change-Id: I1bc8193989480197c55182cd3acc75af62fb5c85
parent ce7bd347
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ java_library_static {
        "android.hardware.health-V2.0-java",
        "android.hardware.health-V2.1-java",
        "android.hardware.light-V1-java",
        "android.hardware.tv.cec-V1.0-java",
        "android.hardware.tv.cec-V1.1-java",
        "android.hardware.weaver-V1.0-java",
        "android.hardware.biometrics.face-V1.1-java",
        "android.hardware.biometrics.face-V1-java",
+255 −10
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.function.Predicate;

@@ -149,6 +150,12 @@ final class HdmiCecController {
     *         returns {@code null}.
     */
    static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
        HdmiCecController controller = createWithNativeWrapper(service, new NativeWrapperImpl11(),
                atomWriter);
        if (controller != null) {
            return controller;
        }
        HdmiLogger.warning("Unable to use cec@1.1");
        return createWithNativeWrapper(service, new NativeWrapperImpl(), atomWriter);
    }

@@ -312,7 +319,7 @@ final class HdmiCecController {
    }

    /**
     * Return CEC version of the device.
     * Return highest CEC version supported by this device.
     *
     * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
     */
@@ -745,13 +752,202 @@ final class HdmiCecController {
        boolean nativeIsConnected(int port);
    }

    private static final class NativeWrapperImpl implements NativeWrapper,
    private static final class NativeWrapperImpl11 implements NativeWrapper,
            IHwBinder.DeathRecipient, getPhysicalAddressCallback {
        private IHdmiCec mHdmiCec;
        private android.hardware.tv.cec.V1_1.IHdmiCec mHdmiCec;
        @Nullable private HdmiCecCallback mCallback;

        private final Object mLock = new Object();
        private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;

        @Override
        public String nativeInit() {
            return (connectToHal() ? mHdmiCec.toString() : null);
        }

        boolean connectToHal() {
            try {
                mHdmiCec = android.hardware.tv.cec.V1_1.IHdmiCec.getService(true);
                try {
                    mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
                } catch (RemoteException e) {
                    HdmiLogger.error("Couldn't link to death : ", e);
                }
            } catch (RemoteException | NoSuchElementException e) {
                HdmiLogger.error("Couldn't connect to cec@1.1", e);
                return false;
            }
            return true;
        }

        @Override
        public void onValues(int result, short addr) {
            if (result == Result.SUCCESS) {
                synchronized (mLock) {
                    mPhysicalAddress = new Short(addr).intValue();
                }
            }
        }

        @Override
        public void serviceDied(long cookie) {
            if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
                HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting");
                connectToHal();
                // Reconnect the callback
                if (mCallback != null) {
                    setCallback(mCallback);
                }
            }
        }

        @Override
        public void setCallback(HdmiCecCallback callback) {
            mCallback = callback;
            try {
                mHdmiCec.setCallback_1_1(new HdmiCecCallback11(callback));
            } catch (RemoteException e) {
                HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
            }
        }

        @Override
        public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
            android.hardware.tv.cec.V1_1.CecMessage message =
                    new android.hardware.tv.cec.V1_1.CecMessage();
            message.initiator = srcAddress;
            message.destination = dstAddress;
            message.body = new ArrayList<>(body.length);
            for (byte b : body) {
                message.body.add(b);
            }
            try {
                return mHdmiCec.sendMessage_1_1(message);
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to send CEC message : ", e);
                return SendMessageResult.FAIL;
            }
        }

        @Override
        public int nativeAddLogicalAddress(int logicalAddress) {
            try {
                return mHdmiCec.addLogicalAddress_1_1(logicalAddress);
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to add a logical address : ", e);
                return Result.FAILURE_INVALID_ARGS;
            }
        }

        @Override
        public void nativeClearLogicalAddress() {
            try {
                mHdmiCec.clearLogicalAddress();
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to clear logical address : ", e);
            }
        }

        @Override
        public int nativeGetPhysicalAddress() {
            try {
                mHdmiCec.getPhysicalAddress(this);
                return mPhysicalAddress;
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to get physical address : ", e);
                return INVALID_PHYSICAL_ADDRESS;
            }
        }

        @Override
        public int nativeGetVersion() {
            try {
                return mHdmiCec.getCecVersion();
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to get cec version : ", e);
                return Result.FAILURE_UNKNOWN;
            }
        }

        @Override
        public int nativeGetVendorId() {
            try {
                return mHdmiCec.getVendorId();
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to get vendor id : ", e);
                return Result.FAILURE_UNKNOWN;
            }
        }

        @Override
        public HdmiPortInfo[] nativeGetPortInfos() {
            try {
                ArrayList<android.hardware.tv.cec.V1_0.HdmiPortInfo> hdmiPortInfos =
                        mHdmiCec.getPortInfo();
                HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
                int i = 0;
                for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
                    hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
                            portInfo.type,
                            portInfo.physicalAddress,
                            portInfo.cecSupported,
                            false,
                            portInfo.arcSupported);
                    i++;
                }
                return hdmiPortInfo;
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to get port information : ", e);
                return null;
            }
        }

        @Override
        public void nativeSetOption(int flag, boolean enabled) {
            try {
                mHdmiCec.setOption(flag, enabled);
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to set option : ", e);
            }
        }

        @Override
        public void nativeSetLanguage(String language) {
            try {
                mHdmiCec.setLanguage(language);
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to set language : ", e);
            }
        }

        @Override
        public void nativeEnableAudioReturnChannel(int port, boolean flag) {
            try {
                mHdmiCec.enableAudioReturnChannel(port, flag);
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to enable/disable ARC : ", e);
            }
        }

        @Override
        public boolean nativeIsConnected(int port) {
            try {
                return mHdmiCec.isConnected(port);
            } catch (RemoteException e) {
                HdmiLogger.error("Failed to get connection info : ", e);
                return false;
            }
        }
    }

    private static final class NativeWrapperImpl implements NativeWrapper,
            IHwBinder.DeathRecipient, getPhysicalAddressCallback {
        private android.hardware.tv.cec.V1_0.IHdmiCec mHdmiCec;
        @Nullable private HdmiCecCallback mCallback;

        private final Object mLock = new Object();
        private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;

        @Override
        public String nativeInit() {
            return (connectToHal() ? mHdmiCec.toString() : null);
@@ -765,8 +961,8 @@ final class HdmiCecController {
                } catch (RemoteException e) {
                    HdmiLogger.error("Couldn't link to death : ", e);
                }
            } catch (RemoteException e) {
                HdmiLogger.error("Couldn't get tv.cec service : ", e);
            } catch (RemoteException | NoSuchElementException e) {
                HdmiLogger.error("Couldn't connect to cec@1.0", e);
                return false;
            }
            return true;
@@ -776,7 +972,7 @@ final class HdmiCecController {
        public void setCallback(@NonNull HdmiCecCallback callback) {
            mCallback = callback;
            try {
                mHdmiCec.setCallback(callback);
                mHdmiCec.setCallback(new HdmiCecCallback10(callback));
            } catch (RemoteException e) {
                HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
            }
@@ -931,20 +1127,69 @@ final class HdmiCecController {
        }
    }

    final class HdmiCecCallback extends IHdmiCecCallback.Stub {
    final class HdmiCecCallback {
        public void onCecMessage(int initiator, int destination, byte[] body) {
            runOnServiceThread(
                    () -> handleIncomingCecCommand(initiator, destination, body));
        }

        public void onHotplugEvent(int portId, boolean connected) {
            runOnServiceThread(() -> handleHotplug(portId, connected));
        }
    }

    private static final class HdmiCecCallback10 extends IHdmiCecCallback.Stub {
        private final HdmiCecCallback mHdmiCecCallback;

        HdmiCecCallback10(HdmiCecCallback hdmiCecCallback) {
            mHdmiCecCallback = hdmiCecCallback;
        }

        @Override
        public void onCecMessage(CecMessage message) throws RemoteException {
            byte[] body = new byte[message.body.size()];
            for (int i = 0; i < message.body.size(); i++) {
                body[i] = message.body.get(i);
            }
            runOnServiceThread(
                    () -> handleIncomingCecCommand(message.initiator, message.destination, body));
            mHdmiCecCallback.onCecMessage(message.initiator, message.destination, body);
        }

        @Override
        public void onHotplugEvent(HotplugEvent event) throws RemoteException {
            mHdmiCecCallback.onHotplugEvent(event.portId, event.connected);
        }
    }

    private static final class HdmiCecCallback11
            extends android.hardware.tv.cec.V1_1.IHdmiCecCallback.Stub {
        private final HdmiCecCallback mHdmiCecCallback;

        HdmiCecCallback11(HdmiCecCallback hdmiCecCallback) {
            mHdmiCecCallback = hdmiCecCallback;
        }

        @Override
        public void onCecMessage_1_1(android.hardware.tv.cec.V1_1.CecMessage message)
                throws RemoteException {
            byte[] body = new byte[message.body.size()];
            for (int i = 0; i < message.body.size(); i++) {
                body[i] = message.body.get(i);
            }
            mHdmiCecCallback.onCecMessage(message.initiator, message.destination, body);
        }

        @Override
        public void onCecMessage(CecMessage message) throws RemoteException {
            byte[] body = new byte[message.body.size()];
            for (int i = 0; i < message.body.size(); i++) {
                body[i] = message.body.get(i);
            }
            mHdmiCecCallback.onCecMessage(message.initiator, message.destination, body);
        }

        @Override
        public void onHotplugEvent(HotplugEvent event) throws RemoteException {
            runOnServiceThread(() -> handleHotplug(event.portId, event.connected));
            mHdmiCecCallback.onHotplugEvent(event.portId, event.connected);
        }
    }

+10 −26
Original line number Diff line number Diff line
@@ -15,12 +15,9 @@
 */
package com.android.server.hdmi;

import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.CecMessage;
import android.hardware.tv.cec.V1_0.HotplugEvent;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
@@ -99,7 +96,7 @@ final class FakeNativeWrapper implements NativeWrapper {

    @Override
    public int nativeGetVersion() {
        return 0;
        return HdmiControlManager.HDMI_CEC_VERSION_2_0;
    }

    @Override
@@ -139,20 +136,15 @@ final class FakeNativeWrapper implements NativeWrapper {
        if (mCallback == null) {
            return;
        }
        CecMessage message = new CecMessage();
        message.initiator = hdmiCecMessage.getSource();
        message.destination = hdmiCecMessage.getDestination();
        ArrayList<Byte> body = new ArrayList<>();
        body.add((byte) hdmiCecMessage.getOpcode());
        int source = hdmiCecMessage.getSource();
        int destination = hdmiCecMessage.getDestination();
        byte[] body = new byte[hdmiCecMessage.getParams().length + 1];
        int i = 0;
        body[i++] = (byte) hdmiCecMessage.getOpcode();
        for (byte param : hdmiCecMessage.getParams()) {
            body.add(param);
        }
        message.body = body;
        try {
            mCallback.onCecMessage(message);
        } catch (RemoteException e) {
            Log.e(TAG, "Error sending CEC message", e);
            body[i++] = param;
        }
        mCallback.onCecMessage(source, destination, body);
    }

    public void onHotplugEvent(int port, boolean connected) {
@@ -160,15 +152,7 @@ final class FakeNativeWrapper implements NativeWrapper {
            return;
        }

        HotplugEvent hotplugEvent = new HotplugEvent();
        hotplugEvent.portId = port;
        hotplugEvent.connected = connected;

        try {
            mCallback.onHotplugEvent(hotplugEvent);
        } catch (RemoteException e) {
            Log.e(TAG, "Error sending hotplug event", e);
        }
        mCallback.onHotplugEvent(port, connected);
    }

    public List<HdmiCecMessage> getResultMessages() {