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

Commit 37554062 authored by Jungshik Jang's avatar Jungshik Jang Committed by Android (Google) Code Review
Browse files

Merge "DO NOT MERGE: Add cec message handler to hdmi cec jni implementation" into lmp-preview-dev

parents dc6adf5c c47e26ca
Loading
Loading
Loading
Loading
+7 −16
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiCecMessage;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;


@@ -29,7 +30,6 @@ import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import libcore.util.EmptyArray;
import libcore.util.EmptyArray;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.List;


/**
/**
@@ -99,7 +99,7 @@ final class HdmiCecController {
     */
     */
    static HdmiCecController create(HdmiControlService service) {
    static HdmiCecController create(HdmiControlService service) {
        HdmiCecController controller = new HdmiCecController();
        HdmiCecController controller = new HdmiCecController();
        long nativePtr = nativeInit(controller);
        long nativePtr = nativeInit(controller, service.getServiceLooper().getQueue());
        if (nativePtr == 0L) {
        if (nativePtr == 0L) {
            controller = null;
            controller = null;
            return null;
            return null;
@@ -471,8 +471,8 @@ final class HdmiCecController {


    private void onReceiveCommand(HdmiCecMessage message) {
    private void onReceiveCommand(HdmiCecMessage message) {
        assertRunOnServiceThread();
        assertRunOnServiceThread();
        if (isAcceptableAddress(message.getDestination()) &&
        if (isAcceptableAddress(message.getDestination())
                mService.handleCecCommand(message)) {
                && mService.handleCecCommand(message)) {
            return;
            return;
        }
        }


@@ -517,17 +517,8 @@ final class HdmiCecController {
     * Called by native when incoming CEC message arrived.
     * Called by native when incoming CEC message arrived.
     */
     */
    private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
    private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
        byte opcode = body[0];
        assertRunOnServiceThread();
        byte params[] = Arrays.copyOfRange(body, 1, body.length);
        onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
        final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);

        // Delegate message to main handler so that it handles in main thread.
        runOnServiceThread(new Runnable() {
            @Override
            public void run() {
                onReceiveCommand(cecMessage);
            }
        });
    }
    }


    /**
    /**
@@ -539,7 +530,7 @@ final class HdmiCecController {
        mService.onHotplug(0, connected);
        mService.onHotplug(0, connected);
    }
    }


    private static native long nativeInit(HdmiCecController handler);
    private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
    private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
    private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
            int dstAddress, byte[] body);
            int dstAddress, byte[] body);
    private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
    private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
+15 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.HdmiCecMessage;


import java.io.UnsupportedEncodingException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;


/**
/**
 * A helper class to build {@link HdmiCecMessage} from various cec commands.
 * A helper class to build {@link HdmiCecMessage} from various cec commands.
@@ -38,6 +39,20 @@ public class HdmiCecMessageBuilder {


    private HdmiCecMessageBuilder() {}
    private HdmiCecMessageBuilder() {}


    /**
     * Build {@link HdmiCecMessage} from raw data.
     *
     * @param src source address of command
     * @param dest destination address of command
     * @param body body of message. It includes opcode.
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage of(int src, int dest, byte[] body) {
        byte opcode = body[0];
        byte params[] = Arrays.copyOfRange(body, 1, body.length);
        return new HdmiCecMessage(src, dest, opcode, params);
    }

    /**
    /**
     * Build <Feature Abort> command. <Feature Abort> consists of
     * Build <Feature Abort> command. <Feature Abort> consists of
     * 1 byte original opcode and 1 byte reason fields with basic fields.
     * 1 byte original opcode and 1 byte reason fields with basic fields.
+147 −65
Original line number Original line Diff line number Diff line
@@ -21,12 +21,15 @@
#include "JNIHelp.h"
#include "JNIHelp.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedPrimitiveArray.h"


#include <string>
#include <cstring>


#include <android_os_MessageQueue.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <android_runtime/Log.h>
#include <hardware/hdmi_cec.h>
#include <hardware/hdmi_cec.h>
#include <sys/param.h>
#include <sys/param.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>


namespace android {
namespace android {


@@ -37,7 +40,8 @@ static struct {


class HdmiCecController {
class HdmiCecController {
public:
public:
    HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj);
    HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj,
            const sp<Looper>& looper);


    void init();
    void init();


@@ -54,28 +58,89 @@ public:
    // Get vendor id used for vendor command.
    // Get vendor id used for vendor command.
    uint32_t getVendorId();
    uint32_t getVendorId();


private:
    jobject getCallbacksObj() const {
    // Propagate the message up to Java layer.
        return mCallbacksObj;
    void propagateCecCommand(const cec_message_t& message);
    }
    void propagateHotplugEvent(const hotplug_event_t& event);


private:
    static void onReceived(const hdmi_event_t* event, void* arg);
    static void onReceived(const hdmi_event_t* event, void* arg);
    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);


    hdmi_cec_device_t* mDevice;
    hdmi_cec_device_t* mDevice;
    jobject mCallbacksObj;
    jobject mCallbacksObj;
    sp<Looper> mLooper;
};
};


HdmiCecController::HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj) :
// RefBase wrapper for hdmi_event_t. As hdmi_event_t coming from HAL
    mDevice(device),
// may keep its own lifetime, we need to copy it in order to delegate
    mCallbacksObj(callbacksObj) {
// it to service thread.
class CecEventWrapper : public LightRefBase<CecEventWrapper> {
public:
    CecEventWrapper(const hdmi_event_t& event) {
        // Copy message.
        switch (event.type) {
        case HDMI_EVENT_CEC_MESSAGE:
            mEvent.cec.initiator = event.cec.initiator;
            mEvent.cec.destination = event.cec.destination;
            mEvent.cec.length = event.cec.length;
            std::memcpy(mEvent.cec.body, event.cec.body, event.cec.length);
            break;
        case HDMI_EVENT_HOT_PLUG:
            mEvent.hotplug.connected = event.hotplug.connected;
            mEvent.hotplug.port = event.hotplug.port;
            break;
        case HDMI_EVENT_TX_STATUS:
            mEvent.tx_status.status = event.tx_status.status;
            mEvent.tx_status.opcode = event.tx_status.opcode;
            break;
        default:
            // TODO: add more type whenever new type is introduced.
            break;
        }
    }
    }


void HdmiCecController::init() {
    const cec_message_t& cec() const {
    mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this);
        return mEvent.cec;
    }

    const hotplug_event_t& hotplug() const {
        return mEvent.hotplug;
    }
    }


void HdmiCecController::propagateCecCommand(const cec_message_t& message) {
    virtual ~CecEventWrapper() {}

private:
    hdmi_event_t mEvent;
};

// Handler class to delegate incoming message to service thread.
class HdmiCecEventHandler : public MessageHandler {
public:
    HdmiCecEventHandler(HdmiCecController* controller, const sp<CecEventWrapper>& event)
        : mController(controller),
          mEventWrapper(event) {
    }

    virtual ~HdmiCecEventHandler() {}

    void handleMessage(const Message& message) {
        switch (message.what) {
        case HDMI_EVENT_CEC_MESSAGE:
            propagateCecCommand(mEventWrapper->cec());
            break;
        case HDMI_EVENT_HOT_PLUG:
            propagateHotplugEvent(mEventWrapper->hotplug());
            break;
        case HDMI_EVENT_TX_STATUS:
            // TODO: propagate this to controller.
        default:
            // TODO: add more type whenever new type is introduced.
            break;
        }
    }

private:
    // Propagate the message up to Java layer.
    void propagateCecCommand(const cec_message_t& message) {
        jint srcAddr = message.initiator;
        jint srcAddr = message.initiator;
        jint dstAddr = message.destination;
        jint dstAddr = message.destination;
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -83,22 +148,47 @@ void HdmiCecController::propagateCecCommand(const cec_message_t& message) {
        const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body);
        const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body);
        env->SetByteArrayRegion(body, 0, message.length, bodyPtr);
        env->SetByteArrayRegion(body, 0, message.length, bodyPtr);


    env->CallVoidMethod(mCallbacksObj,
        env->CallVoidMethod(mController->getCallbacksObj(),
            gHdmiCecControllerClassInfo.handleIncomingCecCommand,
                gHdmiCecControllerClassInfo.handleIncomingCecCommand, srcAddr,
            srcAddr, dstAddr, body);
                dstAddr, body);
        env->DeleteLocalRef(body);
        env->DeleteLocalRef(body);


        checkAndClearExceptionFromCallback(env, __FUNCTION__);
        checkAndClearExceptionFromCallback(env, __FUNCTION__);
    }
    }


void HdmiCecController::propagateHotplugEvent(const hotplug_event_t& event) {
    void propagateHotplugEvent(const hotplug_event_t& event) {
        // Note that this method should be called in service thread.
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->CallVoidMethod(mCallbacksObj,
        env->CallVoidMethod(mController->getCallbacksObj(),
                gHdmiCecControllerClassInfo.handleHotplug, event.connected);
                gHdmiCecControllerClassInfo.handleHotplug, event.connected);


        checkAndClearExceptionFromCallback(env, __FUNCTION__);
        checkAndClearExceptionFromCallback(env, __FUNCTION__);
    }
    }


    // static
    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
        if (env->ExceptionCheck()) {
            ALOGE("An exception was thrown by callback '%s'.", methodName);
            LOGE_EX(env);
            env->ExceptionClear();
        }
    }

    HdmiCecController* mController;
    sp<CecEventWrapper> mEventWrapper;
};

HdmiCecController::HdmiCecController(hdmi_cec_device_t* device,
        jobject callbacksObj, const sp<Looper>& looper) :
    mDevice(device),
    mCallbacksObj(callbacksObj),
    mLooper(looper) {
}

void HdmiCecController::init() {
    mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this);
}

int HdmiCecController::sendMessage(const cec_message_t& message) {
int HdmiCecController::sendMessage(const cec_message_t& message) {
    // TODO: propagate send_message's return value.
    // TODO: propagate send_message's return value.
    return mDevice->send_message(mDevice, &message);
    return mDevice->send_message(mDevice, &message);
@@ -132,15 +222,6 @@ uint32_t HdmiCecController::getVendorId() {
    return vendorId;
    return vendorId;
}
}


// static
void HdmiCecController::checkAndClearExceptionFromCallback(JNIEnv* env,
        const char* methodName) {
    if (env->ExceptionCheck()) {
        ALOGE("An exception was thrown by callback '%s'.", methodName);
        LOGE_EX(env);
        env->ExceptionClear();
    }
}


// static
// static
void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
@@ -149,17 +230,9 @@ void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
        return;
        return;
    }
    }


    switch (event->type) {
    sp<CecEventWrapper> spEvent(new CecEventWrapper(*event));
    case HDMI_EVENT_CEC_MESSAGE:
    sp<HdmiCecEventHandler> handler(new HdmiCecEventHandler(controller, spEvent));
        controller->propagateCecCommand(event->cec);
    controller->mLooper->sendMessage(handler, event->type);
        break;
    case HDMI_EVENT_HOT_PLUG:
        controller->propagateHotplugEvent(event->hotplug);
        break;
    default:
        ALOGE("Unsupported event type: %d", event->type);
        break;
    }
}
}


//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
@@ -167,31 +240,38 @@ void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
        LOG_FATAL_IF(! var, "Unable to find method " methodName);
        LOG_FATAL_IF(! var, "Unable to find method " methodName);


static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
// TODO: replace above code with following once
// replace old HdmiCecService with HdmiControlService
#undef HDMI_CEC_HARDWARE_MODULE_ID
#define HDMI_CEC_HARDWARE_MODULE_ID "hdmi_cec_module"
#undef HDMI_CEC_HARDWARE_INTERFACE
#define HDMI_CEC_HARDWARE_INTERFACE "hdmi_cec_module_hw_if"

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj,
        jobject messageQueueObj) {
    int err;
    int err;
    // If use same hardware module id between HdmiCecService and
    // HdmiControlSservice it may conflict and cause abnormal state of HAL.
    // TODO: use HDMI_CEC_HARDWARE_MODULE_ID of hdmi_cec.h for module id
    //       once migration to HdmiControlService is done.
    hw_module_t* module;
    hw_module_t* module;
    err = hw_get_module("hdmi_cec_module",
    err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID,
            const_cast<const hw_module_t **>(&module));
            const_cast<const hw_module_t **>(&module));
    if (err != 0) {
    if (err != 0) {
        ALOGE("Error acquiring hardware module: %d", err);
        ALOGE("Error acquiring hardware module: %d", err);
        return 0;
        return 0;
    }
    }

    hw_device_t* device;
    hw_device_t* device;
    // TODO: use HDMI_CEC_HARDWARE_INTERFACE of hdmi_cec.h for interface name
    err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
    //       once migration to HdmiControlService is done.
    err = module->methods->open(module, "hdmi_cec_module_hw_if", &device);
    if (err != 0) {
    if (err != 0) {
        ALOGE("Error opening hardware module: %d", err);
        ALOGE("Error opening hardware module: %d", err);
        return 0;
        return 0;
    }
    }


    sp<MessageQueue> messageQueue =
            android_os_MessageQueue_getMessageQueue(env, messageQueueObj);

    HdmiCecController* controller = new HdmiCecController(
    HdmiCecController* controller = new HdmiCecController(
            reinterpret_cast<hdmi_cec_device*>(device),
            reinterpret_cast<hdmi_cec_device*>(device),
            env->NewGlobalRef(callbacksObj));
            env->NewGlobalRef(callbacksObj),
            messageQueue->getLooper());
    controller->init();
    controller->init();


    GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz,
    GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz,
@@ -255,7 +335,8 @@ static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) {


static JNINativeMethod sMethods[] = {
static JNINativeMethod sMethods[] = {
    /* name, signature, funcPtr */
    /* name, signature, funcPtr */
    { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J",
    { "nativeInit",
      "(Lcom/android/server/hdmi/HdmiCecController;Landroid/os/MessageQueue;)J",
      (void *) nativeInit },
      (void *) nativeInit },
    { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand },
    { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand },
    { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress },
    { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress },
@@ -268,7 +349,8 @@ static JNINativeMethod sMethods[] = {
#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"
#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"


int register_android_server_hdmi_HdmiCecController(JNIEnv* env) {
int register_android_server_hdmi_HdmiCecController(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods,
            NELEM(sMethods));
    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
    return 0;
    return 0;
}
}