diff options
author | Jeff Brown <jeffbrown@google.com> | 2013-03-27 02:43:54 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-03-27 02:43:54 +0000 |
commit | ca3d655d20c13c71972a4475cec3b98efa7dbdd0 (patch) | |
tree | 2766b887f99e37bcdc423ecf668b7ad6ee58d609 /core/jni | |
parent | b404ecc91a5dd3dd027554490b2ca18c1048bdba (diff) | |
parent | c28867a1d67121ce5963de135e3ae2b1dbd9a33d (diff) | |
download | frameworks_base-ca3d655d20c13c71972a4475cec3b98efa7dbdd0.zip frameworks_base-ca3d655d20c13c71972a4475cec3b98efa7dbdd0.tar.gz frameworks_base-ca3d655d20c13c71972a4475cec3b98efa7dbdd0.tar.bz2 |
Merge "Use input transport for communications between app and IME." into jb-mr2-dev
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_view_InputEventSender.cpp | 307 |
3 files changed, 310 insertions, 0 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 1e27be8..66cea9d7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -44,6 +44,7 @@ LOCAL_SRC_FILES:= \ android_view_InputChannel.cpp \ android_view_InputDevice.cpp \ android_view_InputEventReceiver.cpp \ + android_view_InputEventSender.cpp \ android_view_KeyEvent.cpp \ android_view_KeyCharacterMap.cpp \ android_view_HardwareRenderer.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 86d3cb6..1300d01 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -163,6 +163,7 @@ extern int register_android_media_RemoteDisplay(JNIEnv *env); extern int register_android_view_InputChannel(JNIEnv* env); extern int register_android_view_InputDevice(JNIEnv* env); extern int register_android_view_InputEventReceiver(JNIEnv* env); +extern int register_android_view_InputEventSender(JNIEnv* env); extern int register_android_view_KeyCharacterMap(JNIEnv *env); extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); @@ -1195,6 +1196,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_app_NativeActivity), REG_JNI(register_android_view_InputChannel), REG_JNI(register_android_view_InputEventReceiver), + REG_JNI(register_android_view_InputEventSender), REG_JNI(register_android_view_KeyEvent), REG_JNI(register_android_view_MotionEvent), REG_JNI(register_android_view_PointerIcon), diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp new file mode 100644 index 0000000..bd1d103 --- /dev/null +++ b/core/jni/android_view_InputEventSender.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2013 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 "InputEventSender" + +//#define LOG_NDEBUG 0 + +// Log debug messages about the dispatch cycle. +#define DEBUG_DISPATCH_CYCLE 0 + + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> +#include <utils/Looper.h> +#include <utils/threads.h> +#include <utils/KeyedVector.h> +#include <androidfw/InputTransport.h> +#include "android_os_MessageQueue.h" +#include "android_view_InputChannel.h" +#include "android_view_KeyEvent.h" +#include "android_view_MotionEvent.h" + +namespace android { + +static struct { + jclass clazz; + + jmethodID dispatchInputEventFinished; +} gInputEventSenderClassInfo; + + +class NativeInputEventSender : public LooperCallback { +public: + NativeInputEventSender(JNIEnv* env, + jobject senderObj, const sp<InputChannel>& inputChannel, + const sp<MessageQueue>& messageQueue); + + status_t initialize(); + void dispose(); + status_t sendKeyEvent(uint32_t seq, const KeyEvent* event); + status_t sendMotionEvent(uint32_t seq, const MotionEvent* event); + +protected: + virtual ~NativeInputEventSender(); + +private: + jobject mSenderObjGlobal; + InputPublisher mInputPublisher; + sp<MessageQueue> mMessageQueue; + KeyedVector<uint32_t, uint32_t> mPublishedSeqMap; + uint32_t mNextPublishedSeq; + + const char* getInputChannelName() { + return mInputPublisher.getChannel()->getName().string(); + } + + virtual int handleEvent(int receiveFd, int events, void* data); + status_t receiveFinishedSignals(JNIEnv* env); +}; + + +NativeInputEventSender::NativeInputEventSender(JNIEnv* env, + jobject senderObj, const sp<InputChannel>& inputChannel, + const sp<MessageQueue>& messageQueue) : + mSenderObjGlobal(env->NewGlobalRef(senderObj)), + mInputPublisher(inputChannel), mMessageQueue(messageQueue), + mNextPublishedSeq(0) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName()); +#endif +} + +NativeInputEventSender::~NativeInputEventSender() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mSenderObjGlobal); +} + +status_t NativeInputEventSender::initialize() { + int receiveFd = mInputPublisher.getChannel()->getFd(); + mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); + return OK; +} + +void NativeInputEventSender::dispose() { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName()); +#endif + + mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd()); +} + +status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq); +#endif + + uint32_t publishedSeq = mNextPublishedSeq++; + status_t status = mInputPublisher.publishKeyEvent(publishedSeq, + event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(), + event->getKeyCode(), event->getScanCode(), event->getMetaState(), + event->getRepeatCount(), event->getDownTime(), event->getEventTime()); + if (status) { + ALOGW("Failed to send key event on channel '%s'. status=%d", + getInputChannelName(), status); + return status; + } + mPublishedSeqMap.add(publishedSeq, seq); + return OK; +} + +status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName(), seq); +#endif + + uint32_t publishedSeq; + for (size_t i = 0; i <= event->getHistorySize(); i++) { + publishedSeq = mNextPublishedSeq++; + status_t status = mInputPublisher.publishMotionEvent(publishedSeq, + event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(), + event->getEdgeFlags(), event->getMetaState(), event->getButtonState(), + event->getXOffset(), event->getYOffset(), + event->getXPrecision(), event->getYPrecision(), + event->getDownTime(), event->getHistoricalEventTime(i), + event->getPointerCount(), event->getPointerProperties(), + event->getHistoricalRawPointerCoords(0, i)); + if (status) { + ALOGW("Failed to send motion event sample on channel '%s'. status=%d", + getInputChannelName(), status); + return status; + } + } + mPublishedSeqMap.add(publishedSeq, seq); + return OK; +} + +int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) { + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { + ALOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " + "events=0x%x", getInputChannelName(), events); + return 0; // remove the callback + } + + if (!(events & ALOOPER_EVENT_INPUT)) { + ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " + "events=0x%x", getInputChannelName(), events); + return 1; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + status_t status = receiveFinishedSignals(env); + mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); + return status == OK || status == NO_MEMORY ? 1 : 0; +} + +status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName()); +#endif + + bool skipCallbacks = false; + for (;;) { + uint32_t publishedSeq; + bool handled; + status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled); + if (status) { + if (status == WOULD_BLOCK) { + return OK; + } + ALOGE("channel '%s' ~ Failed to consume finished signals. status=%d", + getInputChannelName(), status); + return status; + } + + ssize_t index = mPublishedSeqMap.indexOfKey(publishedSeq); + if (index >= 0) { + uint32_t seq = mPublishedSeqMap.valueAt(index); + mPublishedSeqMap.removeItemsAt(index); + +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, " + "pendingEvents=%u.", + getInputChannelName(), seq, handled ? "true" : "false", + mPublishedSeqMap.size()); +#endif + + if (!skipCallbacks) { + env->CallVoidMethod(mSenderObjGlobal, + gInputEventSenderClassInfo.dispatchInputEventFinished, + jint(seq), jboolean(handled)); + if (env->ExceptionCheck()) { + ALOGE("Exception dispatching finished signal."); + skipCallbacks = true; + } + } + } + } +} + + +static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj, + jobject inputChannelObj, jobject messageQueueObj) { + sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, + inputChannelObj); + if (inputChannel == NULL) { + jniThrowRuntimeException(env, "InputChannel is not initialized."); + return 0; + } + + sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); + if (messageQueue == NULL) { + jniThrowRuntimeException(env, "MessageQueue is not initialized."); + return 0; + } + + sp<NativeInputEventSender> sender = new NativeInputEventSender(env, + senderObj, inputChannel, messageQueue); + status_t status = sender->initialize(); + if (status) { + String8 message; + message.appendFormat("Failed to initialize input event sender. status=%d", status); + jniThrowRuntimeException(env, message.string()); + return 0; + } + + sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object + return reinterpret_cast<jint>(sender.get()); +} + +static void nativeDispose(JNIEnv* env, jclass clazz, jint senderPtr) { + sp<NativeInputEventSender> sender = + reinterpret_cast<NativeInputEventSender*>(senderPtr); + sender->dispose(); + sender->decStrong(gInputEventSenderClassInfo.clazz); // drop reference held by the object +} + +static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jint senderPtr, + jint seq, jobject eventObj) { + sp<NativeInputEventSender> sender = + reinterpret_cast<NativeInputEventSender*>(senderPtr); + KeyEvent event; + android_view_KeyEvent_toNative(env, eventObj, &event); + status_t status = sender->sendKeyEvent(seq, &event); + return !status; +} + +static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jint senderPtr, + jint seq, jobject eventObj) { + sp<NativeInputEventSender> sender = + reinterpret_cast<NativeInputEventSender*>(senderPtr); + MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj); + status_t status = sender->sendMotionEvent(seq, event); + return !status; +} + + +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", + "(Landroid/view/InputEventSender;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", + (void*)nativeInit }, + { "nativeDispose", "(I)V", + (void*)nativeDispose }, + { "nativeSendKeyEvent", "(IILandroid/view/KeyEvent;)Z", + (void*)nativeSendKeyEvent }, + { "nativeSendMotionEvent", "(IILandroid/view/MotionEvent;)Z", + (void*)nativeSendMotionEvent }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + +int register_android_view_InputEventSender(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/InputEventSender", + gMethods, NELEM(gMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + FIND_CLASS(gInputEventSenderClassInfo.clazz, "android/view/InputEventSender"); + + GET_METHOD_ID(gInputEventSenderClassInfo.dispatchInputEventFinished, + gInputEventSenderClassInfo.clazz, + "dispatchInputEventFinished", "(IZ)V"); + return 0; +} + +} // namespace android |