summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorJungshik Jang <jayjang@google.com>2014-05-02 00:32:44 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-05-02 00:32:44 +0000
commit615d1337fcb4b7e16706e615b19305eea7b689bb (patch)
treebc7c712572f5d4c11fbc2b7b57fea1d3aabba253 /services
parent27e12e72926a552bb5f7d9dc57e9ad562fe4f3b0 (diff)
parente9c77c88ea34a66f83a94f960547275c0ff6bd07 (diff)
downloadframeworks_base-615d1337fcb4b7e16706e615b19305eea7b689bb.zip
frameworks_base-615d1337fcb4b7e16706e615b19305eea7b689bb.tar.gz
frameworks_base-615d1337fcb4b7e16706e615b19305eea7b689bb.tar.bz2
Merge "Implement native send and receive logic for HdmiCecController."
Diffstat (limited to 'services')
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java132
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java34
-rw-r--r--services/core/jni/com_android_server_hdmi_HdmiCecController.cpp134
3 files changed, 262 insertions, 38 deletions
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 5f07108..8d4341d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -16,9 +16,14 @@
package com.android.server.hdmi;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.Slog;
+
+import java.util.Arrays;
/**
* Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
@@ -32,12 +37,27 @@ import android.os.Message;
class HdmiCecController {
private static final String TAG = "HdmiCecController";
+ // A message to pass cec send command to IO looper.
+ private static final int MSG_SEND_CEC_COMMAND = 1;
+
+ // Message types to handle incoming message in main service looper.
+ private final static int MSG_RECEIVE_CEC_COMMAND = 1;
+
+ // TODO: move these values to HdmiCec.java once make it internal constant class.
+ // CEC's ABORT reason values.
+ private static final int ABORT_UNRECOGNIZED_MODE = 0;
+ private static final int ABORT_NOT_IN_CORRECT_MODE = 1;
+ private static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
+ private static final int ABORT_INVALID_OPERAND = 3;
+ private static final int ABORT_REFUSED = 4;
+ private static final int ABORT_UNABLE_TO_DETERMINE = 5;
+
// Handler instance to process synchronous I/O (mainly send) message.
private Handler mIoHandler;
// Handler instance to process various messages coming from other CEC
// device or issued by internal state change.
- private Handler mMessageHandler;
+ private Handler mControlHandler;
// Stores the pointer to the native implementation of the service that
// interacts with HAL.
@@ -52,14 +72,12 @@ class HdmiCecController {
* inner device or has no device it will return {@code null}.
*
* <p>Declared as package-private, accessed by {@link HdmiControlService} only.
- *
- * @param ioLooper a Looper instance to handle IO (mainly send message) operation.
- * @param messageHandler a message handler that processes a message coming from other
- * CEC compatible device or callback of internal state change.
+ * @param service {@link HdmiControlService} instance used to create internal handler
+ * and to pass callback for incoming message or event.
* @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
* returns {@code null}.
*/
- static HdmiCecController create(Looper ioLooper, Handler messageHandler) {
+ static HdmiCecController create(HdmiControlService service) {
HdmiCecController handler = new HdmiCecController();
long nativePtr = nativeInit(handler);
if (nativePtr == 0L) {
@@ -67,28 +85,108 @@ class HdmiCecController {
return null;
}
- handler.init(ioLooper, messageHandler, nativePtr);
+ handler.init(service, nativePtr);
return handler;
}
- private void init(Looper ioLooper, Handler messageHandler, long nativePtr) {
- mIoHandler = new Handler(ioLooper) {
- @Override
- public void handleMessage(Message msg) {
- // TODO: Call native sendMessage.
+ private static byte[] buildBody(int opcode, byte[] params) {
+ byte[] body = new byte[params.length + 1];
+ body[0] = (byte) opcode;
+ System.arraycopy(params, 0, body, 1, params.length);
+ return body;
+ }
+
+ private final class IoHandler extends Handler {
+ private IoHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SEND_CEC_COMMAND:
+ HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj;
+ byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
+ nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
+ cecMessage.getDestination(), body);
+ break;
+ default:
+ Slog.w(TAG, "Unsupported CEC Io request:" + msg.what);
+ break;
+ }
+ }
+ }
+
+ private final class ControlHandler extends Handler {
+ private ControlHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_RECEIVE_CEC_COMMAND:
+ // TODO: delegate it to HdmiControl service.
+ onReceiveCommand((HdmiCecMessage) msg.obj);
+ break;
+ default:
+ Slog.i(TAG, "Unsupported message type:" + msg.what);
+ break;
}
- };
+ }
+ }
- mMessageHandler = messageHandler;
+ private void init(HdmiControlService service, long nativePtr) {
+ mIoHandler = new IoHandler(service.getServiceLooper());
+ mControlHandler = new ControlHandler(service.getServiceLooper());
mNativePtr = nativePtr;
}
+ private void onReceiveCommand(HdmiCecMessage message) {
+ // TODO: Handle message according to opcode type.
+
+ // TODO: Use device's source address for broadcast message.
+ int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ?
+ message.getDestination() : 0;
+ // Reply <Feature Abort> to initiator (source) for all requests.
+ sendFeatureAbort(sourceAddress, message.getSource(), message.getOpcode(),
+ ABORT_REFUSED);
+ }
+
+ private void sendFeatureAbort(int srcAddress, int destAddress, int originalOpcode,
+ int reason) {
+ byte[] params = new byte[2];
+ params[0] = (byte) originalOpcode;
+ params[1] = (byte) reason;
+
+ HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, destAddress,
+ HdmiCec.MESSAGE_FEATURE_ABORT, params);
+ Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
+ mIoHandler.sendMessage(message);
+ }
+
+ /**
+ * Called by native when incoming CEC message arrived.
+ */
+ private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
+ byte opcode = body[0];
+ byte params[] = Arrays.copyOfRange(body, 1, body.length);
+ HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+
+ // Delegate message to main handler so that it handles in main thread.
+ Message message = mControlHandler.obtainMessage(
+ MSG_RECEIVE_CEC_COMMAND, cecMessage);
+ mControlHandler.sendMessage(message);
+ }
+
/**
- * Called by native when an HDMI-CEC message arrived.
+ * Called by native when a hotplug event issues.
*/
- private void handleMessage(int srcAddress, int dstAddres, int opcode, byte[] params) {
- // TODO: Translate message and delegate it to main message handler.
+ private void handleHotplug(boolean connected) {
+ // TODO: Delegate event to main message handler.
}
private static native long nativeInit(HdmiCecController handler);
+ private static native int nativeSendCecCommand(long contollerPtr, int srcAddress,
+ int dstAddress, byte[] body);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 56c5b49..7c1995e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,9 +18,8 @@ package com.android.server.hdmi;
import android.annotation.Nullable;
import android.content.Context;
-import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Message;
+import android.os.Looper;
import android.util.Slog;
import com.android.server.SystemService;
@@ -37,14 +36,6 @@ public final class HdmiControlService extends SystemService {
// and sparse call it shares a thread to handle IO operations.
private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
- // Main handler class to handle incoming message from each controller.
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // TODO: Add handler for each message type.
- }
- };
-
@Nullable
private HdmiCecController mCecController;
@@ -57,14 +48,33 @@ public final class HdmiControlService extends SystemService {
@Override
public void onStart() {
- mCecController = HdmiCecController.create(mIoThread.getLooper(), mHandler);
+ mCecController = HdmiCecController.create(this);
if (mCecController == null) {
Slog.i(TAG, "Device does not support HDMI-CEC.");
}
- mMhlController = HdmiMhlController.create(mIoThread.getLooper(), mHandler);
+ mMhlController = HdmiMhlController.create(this);
if (mMhlController == null) {
Slog.i(TAG, "Device does not support MHL-control.");
}
}
+
+ /**
+ * Returns {@link Looper} for IO operation.
+ *
+ * <p>Declared as package-private.
+ */
+ Looper getIoLooper() {
+ return mIoThread.getLooper();
+ }
+
+ /**
+ * Returns {@link Looper} of main thread. Use this {@link Looper} instance
+ * for tasks that are running on main service thread.
+ *
+ * <p>Declared as package-private.
+ */
+ Looper getServiceLooper() {
+ return Looper.myLooper();
+ }
}
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index f3e8f3c..cfe74ed 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -19,57 +19,173 @@
#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 handleMessage;
+ jmethodID handleIncomingCecCommand;
+ jmethodID handleHotplug;
} gHdmiCecControllerClassInfo;
-
class HdmiCecController {
public:
- HdmiCecController(jobject callbacksObj);
+ 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);
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(jobject callbacksObj) :
+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);
+}
+
+// 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* handler = static_cast<HdmiCecController*>(arg);
- if (handler == NULL) {
+ HdmiCecController* controller = static_cast<HdmiCecController*>(arg);
+ if (controller == NULL) {
return;
}
- // TODO: propagate message to Java layer.
+ 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) {
- // TODO: initialize hal and pass it to controller if ready.
+ 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 JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J",
(void *) nativeInit },
+ { "nativeSendCommand", "(JII[B)I",
+ (void *) nativeSendCecCommand },
};
#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"