com_android_server_hdmi_HdmiCecController.cpp revision e74282b70b4a8ad1186fdc32204e1ecee16cd901
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 <cstring> 25 26#include <android_os_MessageQueue.h> 27#include <android_runtime/AndroidRuntime.h> 28#include <android_runtime/Log.h> 29#include <hardware/hdmi_cec.h> 30#include <sys/param.h> 31#include <utils/Looper.h> 32#include <utils/RefBase.h> 33 34namespace android { 35 36static struct { 37 jmethodID handleIncomingCecCommand; 38 jmethodID handleHotplug; 39} gHdmiCecControllerClassInfo; 40 41class HdmiCecController { 42public: 43 HdmiCecController(hdmi_cec_device_t* device, jobject callbacksObj, 44 const sp<Looper>& looper); 45 46 void init(); 47 48 // Send message to other device. Note that it runs in IO thread. 49 int sendMessage(const cec_message_t& message); 50 // Add a logical address to device. 51 int addLogicalAddress(cec_logical_address_t address); 52 // Clear all logical address registered to the device. 53 void clearLogicaladdress(); 54 // Get physical address of device. 55 int getPhysicalAddress(); 56 // Get CEC version from driver. 57 int getVersion(); 58 // Get vendor id used for vendor command. 59 uint32_t getVendorId(); 60 61 jobject getCallbacksObj() const { 62 return mCallbacksObj; 63 } 64 65private: 66 static void onReceived(const hdmi_event_t* event, void* arg); 67 68 hdmi_cec_device_t* mDevice; 69 jobject mCallbacksObj; 70 sp<Looper> mLooper; 71}; 72 73// RefBase wrapper for hdmi_event_t. As hdmi_event_t coming from HAL 74// may keep its own lifetime, we need to copy it in order to delegate 75// it to service thread. 76class CecEventWrapper : public LightRefBase<CecEventWrapper> { 77public: 78 CecEventWrapper(const hdmi_event_t& event) { 79 // Copy message. 80 switch (event.type) { 81 case HDMI_EVENT_CEC_MESSAGE: 82 mEvent.cec.initiator = event.cec.initiator; 83 mEvent.cec.destination = event.cec.destination; 84 mEvent.cec.length = event.cec.length; 85 std::memcpy(mEvent.cec.body, event.cec.body, event.cec.length); 86 break; 87 case HDMI_EVENT_HOT_PLUG: 88 mEvent.hotplug.connected = event.hotplug.connected; 89 mEvent.hotplug.port = event.hotplug.port; 90 break; 91 case HDMI_EVENT_TX_STATUS: 92 mEvent.tx_status.status = event.tx_status.status; 93 mEvent.tx_status.opcode = event.tx_status.opcode; 94 break; 95 default: 96 // TODO: add more type whenever new type is introduced. 97 break; 98 } 99 } 100 101 const cec_message_t& cec() const { 102 return mEvent.cec; 103 } 104 105 const hotplug_event_t& hotplug() const { 106 return mEvent.hotplug; 107 } 108 109 virtual ~CecEventWrapper() {} 110 111private: 112 hdmi_event_t mEvent; 113}; 114 115// Handler class to delegate incoming message to service thread. 116class HdmiCecEventHandler : public MessageHandler { 117public: 118 HdmiCecEventHandler(HdmiCecController* controller, const sp<CecEventWrapper>& event) 119 : mController(controller), 120 mEventWrapper(event) { 121 } 122 123 virtual ~HdmiCecEventHandler() {} 124 125 void handleMessage(const Message& message) { 126 switch (message.what) { 127 case HDMI_EVENT_CEC_MESSAGE: 128 propagateCecCommand(mEventWrapper->cec()); 129 break; 130 case HDMI_EVENT_HOT_PLUG: 131 propagateHotplugEvent(mEventWrapper->hotplug()); 132 break; 133 case HDMI_EVENT_TX_STATUS: 134 // TODO: propagate this to controller. 135 default: 136 // TODO: add more type whenever new type is introduced. 137 break; 138 } 139 } 140 141private: 142 // Propagate the message up to Java layer. 143 void propagateCecCommand(const cec_message_t& message) { 144 jint srcAddr = message.initiator; 145 jint dstAddr = message.destination; 146 JNIEnv* env = AndroidRuntime::getJNIEnv(); 147 jbyteArray body = env->NewByteArray(message.length); 148 const jbyte* bodyPtr = reinterpret_cast<const jbyte *>(message.body); 149 env->SetByteArrayRegion(body, 0, message.length, bodyPtr); 150 151 env->CallVoidMethod(mController->getCallbacksObj(), 152 gHdmiCecControllerClassInfo.handleIncomingCecCommand, srcAddr, 153 dstAddr, body); 154 env->DeleteLocalRef(body); 155 156 checkAndClearExceptionFromCallback(env, __FUNCTION__); 157 } 158 159 void propagateHotplugEvent(const hotplug_event_t& event) { 160 // Note that this method should be called in service thread. 161 JNIEnv* env = AndroidRuntime::getJNIEnv(); 162 env->CallVoidMethod(mController->getCallbacksObj(), 163 gHdmiCecControllerClassInfo.handleHotplug, event.connected); 164 165 checkAndClearExceptionFromCallback(env, __FUNCTION__); 166 } 167 168 // static 169 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { 170 if (env->ExceptionCheck()) { 171 ALOGE("An exception was thrown by callback '%s'.", methodName); 172 LOGE_EX(env); 173 env->ExceptionClear(); 174 } 175 } 176 177 HdmiCecController* mController; 178 sp<CecEventWrapper> mEventWrapper; 179}; 180 181HdmiCecController::HdmiCecController(hdmi_cec_device_t* device, 182 jobject callbacksObj, const sp<Looper>& looper) : 183 mDevice(device), 184 mCallbacksObj(callbacksObj), 185 mLooper(looper) { 186} 187 188void HdmiCecController::init() { 189 mDevice->register_event_callback(mDevice, HdmiCecController::onReceived, this); 190} 191 192int HdmiCecController::sendMessage(const cec_message_t& message) { 193 // TODO: propagate send_message's return value. 194 return mDevice->send_message(mDevice, &message); 195} 196 197int HdmiCecController::addLogicalAddress(cec_logical_address_t address) { 198 return mDevice->add_logical_address(mDevice, address); 199} 200 201void HdmiCecController::clearLogicaladdress() { 202 mDevice->clear_logical_address(mDevice); 203} 204 205int HdmiCecController::getPhysicalAddress() { 206 uint16_t physicalAddress = 0xFFFF; 207 if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) { 208 return physicalAddress; 209 } 210 return -1; 211} 212 213int HdmiCecController::getVersion() { 214 int version = 0; 215 mDevice->get_version(mDevice, &version); 216 return version; 217} 218 219uint32_t HdmiCecController::getVendorId() { 220 uint32_t vendorId = 0; 221 mDevice->get_vendor_id(mDevice, &vendorId); 222 return vendorId; 223} 224 225 226// static 227void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { 228 HdmiCecController* controller = static_cast<HdmiCecController*>(arg); 229 if (controller == NULL) { 230 return; 231 } 232 233 sp<CecEventWrapper> spEvent(new CecEventWrapper(*event)); 234 sp<HdmiCecEventHandler> handler(new HdmiCecEventHandler(controller, spEvent)); 235 controller->mLooper->sendMessage(handler, event->type); 236} 237 238//------------------------------------------------------------------------------ 239#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 240 var = env->GetMethodID(clazz, methodName, methodDescriptor); \ 241 LOG_FATAL_IF(! var, "Unable to find method " methodName); 242 243static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj, 244 jobject messageQueueObj) { 245 int err; 246 hw_module_t* module; 247 err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, 248 const_cast<const hw_module_t **>(&module)); 249 if (err != 0) { 250 ALOGE("Error acquiring hardware module: %d", err); 251 return 0; 252 } 253 254 hw_device_t* device; 255 err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device); 256 if (err != 0) { 257 ALOGE("Error opening hardware module: %d", err); 258 return 0; 259 } 260 261 sp<MessageQueue> messageQueue = 262 android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 263 264 HdmiCecController* controller = new HdmiCecController( 265 reinterpret_cast<hdmi_cec_device*>(device), 266 env->NewGlobalRef(callbacksObj), 267 messageQueue->getLooper()); 268 controller->init(); 269 270 GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz, 271 "handleIncomingCecCommand", "(II[B)V"); 272 GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz, 273 "handleHotplug", "(Z)V"); 274 275 return reinterpret_cast<jlong>(controller); 276} 277 278static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, 279 jint srcAddr, jint dstAddr, jbyteArray body) { 280 cec_message_t message; 281 message.initiator = static_cast<cec_logical_address_t>(srcAddr); 282 message.destination = static_cast<cec_logical_address_t>(dstAddr); 283 284 jsize len = env->GetArrayLength(body); 285 message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH); 286 ScopedByteArrayRO bodyPtr(env, body); 287 std::memcpy(message.body, bodyPtr.get(), len); 288 289 HdmiCecController* controller = 290 reinterpret_cast<HdmiCecController*>(controllerPtr); 291 return controller->sendMessage(message); 292} 293 294static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, 295 jlong controllerPtr, jint logicalAddress) { 296 HdmiCecController* controller = 297 reinterpret_cast<HdmiCecController*>(controllerPtr); 298 return controller->addLogicalAddress( 299 static_cast<cec_logical_address_t>(logicalAddress)); 300} 301 302static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, 303 jlong controllerPtr) { 304 HdmiCecController* controller = 305 reinterpret_cast<HdmiCecController*>(controllerPtr); 306 controller->clearLogicaladdress(); 307} 308 309static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, 310 jlong controllerPtr) { 311 HdmiCecController* controller = 312 reinterpret_cast<HdmiCecController*>(controllerPtr); 313 return controller->getPhysicalAddress(); 314} 315 316static jint nativeGetVersion(JNIEnv* env, jclass clazz, 317 jlong controllerPtr) { 318 HdmiCecController* controller = 319 reinterpret_cast<HdmiCecController*>(controllerPtr); 320 return controller->getVersion(); 321} 322 323static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { 324 HdmiCecController* controller = 325 reinterpret_cast<HdmiCecController*>(controllerPtr); 326 return controller->getVendorId(); 327} 328 329static JNINativeMethod sMethods[] = { 330 /* name, signature, funcPtr */ 331 { "nativeInit", 332 "(Lcom/android/server/hdmi/HdmiCecController;Landroid/os/MessageQueue;)J", 333 (void *) nativeInit }, 334 { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand }, 335 { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress }, 336 { "nativeClearLogicalAddress", "(J)V", (void *) nativeClearLogicalAddress }, 337 { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, 338 { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, 339 { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, 340}; 341 342#define CLASS_PATH "com/android/server/hdmi/HdmiCecController" 343 344int register_android_server_hdmi_HdmiCecController(JNIEnv* env) { 345 int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, 346 NELEM(sMethods)); 347 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 348 return 0; 349} 350 351} /* namespace android */ 352