com_android_server_hdmi_HdmiCecController.cpp revision 4085d0ef62554c7e139b82bca0f97161bb159f8c
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 243// TODO: replace above code with following once 244// replace old HdmiCecService with HdmiControlService 245#undef HDMI_CEC_HARDWARE_MODULE_ID 246#define HDMI_CEC_HARDWARE_MODULE_ID "hdmi_cec_module" 247#undef HDMI_CEC_HARDWARE_INTERFACE 248#define HDMI_CEC_HARDWARE_INTERFACE "hdmi_cec_module_hw_if" 249 250static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj, 251 jobject messageQueueObj) { 252 int err; 253 hw_module_t* module; 254 err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, 255 const_cast<const hw_module_t **>(&module)); 256 if (err != 0) { 257 ALOGE("Error acquiring hardware module: %d", err); 258 return 0; 259 } 260 261 hw_device_t* device; 262 err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device); 263 if (err != 0) { 264 ALOGE("Error opening hardware module: %d", err); 265 return 0; 266 } 267 268 sp<MessageQueue> messageQueue = 269 android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 270 271 HdmiCecController* controller = new HdmiCecController( 272 reinterpret_cast<hdmi_cec_device*>(device), 273 env->NewGlobalRef(callbacksObj), 274 messageQueue->getLooper()); 275 controller->init(); 276 277 GET_METHOD_ID(gHdmiCecControllerClassInfo.handleIncomingCecCommand, clazz, 278 "handleIncomingCecCommand", "(II[B)V"); 279 GET_METHOD_ID(gHdmiCecControllerClassInfo.handleHotplug, clazz, 280 "handleHotplug", "(Z)V"); 281 282 return reinterpret_cast<jlong>(controller); 283} 284 285static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, 286 jint srcAddr, jint dstAddr, jbyteArray body) { 287 cec_message_t message; 288 message.initiator = static_cast<cec_logical_address_t>(srcAddr); 289 message.destination = static_cast<cec_logical_address_t>(dstAddr); 290 291 jsize len = env->GetArrayLength(body); 292 message.length = MIN(len, CEC_MESSAGE_BODY_MAX_LENGTH); 293 ScopedByteArrayRO bodyPtr(env, body); 294 std::memcpy(message.body, bodyPtr.get(), len); 295 296 HdmiCecController* controller = 297 reinterpret_cast<HdmiCecController*>(controllerPtr); 298 return controller->sendMessage(message); 299} 300 301static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, 302 jlong controllerPtr, jint logicalAddress) { 303 HdmiCecController* controller = 304 reinterpret_cast<HdmiCecController*>(controllerPtr); 305 return controller->addLogicalAddress( 306 static_cast<cec_logical_address_t>(logicalAddress)); 307} 308 309static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, 310 jlong controllerPtr) { 311 HdmiCecController* controller = 312 reinterpret_cast<HdmiCecController*>(controllerPtr); 313 controller->clearLogicaladdress(); 314} 315 316static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, 317 jlong controllerPtr) { 318 HdmiCecController* controller = 319 reinterpret_cast<HdmiCecController*>(controllerPtr); 320 return controller->getPhysicalAddress(); 321} 322 323static jint nativeGetVersion(JNIEnv* env, jclass clazz, 324 jlong controllerPtr) { 325 HdmiCecController* controller = 326 reinterpret_cast<HdmiCecController*>(controllerPtr); 327 return controller->getVersion(); 328} 329 330static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { 331 HdmiCecController* controller = 332 reinterpret_cast<HdmiCecController*>(controllerPtr); 333 return controller->getVendorId(); 334} 335 336static JNINativeMethod sMethods[] = { 337 /* name, signature, funcPtr */ 338 { "nativeInit", 339 "(Lcom/android/server/hdmi/HdmiCecController;Landroid/os/MessageQueue;)J", 340 (void *) nativeInit }, 341 { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand }, 342 { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress }, 343 { "nativeClearLogicalAddress", "(J)V", (void *) nativeClearLogicalAddress }, 344 { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, 345 { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, 346 { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, 347}; 348 349#define CLASS_PATH "com/android/server/hdmi/HdmiCecController" 350 351int register_android_server_hdmi_HdmiCecController(JNIEnv* env) { 352 int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, 353 NELEM(sMethods)); 354 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 355 return 0; 356} 357 358} /* namespace android */ 359