| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "HdmiCecControllerJni" |
| |
| #define LOG_NDEBUG 1 |
| |
| #include "JNIHelp.h" |
| #include "ScopedPrimitiveArray.h" |
| |
| #include <string> |
| |
| #include <android_runtime/AndroidRuntime.h> |
| #include <android_runtime/Log.h> |
| #include <hardware/hdmi_cec.h> |
| #include <sys/param.h> |
| |
| namespace android { |
| |
| static struct { |
| jmethodID handleIncomingCecCommand; |
| jmethodID handleHotplug; |
| } gHdmiCecControllerClassInfo; |
| |
| class HdmiCecController { |
| public: |
| HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj); |
| |
| void init(); |
| |
| // Send message to other device. Note that it runs in IO thread. |
| int sendMessage(const cec_message_t& message); |
| // Add a logical address to device. |
| int addLogicalAddress(cec_logical_address_t address); |
| // Clear all logical address registered to the device. |
| void clearLogicaladdress(); |
| // Get physical address of device. |
| int getPhysicalAddress(); |
| // Get CEC version from driver. |
| int getVersion(); |
| // Get vendor id used for vendor command. |
| uint32_t getVendorId(); |
| |
| private: |
| // Propagate the message up to Java layer. |
| void propagateCecCommand(const cec_message_t& message); |
| void propagateHotplugEvent(const hotplug_event_t& event); |
| |
| static void onReceived(const hdmi_event_t* event, void* arg); |
| static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); |
| |
| hdmi_cec_device_t* mDevice; |
| jobject mCallbacksObj; |
| }; |
| |
| HdmiCecController::HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj) : |
| mDevice(device), |
| mCallbacksObj(callbacksObj) { |
| } |
| |
| void HdmiCecController::init() { |
| mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this); |
| } |
| |
| void HdmiCecController::propagateCecCommand(const cec_message_t& message) { |
| jint srcAddr = message.initiator; |
| jint dstAddr = message.destination; |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| jbyteArray body = env->NewByteArray(message.length); |
| const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body); |
| env->SetByteArrayRegion(body, 0, message.length, bodyPtr); |
| |
| env->CallVoidMethod(mCallbacksObj, |
| gHdmiCecControllerClassInfo.handleIncomingCecCommand, |
| srcAddr, dstAddr, body); |
| env->DeleteLocalRef(body); |
| |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| } |
| |
| void HdmiCecController::propagateHotplugEvent(const hotplug_event_t& event) { |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| env->CallVoidMethod(mCallbacksObj, |
| gHdmiCecControllerClassInfo.handleHotplug, event.connected); |
| |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| } |
| |
| int HdmiCecController::sendMessage(const cec_message_t& message) { |
| // TODO: propagate send_message's return value. |
| return mDevice->send_message(mDevice, &message); |
| } |
| |
| int HdmiCecController::addLogicalAddress(cec_logical_address_t address) { |
| return mDevice->add_logical_address(mDevice, address); |
| } |
| |
| void HdmiCecController::clearLogicaladdress() { |
| mDevice->clear_logical_address(mDevice); |
| } |
| |
| int HdmiCecController::getPhysicalAddress() { |
| uint16_t physicalAddress = 0xFFFF; |
| if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) { |
| return physicalAddress; |
| } |
| return -1; |
| } |
| |
| int HdmiCecController::getVersion() { |
| int version = 0; |
| mDevice->get_version(mDevice, &version); |
| return version; |
| } |
| |
| uint32_t HdmiCecController::getVendorId() { |
| uint32_t vendorId = 0; |
| mDevice->get_vendor_id(mDevice, &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 |
| void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { |
| HdmiCecController* controller = static_cast<HdmiCecController*>(arg); |
| if (controller == NULL) { |
| return; |
| } |
| |
| switch (event->type) { |
| case HDMI_EVENT_CEC_MESSAGE: |
| controller->propagateCecCommand(event->cec); |
| break; |
| case HDMI_EVENT_HOT_PLUG: |
| controller->propagateHotplugEvent(event->hotplug); |
| break; |
| default: |
| ALOGE("Unsupported event type: %d", event->type); |
| break; |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ |
| var = env->GetMethodID(clazz, methodName, methodDescriptor); \ |
| LOG_FATAL_IF(! var, "Unable to find method " methodName); |
| |
| static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) { |
| 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; |
| err = hw_get_module("hdmi_cec_module", |
| const_cast<const hw_module_t **>(&module)); |
| if (err != 0) { |
| ALOGE("Error acquiring hardware module: %d", err); |
| return 0; |
| } |
| hw_device_t* device; |
| // TODO: use HDMI_CEC_HARDWARE_INTERFACE of hdmi_cec.h for interface name |
| // once migration to HdmiControlService is done. |
| err = module->methods->open(module, "hdmi_cec_module_hw_if", &device); |
| if (err != 0) { |
| ALOGE("Error opening hardware module: %d", err); |
| return 0; |
| } |
| |
| HdmiCecController* controller = new HdmiCecController( |
| reinterpret_cast<hdmi_cec_device*>(device), |
| env->NewGlobalRef(callbacksObj)); |
| controller->init(); |
| |
| GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz, |
| "handleIncomingCecCommand", "(II[B)V"); |
| GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz, |
| "handleHotplug", "(Z)V"); |
| |
| return reinterpret_cast<jlong>(controller); |
| } |
| |
| static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, |
| jint srcAddr, jint dstAddr, jbyteArray body) { |
| cec_message_t message; |
| message.initiator = static_cast<cec_logical_address_t>(srcAddr); |
| message.destination = static_cast<cec_logical_address_t>(dstAddr); |
| |
| jsize len = env->GetArrayLength(body); |
| message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH); |
| ScopedByteArrayRO bodyPtr(env, body); |
| std::memcpy(message.body, bodyPtr.get(), len); |
| |
| HdmiCecController* controller = |
| reinterpret_cast<HdmiCecController*>(controllerPtr); |
| return controller->sendMessage(message); |
| } |
| |
| static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, |
| jlong controllerPtr, jint logicalAddress) { |
| HdmiCecController* controller = |
| reinterpret_cast<HdmiCecController*>(controllerPtr); |
| return controller->addLogicalAddress( |
| static_cast<cec_logical_address_t>(logicalAddress)); |
| } |
| |
| static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, |
| jlong controllerPtr) { |
| HdmiCecController* controller = |
| reinterpret_cast<HdmiCecController*>(controllerPtr); |
| controller->clearLogicaladdress(); |
| } |
| |
| static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, |
| jlong controllerPtr) { |
| HdmiCecController* controller = |
| reinterpret_cast<HdmiCecController*>(controllerPtr); |
| return controller->getPhysicalAddress(); |
| } |
| |
| static jint nativeGetVersion(JNIEnv* env, jclass clazz, |
| jlong controllerPtr) { |
| HdmiCecController* controller = |
| reinterpret_cast<HdmiCecController*>(controllerPtr); |
| return controller->getVersion(); |
| } |
| |
| static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { |
| HdmiCecController* controller = |
| reinterpret_cast<HdmiCecController*>(controllerPtr); |
| return controller->getVendorId(); |
| } |
| |
| static JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J", |
| (void *) nativeInit }, |
| { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand }, |
| { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress }, |
| { "nativeClearLogicalAddress", "(J)V", (void *) nativeClearLogicalAddress }, |
| { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, |
| { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, |
| { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, |
| }; |
| |
| #define CLASS_PATH "com/android/server/hdmi/HdmiCecController" |
| |
| int register_android_server_hdmi_HdmiCecController(JNIEnv* env) { |
| int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods)); |
| LOG_FATAL_IF(res < 0, "Unable to register native methods."); |
| return 0; |
| } |
| |
| } /* namespace android */ |