com_android_server_hdmi_HdmiCecController.cpp revision e9c77c88ea34a66f83a94f960547275c0ff6bd07
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "HdmiCecControllerJni" 18 19#define LOG_NDEBUG 1 20 21#include "JNIHelp.h" 22#include "ScopedPrimitiveArray.h" 23 24#include <string> 25 26#include <android_runtime/AndroidRuntime.h> 27#include <android_runtime/Log.h> 28#include <hardware/hdmi_cec.h> 29#include <sys/param.h> 30 31namespace android { 32 33static struct { 34 jmethodID handleIncomingCecCommand; 35 jmethodID handleHotplug; 36} gHdmiCecControllerClassInfo; 37 38class HdmiCecController { 39public: 40 HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj); 41 42 void init(); 43 44 // Send message to other device. Note that it runs in IO thread. 45 int sendMessage(const cec_message_t& message); 46 47private: 48 // Propagate the message up to Java layer. 49 void propagateCecCommand(const cec_message_t& message); 50 void propagateHotplugEvent(const hotplug_event_t& event); 51 52 static void onReceived(const hdmi_event_t* event, void* arg); 53 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); 54 55 hdmi_cec_device_t* mDevice; 56 jobject mCallbacksObj; 57}; 58 59HdmiCecController::HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj) : 60 mDevice(device), 61 mCallbacksObj(callbacksObj) { 62} 63 64void HdmiCecController::init() { 65 mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this); 66} 67 68void HdmiCecController::propagateCecCommand(const cec_message_t& message) { 69 jint srcAddr = message.initiator; 70 jint dstAddr = message.destination; 71 JNIEnv* env = AndroidRuntime::getJNIEnv(); 72 jbyteArray body = env->NewByteArray(message.length); 73 const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body); 74 env->SetByteArrayRegion(body, 0, message.length, bodyPtr); 75 76 env->CallVoidMethod(mCallbacksObj, 77 gHdmiCecControllerClassInfo.handleIncomingCecCommand, 78 srcAddr, dstAddr, body); 79 env->DeleteLocalRef(body); 80 81 checkAndClearExceptionFromCallback(env, __FUNCTION__); 82} 83 84void HdmiCecController::propagateHotplugEvent(const hotplug_event_t& event) { 85 JNIEnv* env = AndroidRuntime::getJNIEnv(); 86 env->CallVoidMethod(mCallbacksObj, 87 gHdmiCecControllerClassInfo.handleHotplug, event.connected); 88 89 checkAndClearExceptionFromCallback(env, __FUNCTION__); 90} 91 92int HdmiCecController::sendMessage(const cec_message_t& message) { 93 // TODO: propagate send_message's return value. 94 return mDevice->send_message(mDevice, &message); 95} 96 97// static 98void HdmiCecController::checkAndClearExceptionFromCallback(JNIEnv* env, 99 const char* methodName) { 100 if (env->ExceptionCheck()) { 101 ALOGE("An exception was thrown by callback '%s'.", methodName); 102 LOGE_EX(env); 103 env->ExceptionClear(); 104 } 105} 106 107// static 108void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { 109 HdmiCecController* controller = static_cast<HdmiCecController*>(arg); 110 if (controller == NULL) { 111 return; 112 } 113 114 switch (event->type) { 115 case HDMI_EVENT_CEC_MESSAGE: 116 controller->propagateCecCommand(event->cec); 117 break; 118 case HDMI_EVENT_HOT_PLUG: 119 controller->propagateHotplugEvent(event->hotplug); 120 break; 121 default: 122 ALOGE("Unsupported event type: %d", event->type); 123 break; 124 } 125} 126 127//------------------------------------------------------------------------------ 128#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 129 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 130 LOG_FATAL_IF(! var, "Unable to find method " methodName); 131 132static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) { 133 int err; 134 // If use same hardware module id between HdmiCecService and 135 // HdmiControlSservice it may conflict and cause abnormal state of HAL. 136 // TODO: use HDMI_CEC_HARDWARE_MODULE_ID of hdmi_cec.h for module id 137 // once migration to HdmiControlService is done. 138 hw_module_t* module; 139 err = hw_get_module("hdmi_cec_module", 140 const_cast<const hw_module_t **>(&module)); 141 if (err != 0) { 142 ALOGE("Error acquiring hardware module: %d", err); 143 return 0; 144 } 145 hw_device_t* device; 146 // TODO: use HDMI_CEC_HARDWARE_INTERFACE of hdmi_cec.h for interface name 147 // once migration to HdmiControlService is done. 148 err = module->methods->open(module, "hdmi_cec_module_hw_if", &device); 149 if (err != 0) { 150 ALOGE("Error opening hardware module: %d", err); 151 return 0; 152 } 153 154 HdmiCecController* controller = new HdmiCecController( 155 reinterpret_cast<hdmi_cec_device*>(device), 156 env->NewGlobalRef(callbacksObj)); 157 controller->init(); 158 159 GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz, 160 "handleIncomingCecCommand", "(II[B)V"); 161 GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz, 162 "handleHotplug", "(Z)V"); 163 164 return reinterpret_cast<jlong>(controller); 165} 166 167static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, 168 jint srcAddr, jint dstAddr, jbyteArray body) { 169 cec_message_t message; 170 message.initiator = static_cast<cec_logical_address_t>(srcAddr); 171 message.destination = static_cast<cec_logical_address_t>(dstAddr); 172 173 jsize len = env->GetArrayLength(body); 174 message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH); 175 ScopedByteArrayRO bodyPtr(env, body); 176 std::memcpy(message.body, bodyPtr.get(), len); 177 178 HdmiCecController* controller = 179 reinterpret_cast<HdmiCecController*>(controllerPtr); 180 return controller->sendMessage(message); 181} 182 183static JNINativeMethod sMethods[] = { 184 /* name, signature, funcPtr */ 185 { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J", 186 (void *) nativeInit }, 187 { "nativeSendCommand", "(JII[B)I", 188 (void *) nativeSendCecCommand }, 189}; 190 191#define CLASS_PATH "com/android/server/hdmi/HdmiCecController" 192 193int register_android_server_hdmi_HdmiCecController(JNIEnv* env) { 194 int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods)); 195 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 196 return 0; 197} 198 199} /* namespace android */ 200