| /* |
| * 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 "HdmiCecJni" |
| |
| #define LOG_NDEBUG 1 |
| |
| #include "ScopedPrimitiveArray.h" |
| |
| #include <string> |
| #include <deque> |
| #include <map> |
| |
| #include <android_runtime/AndroidRuntime.h> |
| #include <android_runtime/Log.h> |
| #include <hardware/hdmi_cec.h> |
| |
| namespace android { |
| |
| static struct { |
| jmethodID handleMessage; |
| jmethodID handleHotplug; |
| jmethodID getActiveSource; |
| jmethodID getLanguage; |
| } gHdmiCecServiceClassInfo; |
| |
| #ifndef min |
| #define min(a, b) ((a) > (b) ? (b) : (a)) |
| #endif |
| |
| class HdmiCecHandler { |
| public: |
| enum HdmiCecError { |
| SUCCESS = 0, |
| FAILED = -1 |
| }; |
| |
| // Data type to hold a CEC message or internal event data. |
| typedef union { |
| cec_message_t cec; |
| hotplug_event_t hotplug; |
| } queue_item_t; |
| |
| // Entry used for message queue. |
| typedef std::pair<int, const queue_item_t> MessageEntry; |
| |
| HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj); |
| |
| void initialize(); |
| |
| // initialize individual logical device. |
| cec_logical_address_t initLogicalDevice(cec_device_type_t type); |
| void releaseLogicalDevice(cec_device_type_t type); |
| |
| cec_logical_address_t getLogicalAddress(cec_device_type_t deviceType); |
| uint16_t getPhysicalAddress(); |
| int getDeviceType(cec_logical_address_t addr); |
| void queueMessage(const MessageEntry& message); |
| void queueOutgoingMessage(const cec_message_t& message); |
| void sendReportPhysicalAddress(); |
| void sendActiveSource(cec_logical_address_t srcAddr); |
| void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, |
| int opcode, int reason); |
| void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, |
| int version); |
| void sendDeviceVendorId(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr); |
| void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr); |
| void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, |
| const char* name, size_t len); |
| void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr); |
| |
| void sendCecMessage(const cec_message_t& message); |
| void setOsdName(const char* name, size_t len); |
| |
| private: |
| enum { |
| EVENT_TYPE_RX, |
| EVENT_TYPE_TX, |
| EVENT_TYPE_HOTPLUG, |
| EVENT_TYPE_STANDBY |
| }; |
| |
| static const unsigned int MAX_BUFFER_SIZE = 256; |
| static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF; |
| static const int INACTIVE_DEVICE_TYPE = -1; |
| |
| static void onReceived(const hdmi_event_t* event, void* arg); |
| static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); |
| |
| void updatePhysicalAddress(); |
| void updateLogicalAddress(); |
| void dispatchMessage(const MessageEntry& message); |
| void processIncomingMessage(const cec_message_t& msg); |
| |
| // Check the message before we pass it up to framework. If true, we proceed. |
| // otherwise do not propagate it. |
| bool precheckMessage(const cec_message_t& msg); |
| |
| // Propagate the message up to Java layer. |
| void propagateMessage(const cec_message_t& msg); |
| void propagateHotplug(bool connected); |
| |
| // Handles incoming <Request Active Source> message. If one of logical |
| // devices is active, it should reply with <Active Source> message. |
| void handleRequestActiveSource(); |
| void handleGetOsdName(const cec_message_t& msg); |
| void handleGiveDeviceVendorID(const cec_message_t& msg); |
| void handleGetCECVersion(const cec_message_t& msg); |
| void handleGetMenuLanguage(const cec_message_t& msg); |
| |
| // Internal thread for message queue handler |
| class HdmiThread : public Thread { |
| public: |
| HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) : |
| Thread(canCallJava), |
| mHdmiCecHandler(hdmiCecHandler) { |
| } |
| private: |
| virtual bool threadLoop() { |
| ALOGV("HdmiThread started"); |
| AutoMutex _l(mHdmiCecHandler->mMessageQueueLock); |
| mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock); |
| /* Process all messages in the queue */ |
| while (mHdmiCecHandler->mMessageQueue.size() > 0) { |
| MessageEntry entry = mHdmiCecHandler->mMessageQueue.front(); |
| mHdmiCecHandler->dispatchMessage(entry); |
| } |
| return true; |
| } |
| |
| HdmiCecHandler* mHdmiCecHandler; |
| }; |
| |
| // device type -> logical address mapping |
| std::map<cec_device_type_t, cec_logical_address_t> mLogicalDevices; |
| |
| hdmi_cec_device_t* mDevice; |
| jobject mCallbacksObj; |
| Mutex mLock; |
| Mutex mMessageQueueLock; |
| Condition mMessageQueueCondition; |
| sp<HdmiThread> mMessageQueueHandler; |
| |
| std::deque<MessageEntry> mMessageQueue; |
| uint16_t mPhysicalAddress; |
| std::string mOsdName; |
| }; |
| |
| |
| HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) : |
| mDevice(device), |
| mCallbacksObj(callbacksObj) { |
| } |
| |
| void HdmiCecHandler::initialize() { |
| mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this); |
| mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */); |
| mMessageQueueHandler->run("MessageHandler"); |
| updatePhysicalAddress(); |
| } |
| |
| uint16_t HdmiCecHandler::getPhysicalAddress() { |
| return mPhysicalAddress; |
| } |
| |
| void HdmiCecHandler::updatePhysicalAddress() { |
| uint16_t addr; |
| if (!mDevice->get_physical_address(mDevice, &addr)) { |
| mPhysicalAddress = addr; |
| } else { |
| mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; |
| } |
| } |
| |
| void HdmiCecHandler::updateLogicalAddress() { |
| std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); |
| for (; it != mLogicalDevices.end(); ++it) { |
| cec_logical_address_t addr; |
| if (!mDevice->get_logical_address(mDevice, it->first, &addr)) { |
| it->second = addr; |
| } |
| } |
| } |
| |
| cec_logical_address_t HdmiCecHandler::initLogicalDevice(cec_device_type_t type) { |
| cec_logical_address_t addr; |
| int res = mDevice->allocate_logical_address(mDevice, type, &addr); |
| |
| if (res != 0) { |
| ALOGE("Logical Address Allocation failed: %d", res); |
| } else { |
| ALOGV("Logical Address Allocation success: %d", addr); |
| mLogicalDevices.insert(std::pair<cec_device_type_t, cec_logical_address_t>(type, addr)); |
| } |
| return addr; |
| } |
| |
| void HdmiCecHandler::releaseLogicalDevice(cec_device_type_t type) { |
| std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.find(type); |
| if (it != mLogicalDevices.end()) { |
| mLogicalDevices.erase(it); |
| } |
| // TODO: remove the address monitored in HAL as well. |
| } |
| |
| cec_logical_address_t HdmiCecHandler::getLogicalAddress(cec_device_type_t type) { |
| std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.find(type); |
| if (it != mLogicalDevices.end()) { |
| return it->second; |
| } |
| return CEC_ADDR_UNREGISTERED; |
| } |
| |
| int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) { |
| std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); |
| for (; it != mLogicalDevices.end(); ++it) { |
| if (it->second == addr) { |
| return it->first; |
| } |
| } |
| return INACTIVE_DEVICE_TYPE; |
| } |
| |
| void HdmiCecHandler::queueMessage(const MessageEntry& entry) { |
| AutoMutex _l(mMessageQueueLock); |
| if (mMessageQueue.size() <= MAX_BUFFER_SIZE) { |
| mMessageQueue.push_back(entry); |
| mMessageQueueCondition.signal(); |
| } else { |
| ALOGW("Queue is full! Message dropped."); |
| } |
| } |
| |
| void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) { |
| queue_item_t item; |
| item.cec = message; |
| MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item); |
| queueMessage(entry); |
| } |
| |
| void HdmiCecHandler::sendReportPhysicalAddress() { |
| if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { |
| ALOGE("Invalid physical address."); |
| return; |
| } |
| |
| // Report physical address for each logical one hosted in it. |
| std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); |
| while (it != mLogicalDevices.end()) { |
| cec_message_t msg; |
| msg.initiator = it->second; // logical address |
| msg.destination = CEC_ADDR_BROADCAST; |
| msg.length = 4; |
| msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS; |
| msg.body[1] = (mPhysicalAddress >> 8) & 0xff; |
| msg.body[2] = mPhysicalAddress & 0xff; |
| msg.body[3] = it->first; // device type |
| queueOutgoingMessage(msg); |
| ++it; |
| } |
| } |
| |
| void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) { |
| if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { |
| ALOGE("Error getting physical address."); |
| return; |
| } |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = CEC_ADDR_BROADCAST; |
| msg.length = 3; |
| msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE; |
| msg.body[1] = (mPhysicalAddress >> 8) & 0xff; |
| msg.body[2] = mPhysicalAddress & 0xff; |
| queueOutgoingMessage(msg); |
| } |
| |
| void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr, |
| cec_logical_address_t dstAddr, int opcode, int reason) { |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = dstAddr; |
| msg.length = 3; |
| msg.body[0] = CEC_MESSAGE_FEATURE_ABORT; |
| msg.body[1] = opcode; |
| msg.body[2] = reason; |
| queueOutgoingMessage(msg); |
| } |
| |
| void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr, |
| cec_logical_address_t dstAddr, int version) { |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = dstAddr; |
| msg.length = 2; |
| msg.body[0] = CEC_MESSAGE_CEC_VERSION; |
| msg.body[1] = version; |
| queueOutgoingMessage(msg); |
| } |
| |
| void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr, |
| cec_logical_address_t dstAddr) { |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = dstAddr; |
| msg.length = 1; |
| msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID; |
| queueOutgoingMessage(msg); |
| } |
| |
| void HdmiCecHandler::sendDeviceVendorId(cec_logical_address_t srcAddr, |
| cec_logical_address_t dstAddr) { |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = dstAddr; |
| msg.length = 4; |
| msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID; |
| uint32_t vendor_id; |
| mDevice->get_vendor_id(mDevice, &vendor_id); |
| msg.body[1] = (vendor_id >> 16) & 0xff; |
| msg.body[2] = (vendor_id >> 8) & 0xff; |
| msg.body[3] = vendor_id & 0xff; |
| queueOutgoingMessage(msg); |
| } |
| |
| void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, |
| const char* name, size_t len) { |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = dstAddr; |
| msg.body[0] = CEC_MESSAGE_SET_OSD_NAME; |
| msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH); |
| std::memcpy(msg.body + 1, name, msg.length - 1); |
| queueOutgoingMessage(msg); |
| } |
| |
| void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr, |
| cec_logical_address_t dstAddr) { |
| char lang[4]; // buffer for 3-letter language code |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| jstring res = (jstring) env->CallObjectMethod(mCallbacksObj, |
| gHdmiCecServiceClassInfo.getLanguage, |
| getDeviceType(srcAddr)); |
| const char *clang = env->GetStringUTFChars(res, NULL); |
| strlcpy(lang, clang, sizeof(lang)); |
| env->ReleaseStringUTFChars(res, clang); |
| |
| cec_message_t msg; |
| msg.initiator = srcAddr; |
| msg.destination = dstAddr; |
| msg.length = 4; // opcode (1) + language code (3) |
| msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE; |
| std::memcpy(msg.body + 1, lang, 3); |
| queueOutgoingMessage(msg); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| } |
| |
| void HdmiCecHandler::sendCecMessage(const cec_message_t& message) { |
| AutoMutex _l(mLock); |
| ALOGV("sendCecMessage"); |
| mDevice->send_message(mDevice, &message); |
| } |
| |
| void HdmiCecHandler::setOsdName(const char* name, size_t len) { |
| mOsdName.assign(name, min(len, CEC_MESSAGE_BODY_MAX_LENGTH - 1)); |
| } |
| |
| // static |
| void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) { |
| HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg); |
| if (handler == NULL) { |
| return; |
| } |
| queue_item_t item; |
| if (event->type == HDMI_EVENT_CEC_MESSAGE) { |
| item.cec = event->cec; |
| MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item); |
| handler->queueMessage(entry); |
| } else if (event->type == HDMI_EVENT_HOT_PLUG) { |
| item.hotplug = event->hotplug; |
| MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item); |
| handler->queueMessage(entry); |
| } |
| } |
| |
| // static |
| void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { |
| if (env->ExceptionCheck()) { |
| ALOGE("An exception was thrown by callback '%s'.", methodName); |
| LOGE_EX(env); |
| env->ExceptionClear(); |
| } |
| } |
| |
| void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) { |
| int type = entry.first; |
| mMessageQueueLock.unlock(); |
| if (type == EVENT_TYPE_RX) { |
| mMessageQueue.pop_front(); |
| processIncomingMessage(entry.second.cec); |
| } else if (type == EVENT_TYPE_TX) { |
| sendCecMessage(entry.second.cec); |
| mMessageQueue.pop_front(); |
| } else if (type == EVENT_TYPE_HOTPLUG) { |
| mMessageQueue.pop_front(); |
| bool connected = entry.second.hotplug.connected; |
| if (connected) { |
| updatePhysicalAddress(); |
| updateLogicalAddress(); |
| } |
| propagateHotplug(connected); |
| } |
| mMessageQueueLock.lock(); |
| } |
| |
| void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) { |
| int opcode = msg.body[0]; |
| if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) { |
| sendReportPhysicalAddress(); |
| } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) { |
| handleRequestActiveSource(); |
| } else if (opcode == CEC_MESSAGE_GET_OSD_NAME) { |
| handleGetOsdName(msg); |
| } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) { |
| handleGiveDeviceVendorID(msg); |
| } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) { |
| handleGetCECVersion(msg); |
| } else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) { |
| handleGetMenuLanguage(msg); |
| } else if (opcode == CEC_MESSAGE_ABORT) { |
| // Compliance testing requires that abort message be responded with feature abort. |
| sendFeatureAbort(msg.destination, msg.initiator, msg.body[0], ABORT_REFUSED); |
| } else { |
| if (precheckMessage(msg)) { |
| propagateMessage(msg); |
| } |
| } |
| } |
| |
| bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) { |
| // Check if this is the broadcast message coming to itself, which need not be passed |
| // back to framework. This happens because CEC spec specifies that a physical device |
| // may host multiple logical devices. A broadcast message sent by one of them therefore |
| // should be able to reach the others by the loopback mechanism. |
| // |
| // Currently we don't deal with multiple logical devices, so this is not necessary. |
| // It should be revisited once we support hosting multiple logical devices. |
| int opcode = msg.body[0]; |
| if (msg.destination == CEC_ADDR_BROADCAST && |
| (opcode == CEC_MESSAGE_ACTIVE_SOURCE || |
| opcode == CEC_MESSAGE_SET_STREAM_PATH || |
| opcode == CEC_MESSAGE_INACTIVE_SOURCE)) { |
| uint16_t senderAddr = (msg.body[1] << 8) + msg.body[2]; |
| if (senderAddr == mPhysicalAddress) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void HdmiCecHandler::propagateMessage(const cec_message_t& msg) { |
| int paramLen = msg.length - 1; |
| jint srcAddr = msg.initiator; |
| jint dstAddr = msg.destination; |
| jint opcode = msg.body[0]; |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| jbyteArray params = env->NewByteArray(paramLen); |
| const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1); |
| if (paramLen > 0) { |
| env->SetByteArrayRegion(params, 0, paramLen, body); |
| } |
| env->CallVoidMethod(mCallbacksObj, |
| gHdmiCecServiceClassInfo.handleMessage, |
| srcAddr, dstAddr, opcode, params); |
| env->DeleteLocalRef(params); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| } |
| |
| void HdmiCecHandler::propagateHotplug(bool connected) { |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| env->CallVoidMethod(mCallbacksObj, |
| gHdmiCecServiceClassInfo.handleHotplug, |
| connected); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| } |
| |
| |
| void HdmiCecHandler::handleRequestActiveSource() { |
| JNIEnv* env = AndroidRuntime::getJNIEnv(); |
| jint activeDeviceType = env->CallIntMethod(mCallbacksObj, |
| gHdmiCecServiceClassInfo.getActiveSource); |
| if (activeDeviceType != INACTIVE_DEVICE_TYPE) { |
| sendActiveSource(getLogicalAddress(static_cast<cec_device_type_t>(activeDeviceType))); |
| } |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| } |
| |
| void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) { |
| if (!mOsdName.empty()) { |
| sendSetOsdName(msg.destination, msg.initiator, mOsdName.c_str(), mOsdName.length()); |
| } |
| } |
| |
| void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) { |
| sendDeviceVendorId(msg.destination, msg.initiator); |
| } |
| |
| void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) { |
| int version; |
| mDevice->get_version(mDevice, &version); |
| sendCecVersion(msg.destination, msg.initiator, version); |
| } |
| |
| void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) { |
| sendSetMenuLanguage(msg.destination, msg.initiator); |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| #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; |
| hw_module_t* module; |
| err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module)); |
| if (err != 0) { |
| ALOGE("Error acquiring hardware module: %d", err); |
| return 0; |
| } |
| hw_device_t* device; |
| err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device); |
| if (err != 0) { |
| ALOGE("Error opening hardware module: %d", err); |
| return 0; |
| } |
| HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device), |
| env->NewGlobalRef(callbacksObj)); |
| handler->initialize(); |
| |
| GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz, |
| "handleMessage", "(III[B)V"); |
| GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz, |
| "handleHotplug", "(Z)V"); |
| GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz, |
| "getActiveSource", "()I"); |
| GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz, |
| "getLanguage", "(I)Ljava/lang/String;"); |
| |
| return reinterpret_cast<jlong>(handler); |
| } |
| |
| static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType, |
| jint dstAddr, jint opcode, jbyteArray params) { |
| HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); |
| cec_logical_address_t srcAddr = handler->getLogicalAddress( |
| static_cast<cec_device_type_t>(deviceType)); |
| jsize len = env->GetArrayLength(params); |
| ScopedByteArrayRO paramsPtr(env, params); |
| cec_message_t message; |
| message.initiator = srcAddr; |
| message.destination = static_cast<cec_logical_address_t>(dstAddr); |
| message.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH); |
| message.body[0] = opcode; |
| std::memcpy(message.body + 1, paramsPtr.get(), message.length - 1); |
| handler->sendCecMessage(message); |
| } |
| |
| static jint nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr, |
| jint deviceType) { |
| HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); |
| return handler->initLogicalDevice(static_cast<cec_device_type_t>(deviceType)); |
| } |
| |
| static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr, |
| jint deviceType) { |
| HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); |
| return handler->releaseLogicalDevice(static_cast<cec_device_type_t>(deviceType)); |
| } |
| |
| static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr) { |
| HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); |
| return handler->getPhysicalAddress(); |
| } |
| |
| static void nativeSetOsdName(JNIEnv* env, jclass clazz, jlong handlerPtr, jbyteArray name) { |
| HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); |
| jsize len = env->GetArrayLength(name); |
| if (len > 0) { |
| ScopedByteArrayRO namePtr(env, name); |
| handler->setOsdName(reinterpret_cast<const char *>(namePtr.get()), len); |
| } |
| } |
| |
| static JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J", |
| (void *)nativeInit }, |
| { "nativeSendMessage", "(JIII[B)V", |
| (void *)nativeSendMessage }, |
| { "nativeAllocateLogicalAddress", "(JI)I", |
| (void *)nativeAllocateLogicalAddress }, |
| { "nativeRemoveLogicalAddress", "(JI)V", |
| (void *)nativeRemoveLogicalAddress }, |
| { "nativeGetPhysicalAddress", "(J)I", |
| (void *)nativeGetPhysicalAddress }, |
| { "nativeSetOsdName", "(J[B)V", |
| (void *)nativeSetOsdName }, |
| }; |
| |
| #define CLASS_PATH "com/android/server/hdmi/HdmiCecService" |
| |
| int register_android_server_hdmi_HdmiCecService(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 */ |