diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-06-13 17:55:28 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-06-13 17:55:28 -0700 |
commit | 7c8aa44f320f45e8417f0aba9ca67af6a67a5cf7 (patch) | |
tree | 2d547d9d8ddc5b08310070121616294d92ebca70 /core/jni | |
parent | 94f14aeca9e6c6d07b39a7f708eacadcfeb6fbd2 (diff) | |
parent | 46b9ac0ae2162309774a7478cd9d4e578747bfc2 (diff) | |
download | frameworks_base-7c8aa44f320f45e8417f0aba9ca67af6a67a5cf7.zip frameworks_base-7c8aa44f320f45e8417f0aba9ca67af6a67a5cf7.tar.gz frameworks_base-7c8aa44f320f45e8417f0aba9ca67af6a67a5cf7.tar.bz2 |
am 46b9ac0a: Native input dispatch rewrite work in progress.
Merge commit '46b9ac0ae2162309774a7478cd9d4e578747bfc2' into gingerbread
* commit '46b9ac0ae2162309774a7478cd9d4e578747bfc2':
Native input dispatch rewrite work in progress.
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 5 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 10 | ||||
-rw-r--r-- | core/jni/android_os_MessageQueue.cpp | 348 | ||||
-rw-r--r-- | core/jni/android_os_MessageQueue.h | 30 | ||||
-rw-r--r-- | core/jni/android_view_InputChannel.cpp | 288 | ||||
-rw-r--r-- | core/jni/android_view_InputChannel.h | 40 | ||||
-rw-r--r-- | core/jni/android_view_InputQueue.cpp | 471 | ||||
-rw-r--r-- | core/jni/android_view_InputTarget.cpp | 97 | ||||
-rw-r--r-- | core/jni/android_view_InputTarget.h | 31 | ||||
-rw-r--r-- | core/jni/android_view_KeyEvent.cpp | 124 | ||||
-rw-r--r-- | core/jni/android_view_KeyEvent.h | 35 | ||||
-rw-r--r-- | core/jni/android_view_MotionEvent.cpp | 310 | ||||
-rw-r--r-- | core/jni/android_view_MotionEvent.h | 38 | ||||
-rw-r--r-- | core/jni/android_view_ViewRoot.cpp | 36 |
14 files changed, 1567 insertions, 296 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index d4545d7..d854e87 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -45,6 +45,11 @@ LOCAL_SRC_FILES:= \ android_view_Display.cpp \ android_view_Surface.cpp \ android_view_ViewRoot.cpp \ + android_view_InputChannel.cpp \ + android_view_InputQueue.cpp \ + android_view_InputTarget.cpp \ + android_view_KeyEvent.cpp \ + android_view_MotionEvent.cpp \ android_text_AndroidCharacter.cpp \ android_text_AndroidBidi.cpp \ android_text_KeyCharacterMap.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f66ed83..466642a 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -162,6 +162,11 @@ extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env); extern int register_android_app_NativeActivity(JNIEnv *env); +extern int register_android_view_InputChannel(JNIEnv* env); +extern int register_android_view_InputQueue(JNIEnv* env); +extern int register_android_view_InputTarget(JNIEnv* env); +extern int register_android_view_KeyEvent(JNIEnv* env); +extern int register_android_view_MotionEvent(JNIEnv* env); static AndroidRuntime* gCurRuntime = NULL; @@ -1288,6 +1293,11 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_backup_BackupHelperDispatcher), REG_JNI(register_android_app_NativeActivity), + REG_JNI(register_android_view_InputChannel), + REG_JNI(register_android_view_InputQueue), + REG_JNI(register_android_view_InputTarget), + REG_JNI(register_android_view_KeyEvent), + REG_JNI(register_android_view_MotionEvent), }; /* diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index 8984057..030d6c7 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -14,325 +14,151 @@ * limitations under the License. */ -#define LOG_TAG "MQNative" +#define LOG_TAG "MessageQueue-JNI" #include "JNIHelp.h" -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/time.h> -#include <fcntl.h> - -#include <android_runtime/AndroidRuntime.h> -#include <utils/SystemClock.h> -#include <utils/Vector.h> +#include <utils/PollLoop.h> #include <utils/Log.h> +#include "android_os_MessageQueue.h" -using namespace android; +namespace android { // ---------------------------------------------------------------------------- static struct { - jclass mClass; - - jfieldID mObject; // native object attached to the DVM MessageQueue -} gMessageQueueOffsets; - -static struct { - jclass mClass; - jmethodID mConstructor; -} gKeyEventOffsets; - -// TODO: also MotionEvent offsets etc. a la gKeyEventOffsets - -static struct { - jclass mClass; - jmethodID mObtain; // obtain(Handler h, int what, Object obj) -} gMessageOffsets; - -// ---------------------------------------------------------------------------- + jclass clazz; -static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) -{ - if (jniThrowException(env, exc, msg) != 0) - assert(false); -} + jfieldID mPtr; // native object attached to the DVM MessageQueue +} gMessageQueueClassInfo; // ---------------------------------------------------------------------------- -class MessageQueueNative { +class NativeMessageQueue { public: - MessageQueueNative(int readSocket, int writeSocket); - ~MessageQueueNative(); - - // select on all FDs until the designated time; forever if wakeupTime is < 0 - int waitForSignal(jobject mqueue, jlong wakeupTime); + NativeMessageQueue(); + ~NativeMessageQueue(); - // signal the queue-ready pipe - void signalQueuePipe(); + inline sp<PollLoop> getPollLoop() { return mPollLoop; } - // Specify a new input pipe, passing in responsibility for the socket fd and - // ashmem region - int registerInputPipe(JNIEnv* env, int socketFd, int memRegionFd, jobject handler); - - // Forget about this input pipe, closing the socket and ashmem region as well - int unregisterInputPipe(JNIEnv* env, int socketFd); - - size_t numRegisteredPipes() const { return mInputPipes.size(); } + bool pollOnce(int timeoutMillis); + void wake(); private: - struct InputPipe { - int fd; - int region; - jobject handler; - - InputPipe() {} - InputPipe(int _fd, int _r, jobject _h) : fd(_fd), region(_r), handler(_h) {} - }; - - // consume an event from a socket, put it on the DVM MessageQueue indicated, - // and notify the other end of the pipe that we've consumed it. - void queueEventFromPipe(const InputPipe& pipe, jobject mqueue); - - int mQueueReadFd, mQueueWriteFd; - Vector<InputPipe> mInputPipes; + sp<PollLoop> mPollLoop; }; -MessageQueueNative::MessageQueueNative(int readSocket, int writeSocket) - : mQueueReadFd(readSocket), mQueueWriteFd(writeSocket) { -} +// ---------------------------------------------------------------------------- -MessageQueueNative::~MessageQueueNative() { +NativeMessageQueue::NativeMessageQueue() { + mPollLoop = new PollLoop(); } -int MessageQueueNative::waitForSignal(jobject mqueue, jlong timeoutMillis) { - struct timeval tv, *timeout; - fd_set fdset; - - if (timeoutMillis < 0) { - timeout = NULL; - } else { - if (timeoutMillis == 0) { - tv.tv_sec = 0; - tv.tv_usec = 0; - } else { - tv.tv_sec = (timeoutMillis / 1000); - tv.tv_usec = (timeoutMillis - (1000 * tv.tv_sec)) * 1000; - } - timeout = &tv; - } - - // always rebuild the fd set from scratch - FD_ZERO(&fdset); - - // the queue signalling pipe - FD_SET(mQueueReadFd, &fdset); - int maxFd = mQueueReadFd; - - // and the input sockets themselves - for (size_t i = 0; i < mInputPipes.size(); i++) { - FD_SET(mInputPipes[i].fd, &fdset); - if (maxFd < mInputPipes[i].fd) { - maxFd = mInputPipes[i].fd; - } - } - - // now wait - int res = select(maxFd + 1, &fdset, NULL, NULL, timeout); - - // Error? Just return it and bail - if (res < 0) return res; - - // What happened -- timeout or actual data arrived? - if (res == 0) { - // select() returned zero, which means we timed out, which means that it's time - // to deliver the head element that was already on the queue. Just fall through - // without doing anything else. - } else { - // Data (or a queue signal) arrived! - // - // If it's data, pull the data off the pipe, build a new Message with it, put it on - // the DVM-side MessageQueue (pointed to by the 'mqueue' parameter), then proceed - // into the queue-signal case. - // - // If a queue signal arrived, just consume any data pending in that pipe and - // fall out. - bool queue_signalled = (FD_ISSET(mQueueReadFd, &fdset) != 0); - - for (size_t i = 0; i < mInputPipes.size(); i++) { - if (FD_ISSET(mInputPipes[i].fd, &fdset)) { - queueEventFromPipe(mInputPipes[i], mqueue); - queue_signalled = true; // we know a priori that queueing the event does this - } - } - - // Okay, stuff went on the queue. Consume the contents of the signal pipe - // now that we're awake and about to start dispatching messages again. - if (queue_signalled) { - uint8_t buf[16]; - ssize_t nRead; - do { - nRead = read(mQueueReadFd, buf, sizeof(buf)); - } while (nRead > 0); // in nonblocking mode we'll get -1 when it's drained - } - } - - return 0; +NativeMessageQueue::~NativeMessageQueue() { } -// signals to the queue pipe are one undefined byte. it's just a "data has arrived" token -// and the pipe is drained on receipt of at least one signal -void MessageQueueNative::signalQueuePipe() { - int dummy[1]; - write(mQueueWriteFd, dummy, 1); +bool NativeMessageQueue::pollOnce(int timeoutMillis) { + return mPollLoop->pollOnce(timeoutMillis); } -void MessageQueueNative::queueEventFromPipe(const InputPipe& inPipe, jobject mqueue) { - // !!! TODO: read the event data from the InputPipe's ashmem region, convert it to a DVM - // event object of the proper type [MotionEvent or KeyEvent], create a Message holding - // it as appropriate, point the Message to the Handler associated with this InputPipe, - // and call up to the DVM MessageQueue implementation to enqueue it for delivery. +void NativeMessageQueue::wake() { + mPollLoop->wake(); } -// the number of registered pipes on success; < 0 on error -int MessageQueueNative::registerInputPipe(JNIEnv* env, - int socketFd, int memRegionFd, jobject handler) { - // make sure this fd is not already known to us - for (size_t i = 0; i < mInputPipes.size(); i++) { - if (mInputPipes[i].fd == socketFd) { - LOGE("Attempt to re-register input fd %d", socketFd); - return -1; - } - } +// ---------------------------------------------------------------------------- - mInputPipes.push( InputPipe(socketFd, memRegionFd, env->NewGlobalRef(handler)) ); - return mInputPipes.size(); +static NativeMessageQueue* android_os_MessageQueue_getNativeMessageQueue(JNIEnv* env, + jobject messageQueueObj) { + jint intPtr = env->GetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr); + return reinterpret_cast<NativeMessageQueue*>(intPtr); } -// Remove an input pipe from our bookkeeping. Also closes the socket and ashmem -// region file descriptor! -// -// returns the number of remaining input pipes on success; < 0 on error -int MessageQueueNative::unregisterInputPipe(JNIEnv* env, int socketFd) { - for (size_t i = 0; i < mInputPipes.size(); i++) { - if (mInputPipes[i].fd == socketFd) { - close(mInputPipes[i].fd); - close(mInputPipes[i].region); - env->DeleteGlobalRef(mInputPipes[i].handler); - mInputPipes.removeAt(i); - return mInputPipes.size(); - } - } - LOGW("Tried to unregister input pipe %d but not found!", socketFd); - return -1; +static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, + NativeMessageQueue* nativeMessageQueue) { + env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, + reinterpret_cast<jint>(nativeMessageQueue)); } -// ---------------------------------------------------------------------------- - -namespace android { - -static void android_os_MessageQueue_init(JNIEnv* env, jobject obj) { - // Create the pipe - int fds[2]; - int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); - if (err != 0) { - doThrow(env, "java/lang/RuntimeException", "Unable to create socket pair"); - } +sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj) { + NativeMessageQueue* nativeMessageQueue = + android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj); + return nativeMessageQueue != NULL ? nativeMessageQueue->getPollLoop() : NULL; +} - MessageQueueNative *mqn = new MessageQueueNative(fds[0], fds[1]); - if (mqn == NULL) { - close(fds[0]); - close(fds[1]); - doThrow(env, "java/lang/RuntimeException", "Unable to allocate native queue"); +static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { + NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); + if (! nativeMessageQueue) { + jniThrowRuntimeException(env, "Unable to allocate native queue"); + return; } - int flags = fcntl(fds[0], F_GETFL); - fcntl(fds[0], F_SETFL, flags | O_NONBLOCK); - flags = fcntl(fds[1], F_GETFL); - fcntl(fds[1], F_SETFL, flags | O_NONBLOCK); - - env->SetIntField(obj, gMessageQueueOffsets.mObject, (jint)mqn); + android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue); } -static void android_os_MessageQueue_signal(JNIEnv* env, jobject obj) { - MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); - if (mqn != NULL) { - mqn->signalQueuePipe(); - } else { - doThrow(env, "java/lang/IllegalStateException", "Queue not initialized"); +static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jobject obj) { + NativeMessageQueue* nativeMessageQueue = + android_os_MessageQueue_getNativeMessageQueue(env, obj); + if (nativeMessageQueue) { + android_os_MessageQueue_setNativeMessageQueue(env, obj, NULL); + delete nativeMessageQueue; } } -static int android_os_MessageQueue_waitForNext(JNIEnv* env, jobject obj, jlong when) { - MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); - if (mqn != NULL) { - int res = mqn->waitForSignal(obj, when); - return res; // the DVM event, if any, has been constructed and queued now - } - - return -1; +static void throwQueueNotInitialized(JNIEnv* env) { + jniThrowException(env, "java/lang/IllegalStateException", "Message queue not initialized"); } -static void android_os_MessageQueue_registerInputStream(JNIEnv* env, jobject obj, - jint socketFd, jint regionFd, jobject handler) { - MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); - if (mqn != NULL) { - mqn->registerInputPipe(env, socketFd, regionFd, handler); - } else { - doThrow(env, "java/lang/IllegalStateException", "Queue not initialized"); +static jboolean android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, + jint timeoutMillis) { + NativeMessageQueue* nativeMessageQueue = + android_os_MessageQueue_getNativeMessageQueue(env, obj); + if (! nativeMessageQueue) { + throwQueueNotInitialized(env); + return false; } + return nativeMessageQueue->pollOnce(timeoutMillis); } -static void android_os_MessageQueue_unregisterInputStream(JNIEnv* env, jobject obj, - jint socketFd) { - MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject); - if (mqn != NULL) { - mqn->unregisterInputPipe(env, socketFd); - } else { - doThrow(env, "java/lang/IllegalStateException", "Queue not initialized"); +static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj) { + NativeMessageQueue* nativeMessageQueue = + android_os_MessageQueue_getNativeMessageQueue(env, obj); + if (! nativeMessageQueue) { + throwQueueNotInitialized(env); + return; } + return nativeMessageQueue->wake(); } // ---------------------------------------------------------------------------- -const char* const kKeyEventPathName = "android/view/KeyEvent"; -const char* const kMessagePathName = "android/os/Message"; -const char* const kMessageQueuePathName = "android/os/MessageQueue"; - static JNINativeMethod gMessageQueueMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "()V", (void*)android_os_MessageQueue_init }, - { "nativeSignal", "()V", (void*)android_os_MessageQueue_signal }, - { "nativeWaitForNext", "(J)I", (void*)android_os_MessageQueue_waitForNext }, - { "nativeRegisterInputStream", "(IILandroid/os/Handler;)V", (void*)android_os_MessageQueue_registerInputStream }, - { "nativeUnregisterInputStream", "(I)V", (void*)android_os_MessageQueue_unregisterInputStream }, + { "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit }, + { "nativeDestroy", "()V", (void*)android_os_MessageQueue_nativeDestroy }, + { "nativePollOnce", "(I)Z", (void*)android_os_MessageQueue_nativePollOnce }, + { "nativeWake", "()V", (void*)android_os_MessageQueue_nativeWake } }; +#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_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + int register_android_os_MessageQueue(JNIEnv* env) { - jclass clazz; + int res = jniRegisterNativeMethods(env, "android/os/MessageQueue", + gMessageQueueMethods, NELEM(gMessageQueueMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + FIND_CLASS(gMessageQueueClassInfo.clazz, "android/os/MessageQueue"); - clazz = env->FindClass(kMessageQueuePathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.MessageQueue"); - gMessageQueueOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gMessageQueueOffsets.mObject = env->GetFieldID(clazz, "mObject", "I"); - assert(gMessageQueueOffsets.mObject); - - clazz = env->FindClass(kMessagePathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Message"); - gMessageOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gMessageOffsets.mObtain = env->GetStaticMethodID(clazz, "obtain", - "(Landroid/os/Handler;ILjava/lang/Object;)Landroid/os/Message;"); - assert(gMessageOffsets.mObtain); - - clazz = env->FindClass(kKeyEventPathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.view.KeyEvent"); - gKeyEventOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gKeyEventOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(JJIIIIIII)V"); - assert(gKeyEventOffsets.mConstructor); + GET_FIELD_ID(gMessageQueueClassInfo.mPtr, gMessageQueueClassInfo.clazz, + "mPtr", "I"); - return AndroidRuntime::registerNativeMethods(env, kMessageQueuePathName, - gMessageQueueMethods, NELEM(gMessageQueueMethods)); + return 0; } - -}; // end of namespace android +} // namespace android diff --git a/core/jni/android_os_MessageQueue.h b/core/jni/android_os_MessageQueue.h new file mode 100644 index 0000000..5c742e2 --- /dev/null +++ b/core/jni/android_os_MessageQueue.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_OS_MESSAGEQUEUE_H +#define _ANDROID_OS_MESSAGEQUEUE_H + +#include "jni.h" + +namespace android { + +class PollLoop; + +extern sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj); + +} // namespace android + +#endif // _ANDROID_OS_MESSAGEQUEUE_H diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp new file mode 100644 index 0000000..47bb073 --- /dev/null +++ b/core/jni/android_view_InputChannel.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2010 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 "InputChannel-JNI" + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <binder/Parcel.h> +#include <utils/Log.h> +#include <ui/InputTransport.h> +#include "android_view_InputChannel.h" +#include "android_util_Binder.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + + jfieldID mPtr; // native object attached to the DVM InputChannel + jmethodID ctor; +} gInputChannelClassInfo; + +// ---------------------------------------------------------------------------- + +class NativeInputChannel { +public: + NativeInputChannel(const sp<InputChannel>& inputChannel); + ~NativeInputChannel(); + + inline sp<InputChannel> getInputChannel() { return mInputChannel; } + + void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data); + void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj); + +private: + sp<InputChannel> mInputChannel; + InputChannelObjDisposeCallback mDisposeCallback; + void* mDisposeData; +}; + +// ---------------------------------------------------------------------------- + +NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) : + mInputChannel(inputChannel), mDisposeCallback(NULL) { +} + +NativeInputChannel::~NativeInputChannel() { +} + +void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) { + mDisposeCallback = callback; + mDisposeData = data; +} + +void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) { + if (mDisposeCallback) { + mDisposeCallback(env, obj, mInputChannel, mDisposeData); + mDisposeCallback = NULL; + mDisposeData = NULL; + } +} + +// ---------------------------------------------------------------------------- + +static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env, + jobject inputChannelObj) { + jint intPtr = env->GetIntField(inputChannelObj, gInputChannelClassInfo.mPtr); + return reinterpret_cast<NativeInputChannel*>(intPtr); +} + +static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj, + NativeInputChannel* nativeInputChannel) { + env->SetIntField(inputChannelObj, gInputChannelClassInfo.mPtr, + reinterpret_cast<jint>(nativeInputChannel)); +} + +sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) { + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); + return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL; +} + +void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj, + InputChannelObjDisposeCallback callback, void* data) { + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, inputChannelObj); + if (nativeInputChannel == NULL) { + LOGW("Cannot set dispose callback because input channel object has not been initialized."); + } else { + nativeInputChannel->setDisposeCallback(callback, data); + } +} + +static jobject android_view_InputChannel_createInputChannel(JNIEnv* env, + NativeInputChannel* nativeInputChannel) { + jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, + gInputChannelClassInfo.ctor); + android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); + return inputChannelObj; +} + +static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, + jclass clazz, jstring nameObj) { + const char* nameChars = env->GetStringUTFChars(nameObj, NULL); + String8 name(nameChars); + env->ReleaseStringUTFChars(nameObj, nameChars); + + InputChannel* serverChannel; + InputChannel* clientChannel; + status_t result = InputChannel::openInputChannelPair(name, & serverChannel, & clientChannel); + + if (result) { + LOGE("Could not open input channel pair. status=%d", result); + jniThrowRuntimeException(env, "Could not open input channel pair."); + return NULL; + } + + // TODO more robust error checking + jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, + new NativeInputChannel(serverChannel)); + jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, + new NativeInputChannel(clientChannel)); + + jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); + env->SetObjectArrayElement(channelPair, 0, serverChannelObj); + env->SetObjectArrayElement(channelPair, 1, clientChannelObj); + return channelPair; +} + +static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) { + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, obj); + if (nativeInputChannel) { + if (finalized) { + LOGW("Input channel object '%s' was finalized without being disposed!", + nativeInputChannel->getInputChannel()->getName().string()); + } + + nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj); + + android_view_InputChannel_setNativeInputChannel(env, obj, NULL); + delete nativeInputChannel; + } +} + +static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj, + jobject otherObj) { + if (android_view_InputChannel_getInputChannel(env, otherObj) != NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Other object already has a native input channel."); + return; + } + + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, obj); + android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel); + android_view_InputChannel_setNativeInputChannel(env, obj, NULL); +} + +static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj, + jobject parcelObj) { + if (android_view_InputChannel_getInputChannel(env, obj) != NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "This object already has a native input channel."); + return; + } + + Parcel* parcel = parcelForJavaObject(env, parcelObj); + if (parcel) { + bool isInitialized = parcel->readInt32(); + if (isInitialized) { + String8 name = parcel->readString8(); + int32_t ashmemFd = dup(parcel->readFileDescriptor()); + int32_t receivePipeFd = dup(parcel->readFileDescriptor()); + int32_t sendPipeFd = dup(parcel->readFileDescriptor()); + if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) { + if (ashmemFd >= 0) ::close(ashmemFd); + if (receivePipeFd >= 0) ::close(receivePipeFd); + if (sendPipeFd >= 0) ::close(sendPipeFd); + jniThrowRuntimeException(env, + "Could not read input channel file descriptors from parcel."); + return; + } + + InputChannel* inputChannel = new InputChannel(name, ashmemFd, + receivePipeFd, sendPipeFd); + NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel); + + android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel); + } + } +} + +static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj, + jobject parcelObj) { + Parcel* parcel = parcelForJavaObject(env, parcelObj); + if (parcel) { + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, obj); + if (nativeInputChannel) { + sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel(); + + parcel->writeInt32(1); + parcel->writeString8(inputChannel->getName()); + parcel->writeDupFileDescriptor(inputChannel->getAshmemFd()); + parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd()); + parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd()); + } else { + parcel->writeInt32(0); + } + } +} + +static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) { + NativeInputChannel* nativeInputChannel = + android_view_InputChannel_getNativeInputChannel(env, obj); + if (! nativeInputChannel) { + return NULL; + } + + jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string()); + return name; +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gInputChannelMethods[] = { + /* name, signature, funcPtr */ + { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;", + (void*)android_view_InputChannel_nativeOpenInputChannelPair }, + { "nativeDispose", "(Z)V", + (void*)android_view_InputChannel_nativeDispose }, + { "nativeTransferTo", "(Landroid/view/InputChannel;)V", + (void*)android_view_InputChannel_nativeTransferTo }, + { "nativeReadFromParcel", "(Landroid/os/Parcel;)V", + (void*)android_view_InputChannel_nativeReadFromParcel }, + { "nativeWriteToParcel", "(Landroid/os/Parcel;)V", + (void*)android_view_InputChannel_nativeWriteToParcel }, + { "nativeGetName", "()Ljava/lang/String;", + (void*)android_view_InputChannel_nativeGetName }, +}; + +#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); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_view_InputChannel(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/InputChannel", + gInputChannelMethods, NELEM(gInputChannelMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel"); + + GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz, + "mPtr", "I"); + + GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz, + "<init>", "()V"); + + return 0; +} + +} // namespace android diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h new file mode 100644 index 0000000..ac1defb --- /dev/null +++ b/core/jni/android_view_InputChannel.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_VIEW_INPUTCHANNEL_H +#define _ANDROID_VIEW_INPUTCHANNEL_H + +#include "jni.h" + +namespace android { + +class InputChannel; + +typedef void (*InputChannelObjDisposeCallback)(JNIEnv* env, jobject inputChannelObj, + const sp<InputChannel>& inputChannel, void* data); + +extern sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, + jobject inputChannelObj); + +/* Sets a callback that is invoked when the InputChannel DVM object is disposed (or finalized). + * This is used to automatically dispose of other native objects in the input dispatcher + * and input queue to prevent memory leaks. */ +extern void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj, + InputChannelObjDisposeCallback callback, void* data = NULL); + +} // namespace android + +#endif // _ANDROID_OS_INPUTCHANNEL_H diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp new file mode 100644 index 0000000..9cbde25 --- /dev/null +++ b/core/jni/android_view_InputQueue.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2010 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 "InputQueue-JNI" + +//#define LOG_NDEBUG 0 + +// Log debug messages about the dispatch cycle. +#define DEBUG_DISPATCH_CYCLE 1 + + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> +#include <utils/PollLoop.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <ui/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 dispatchKeyEvent; + jmethodID dispatchMotionEvent; +} gInputQueueClassInfo; + +// ---------------------------------------------------------------------------- + +class NativeInputQueue { +public: + NativeInputQueue(); + virtual ~NativeInputQueue(); + + status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj, + jobject inputHandlerObj, jobject messageQueueObj); + + status_t unregisterInputChannel(JNIEnv* env, jobject inputChannelObj); + + status_t finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish); + +private: + class Connection : public RefBase { + protected: + virtual ~Connection(); + + public: + enum Status { + // Everything is peachy. + STATUS_NORMAL, + // The input channel has been unregistered. + STATUS_ZOMBIE + }; + + Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop); + + inline const char* getInputChannelName() { return inputChannel->getName().string(); } + + Status status; + + sp<InputChannel> inputChannel; + InputConsumer inputConsumer; + sp<PollLoop> pollLoop; + jobject inputHandlerObjGlobal; + PreallocatedInputEventFactory inputEventFactory; + + // The sequence number of the current event being dispatched. + // This is used as part of the finished token as a way to determine whether the finished + // token is still valid before sending a finished signal back to the publisher. + uint32_t messageSeqNum; + + // True if a message has been received from the publisher but not yet finished. + bool messageInProgress; + }; + + Mutex mLock; + KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd; + + static void handleInputChannelDisposed(JNIEnv* env, + jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data); + + static bool handleReceiveCallback(int receiveFd, int events, void* data); + + static jlong generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum); + + static void parseFinishedToken(jlong finishedToken, + int32_t* outReceiveFd, uint32_t* outMessageIndex); +}; + +// ---------------------------------------------------------------------------- + +NativeInputQueue::NativeInputQueue() { +} + +NativeInputQueue::~NativeInputQueue() { +} + +status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj, + jobject inputHandlerObj, jobject messageQueueObj) { + sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, + inputChannelObj); + if (inputChannel == NULL) { + LOGW("Input channel is not initialized."); + return BAD_VALUE; + } + + sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj); + + int receiveFd; + { // acquire lock + AutoMutex _l(mLock); + + receiveFd = inputChannel->getReceivePipeFd(); + if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) { + LOGW("Attempted to register already registered input channel '%s'", + inputChannel->getName().string()); + return BAD_VALUE; + } + + sp<Connection> connection = new Connection(inputChannel, pollLoop); + status_t result = connection->inputConsumer.initialize(); + if (result) { + LOGW("Failed to initialize input consumer for input channel '%s', status=%d", + inputChannel->getName().string(), result); + return result; + } + + connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj); + + mConnectionsByReceiveFd.add(receiveFd, connection); + } // release lock + + android_view_InputChannel_setDisposeCallback(env, inputChannelObj, + handleInputChannelDisposed, this); + + pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, NULL); + return OK; +} + +status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) { + sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, + inputChannelObj); + if (inputChannel == NULL) { + LOGW("Input channel is not initialized."); + return BAD_VALUE; + } + + int32_t receiveFd; + sp<Connection> connection; + { // acquire lock + AutoMutex _l(mLock); + + receiveFd = inputChannel->getReceivePipeFd(); + ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd); + if (connectionIndex < 0) { + LOGW("Attempted to unregister already unregistered input channel '%s'", + inputChannel->getName().string()); + return BAD_VALUE; + } + + connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + mConnectionsByReceiveFd.removeItemsAt(connectionIndex); + + connection->status = Connection::STATUS_ZOMBIE; + + env->DeleteGlobalRef(connection->inputHandlerObjGlobal); + connection->inputHandlerObjGlobal = NULL; + } // release lock + + android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); + + connection->pollLoop->removeCallback(receiveFd); + return OK; +} + +status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) { + int32_t receiveFd; + uint32_t messageSeqNum; + parseFinishedToken(finishedToken, &receiveFd, &messageSeqNum); + + { // acquire lock + AutoMutex _l(mLock); + + ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd); + if (connectionIndex < 0) { + if (! ignoreSpuriousFinish) { + LOGW("Attempted to finish input on channel that is no longer registered."); + } + return DEAD_OBJECT; + } + + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) { + if (! ignoreSpuriousFinish) { + LOGW("Attempted to finish input twice on channel '%s'.", + connection->getInputChannelName()); + } + return INVALID_OPERATION; + } + + connection->messageInProgress = false; + + status_t status = connection->inputConsumer.sendFinishedSignal(); + if (status) { + LOGW("Failed to send finished signal on channel '%s'. status=%d", + connection->getInputChannelName(), status); + return status; + } + +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Finished event.", + connection->getInputChannelName()); +#endif + } // release lock + + return OK; +} + +void NativeInputQueue::handleInputChannelDisposed(JNIEnv* env, + jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) { + LOGW("Input channel object '%s' was disposed without first being unregistered with " + "the input queue!", inputChannel->getName().string()); + + NativeInputQueue* q = static_cast<NativeInputQueue*>(data); + q->unregisterInputChannel(env, inputChannelObj); +} + +bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) { + NativeInputQueue* q = static_cast<NativeInputQueue*>(data); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + sp<Connection> connection; + InputEvent* inputEvent; + jobject inputHandlerObjLocal; + jlong finishedToken; + { // acquire lock + AutoMutex _l(q->mLock); + + ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd); + if (connectionIndex < 0) { + LOGE("Received spurious receive callback for unknown input channel. " + "fd=%d, events=0x%x", receiveFd, events); + return false; // remove the callback + } + + connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex); + if (events & (POLLERR | POLLHUP | POLLNVAL)) { + LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " + "events=0x%x", connection->getInputChannelName(), events); + return false; // remove the callback + } + + if (! (events & POLLIN)) { + LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " + "events=0x%x", connection->getInputChannelName(), events); + return true; + } + + status_t status = connection->inputConsumer.receiveDispatchSignal(); + if (status) { + LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", + connection->getInputChannelName(), status); + return false; // remove the callback + } + + if (connection->messageInProgress) { + LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.", + connection->getInputChannelName()); + return true; + } + + status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent); + if (status) { + LOGW("channel '%s' ~ Failed to consume input event. status=%d", + connection->getInputChannelName(), status); + connection->inputConsumer.sendFinishedSignal(); + return true; + } + + connection->messageInProgress = true; + connection->messageSeqNum += 1; + + finishedToken = generateFinishedToken(receiveFd, connection->messageSeqNum); + + inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal); + } // release lock + + // Invoke the handler outside of the lock. + // + // Note: inputEvent is stored in a field of the connection object which could potentially + // become disposed due to the input channel being unregistered concurrently. + // For this reason, we explicitly keep the connection object alive by holding + // a strong pointer to it within this scope. We also grabbed a local reference to + // the input handler object itself for the same reason. + + int32_t inputEventType = inputEvent->getType(); + int32_t inputEventNature = inputEvent->getNature(); + + jobject inputEventObj; + jmethodID dispatchMethodId; + switch (inputEventType) { + case INPUT_EVENT_TYPE_KEY: +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Received key event.", connection->getInputChannelName()); +#endif + inputEventObj = android_view_KeyEvent_fromNative(env, + static_cast<KeyEvent*>(inputEvent)); + dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent; + break; + + case INPUT_EVENT_TYPE_MOTION: +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName()); +#endif + inputEventObj = android_view_MotionEvent_fromNative(env, + static_cast<MotionEvent*>(inputEvent)); + dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent; + break; + + default: + assert(false); // InputConsumer should prevent this from ever happening + inputEventObj = NULL; + } + + if (! inputEventObj) { + LOGW("channel '%s' ~ Failed to obtain DVM event object.", + connection->getInputChannelName()); + env->DeleteLocalRef(inputHandlerObjLocal); + q->finished(env, finishedToken, false); + return true; + } + +#if DEBUG_DISPATCH_CYCLE + LOGD("Invoking input handler."); +#endif + env->CallStaticVoidMethod(gInputQueueClassInfo.clazz, + dispatchMethodId, inputHandlerObjLocal, inputEventObj, + jint(inputEventNature), jlong(finishedToken)); +#if DEBUG_DISPATCH_CYCLE + LOGD("Returned from input handler."); +#endif + + if (env->ExceptionCheck()) { + LOGE("An exception occurred while invoking the input handler for an event."); + LOGE_EX(env); + env->ExceptionClear(); + + q->finished(env, finishedToken, true /*ignoreSpuriousFinish*/); + } + + env->DeleteLocalRef(inputEventObj); + env->DeleteLocalRef(inputHandlerObjLocal); + return true; +} + +jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum) { + return (jlong(receiveFd) << 32) | jlong(messageSeqNum); +} + +void NativeInputQueue::parseFinishedToken(jlong finishedToken, + int32_t* outReceiveFd, uint32_t* outMessageIndex) { + *outReceiveFd = int32_t(finishedToken >> 32); + *outMessageIndex = uint32_t(finishedToken & 0xffffffff); +} + +// ---------------------------------------------------------------------------- + +NativeInputQueue::Connection::Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) : + status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel), + pollLoop(pollLoop), inputHandlerObjGlobal(NULL), + messageSeqNum(0), messageInProgress(false) { +} + +NativeInputQueue::Connection::~Connection() { +} + +// ---------------------------------------------------------------------------- + +static NativeInputQueue gNativeInputQueue; + +static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz, + jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) { + status_t status = gNativeInputQueue.registerInputChannel( + env, inputChannelObj, inputHandlerObj, messageQueueObj); + if (status) { + jniThrowRuntimeException(env, "Failed to register input channel. " + "Check logs for details."); + } +} + +static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz, + jobject inputChannelObj) { + status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj); + if (status) { + jniThrowRuntimeException(env, "Failed to unregister input channel. " + "Check logs for details."); + } +} + +static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz, + jlong finishedToken) { + status_t status = gNativeInputQueue.finished( + env, finishedToken, false /*ignoreSpuriousFinish*/); + if (status) { + jniThrowRuntimeException(env, "Failed to finish input event. " + "Check logs for details."); + } +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gInputQueueMethods[] = { + /* name, signature, funcPtr */ + { "nativeRegisterInputChannel", + "(Landroid/view/InputChannel;Landroid/view/InputHandler;Landroid/os/MessageQueue;)V", + (void*)android_view_InputQueue_nativeRegisterInputChannel }, + { "nativeUnregisterInputChannel", + "(Landroid/view/InputChannel;)V", + (void*)android_view_InputQueue_nativeUnregisterInputChannel }, + { "nativeFinished", "(J)V", + (void*)android_view_InputQueue_nativeFinished } +}; + +#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_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method " methodName); + +int register_android_view_InputQueue(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/InputQueue", + gInputQueueMethods, NELEM(gInputQueueMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + FIND_CLASS(gInputQueueClassInfo.clazz, "android/view/InputQueue"); + + GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchKeyEvent, gInputQueueClassInfo.clazz, + "dispatchKeyEvent", + "(Landroid/view/InputHandler;Landroid/view/KeyEvent;IJ)V"); + + GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchMotionEvent, gInputQueueClassInfo.clazz, + "dispatchMotionEvent", + "(Landroid/view/InputHandler;Landroid/view/MotionEvent;IJ)V"); + return 0; +} + +} // namespace android diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp new file mode 100644 index 0000000..e2a1f23 --- /dev/null +++ b/core/jni/android_view_InputTarget.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 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 "InputTarget-JNI" + +#include "JNIHelp.h" + +#include <utils/Log.h> +#include <ui/InputDispatchPolicy.h> +#include <ui/InputTransport.h> +#include "android_view_InputTarget.h" +#include "android_view_InputChannel.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + + jfieldID mInputChannel; + jfieldID mFlags; + jfieldID mTimeoutNanos; + jfieldID mXOffset; + jfieldID mYOffset; +} gInputTargetClassInfo; + +// ---------------------------------------------------------------------------- + +void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj, + InputTarget* outInputTarget) { + jobject inputChannelObj = env->GetObjectField(inputTargetObj, + gInputTargetClassInfo.mInputChannel); + jint flags = env->GetIntField(inputTargetObj, + gInputTargetClassInfo.mFlags); + jlong timeoutNanos = env->GetLongField(inputTargetObj, + gInputTargetClassInfo.mTimeoutNanos); + jfloat xOffset = env->GetFloatField(inputTargetObj, + gInputTargetClassInfo.mXOffset); + jfloat yOffset = env->GetFloatField(inputTargetObj, + gInputTargetClassInfo.mYOffset); + + outInputTarget->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); + outInputTarget->flags = flags; + outInputTarget->timeout = timeoutNanos; + outInputTarget->xOffset = xOffset; + outInputTarget->yOffset = yOffset; + + env->DeleteLocalRef(inputChannelObj); +} + +// ---------------------------------------------------------------------------- + +#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_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_view_InputTarget(JNIEnv* env) { + FIND_CLASS(gInputTargetClassInfo.clazz, "android/view/InputTarget"); + + GET_FIELD_ID(gInputTargetClassInfo.mInputChannel, gInputTargetClassInfo.clazz, + "mInputChannel", "Landroid/view/InputChannel;"); + + GET_FIELD_ID(gInputTargetClassInfo.mFlags, gInputTargetClassInfo.clazz, + "mFlags", "I"); + + GET_FIELD_ID(gInputTargetClassInfo.mTimeoutNanos, gInputTargetClassInfo.clazz, + "mTimeoutNanos", "J"); + + GET_FIELD_ID(gInputTargetClassInfo.mXOffset, gInputTargetClassInfo.clazz, + "mXOffset", "F"); + + GET_FIELD_ID(gInputTargetClassInfo.mYOffset, gInputTargetClassInfo.clazz, + "mYOffset", "F"); + + return 0; +} + +} // namespace android diff --git a/core/jni/android_view_InputTarget.h b/core/jni/android_view_InputTarget.h new file mode 100644 index 0000000..9230b1b --- /dev/null +++ b/core/jni/android_view_InputTarget.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_VIEW_INPUTTARGET_H +#define _ANDROID_VIEW_INPUTTARGET_H + +#include "jni.h" + +namespace android { + +class InputTarget; + +extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj, + InputTarget* outInputTarget); + +} // namespace android + +#endif // _ANDROID_OS_INPUTTARGET_H diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp new file mode 100644 index 0000000..df3b952 --- /dev/null +++ b/core/jni/android_view_KeyEvent.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2010 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 "KeyEvent-JNI" + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> +#include <ui/Input.h> +#include "android_view_KeyEvent.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + + jmethodID ctor; + + jfieldID mMetaState; + jfieldID mAction; + jfieldID mKeyCode; + jfieldID mScanCode; + jfieldID mRepeatCount; + jfieldID mDeviceId; + jfieldID mFlags; + jfieldID mDownTime; + jfieldID mEventTime; + jfieldID mCharacters; +} gKeyEventClassInfo; + +// ---------------------------------------------------------------------------- + +jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) { + return env->NewObject(gKeyEventClassInfo.clazz, gKeyEventClassInfo.ctor, + nanoseconds_to_milliseconds(event->getDownTime()), + nanoseconds_to_milliseconds(event->getEventTime()), + event->getAction(), + event->getKeyCode(), + event->getRepeatCount(), + event->getMetaState(), + event->getDeviceId(), + event->getScanCode(), + event->getFlags()); +} + +void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature, + KeyEvent* event) { + jint metaState = env->GetIntField(eventObj, gKeyEventClassInfo.mMetaState); + jint action = env->GetIntField(eventObj, gKeyEventClassInfo.mAction); + jint keyCode = env->GetIntField(eventObj, gKeyEventClassInfo.mKeyCode); + jint scanCode = env->GetIntField(eventObj, gKeyEventClassInfo.mScanCode); + jint repeatCount = env->GetIntField(eventObj, gKeyEventClassInfo.mRepeatCount); + jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId); + jint flags = env->GetIntField(eventObj, gKeyEventClassInfo.mFlags); + jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime); + jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime); + + event->initialize(deviceId, nature, action, flags, keyCode, scanCode, metaState, repeatCount, + milliseconds_to_nanoseconds(downTime), + milliseconds_to_nanoseconds(eventTime)); +} + +// ---------------------------------------------------------------------------- + +#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, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_view_KeyEvent(JNIEnv* env) { + FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); + + GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz, + "<init>", "(JJIIIIIII)V"); + + GET_FIELD_ID(gKeyEventClassInfo.mMetaState, gKeyEventClassInfo.clazz, + "mMetaState", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mAction, gKeyEventClassInfo.clazz, + "mAction", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mKeyCode, gKeyEventClassInfo.clazz, + "mKeyCode", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mScanCode, gKeyEventClassInfo.clazz, + "mScanCode", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mRepeatCount, gKeyEventClassInfo.clazz, + "mRepeatCount", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mDeviceId, gKeyEventClassInfo.clazz, + "mDeviceId", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mFlags, gKeyEventClassInfo.clazz, + "mFlags", "I"); + GET_FIELD_ID(gKeyEventClassInfo.mDownTime, gKeyEventClassInfo.clazz, + "mDownTime", "J"); + GET_FIELD_ID(gKeyEventClassInfo.mEventTime, gKeyEventClassInfo.clazz, + "mEventTime", "J"); + GET_FIELD_ID(gKeyEventClassInfo.mCharacters, gKeyEventClassInfo.clazz, + "mCharacters", "Ljava/lang/String;"); + + return 0; +} + +} // namespace android diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h new file mode 100644 index 0000000..3c71b1a --- /dev/null +++ b/core/jni/android_view_KeyEvent.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_VIEW_KEYEVENT_H +#define _ANDROID_VIEW_KEYEVENT_H + +#include "jni.h" + +namespace android { + +class KeyEvent; + +/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance. */ +extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event); + +/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance. */ +extern void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature, + KeyEvent* event); + +} // namespace android + +#endif // _ANDROID_OS_KEYEVENT_H diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp new file mode 100644 index 0000000..629c8fe --- /dev/null +++ b/core/jni/android_view_MotionEvent.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2010 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 "MotionEvent-JNI" + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <utils/Log.h> +#include <ui/Input.h> +#include "android_view_MotionEvent.h" + +// Number of float items per entry in a DVM sample data array +#define NUM_SAMPLE_DATA 4 + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + + jmethodID obtain; + jmethodID recycle; + + jfieldID mDownTime; + jfieldID mEventTimeNano; + jfieldID mAction; + jfieldID mRawX; + jfieldID mRawY; + jfieldID mXPrecision; + jfieldID mYPrecision; + jfieldID mDeviceId; + jfieldID mEdgeFlags; + jfieldID mMetaState; + jfieldID mNumPointers; + jfieldID mNumSamples; + jfieldID mPointerIdentifiers; + jfieldID mDataSamples; + jfieldID mTimeSamples; +} gMotionEventClassInfo; + +// ---------------------------------------------------------------------------- + +jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) { + jint numPointers = jint(event->getPointerCount()); + jint numHistoricalSamples = jint(event->getHistorySize()); + jint numSamples = numHistoricalSamples + 1; + + jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, + gMotionEventClassInfo.obtain, numPointers, numSamples); + if (env->ExceptionCheck()) { + LOGE("An exception occurred while obtaining a motion event."); + LOGE_EX(env); + env->ExceptionClear(); + return NULL; + } + + // MotionEvent.mEventTimeNano is the time of the oldest sample because + // MotionEvent.addBatch does not update it as successive samples are added. + jlong eventTimeNano = numHistoricalSamples != 0 + ? event->getHistoricalEventTime(0) + : event->getEventTime(); + + env->SetLongField(eventObj, gMotionEventClassInfo.mDownTime, + nanoseconds_to_milliseconds(event->getDownTime())); + env->SetLongField(eventObj, gMotionEventClassInfo.mEventTimeNano, + eventTimeNano); + env->SetIntField(eventObj, gMotionEventClassInfo.mAction, + event->getAction()); + env->SetFloatField(eventObj, gMotionEventClassInfo.mRawX, + event->getRawX()); + env->SetFloatField(eventObj, gMotionEventClassInfo.mRawY, + event->getRawY()); + env->SetFloatField(eventObj, gMotionEventClassInfo.mXPrecision, + event->getXPrecision()); + env->SetFloatField(eventObj, gMotionEventClassInfo.mYPrecision, + event->getYPrecision()); + env->SetIntField(eventObj, gMotionEventClassInfo.mDeviceId, + event->getDeviceId()); + env->SetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags, + event->getEdgeFlags()); + env->SetIntField(eventObj, gMotionEventClassInfo.mMetaState, + event->getMetaState()); + env->SetIntField(eventObj, gMotionEventClassInfo.mNumPointers, + numPointers); + env->SetIntField(eventObj, gMotionEventClassInfo.mNumSamples, + numSamples); + + jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj, + gMotionEventClassInfo.mPointerIdentifiers)); + jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj, + gMotionEventClassInfo.mDataSamples)); + jlongArray timeSampleArray = jlongArray(env->GetObjectField(eventObj, + gMotionEventClassInfo.mTimeSamples)); + + jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL); + jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL); + jlong* timeSamples = (jlong*)env->GetPrimitiveArrayCritical(timeSampleArray, NULL); + + for (jint i = 0; i < numPointers; i++) { + pointerIdentifiers[i] = event->getPointerId(i); + } + + // Most recent data is in first slot of the DVM array, followed by the oldest, + // and then all others are in order. + + jfloat* currentDataSample = dataSamples; + jlong* currentTimeSample = timeSamples; + + *(currentTimeSample++) = nanoseconds_to_milliseconds(event->getEventTime()); + for (jint j = 0; j < numPointers; j++) { + *(currentDataSample++) = event->getX(j); + *(currentDataSample++) = event->getY(j); + *(currentDataSample++) = event->getPressure(j); + *(currentDataSample++) = event->getSize(j); + } + + for (jint i = 0; i < numHistoricalSamples; i++) { + *(currentTimeSample++) = nanoseconds_to_milliseconds(event->getHistoricalEventTime(i)); + for (jint j = 0; j < numPointers; j++) { + *(currentDataSample++) = event->getHistoricalX(j, i); + *(currentDataSample++) = event->getHistoricalY(j, i); + *(currentDataSample++) = event->getHistoricalPressure(j, i); + *(currentDataSample++) = event->getHistoricalSize(j, i); + } + } + + env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, 0); + env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0); + env->ReleasePrimitiveArrayCritical(timeSampleArray, timeSamples, 0); + + env->DeleteLocalRef(pointerIdentifierArray); + env->DeleteLocalRef(dataSampleArray); + env->DeleteLocalRef(timeSampleArray); + return eventObj; +} + +void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature, + MotionEvent* event) { + // MotionEvent.mEventTimeNano is the time of the oldest sample because + // MotionEvent.addBatch does not update it as successive samples are added. + jlong downTime = env->GetLongField(eventObj, gMotionEventClassInfo.mDownTime); + jlong eventTimeNano = env->GetLongField(eventObj, gMotionEventClassInfo.mEventTimeNano); + jint action = env->GetIntField(eventObj, gMotionEventClassInfo.mAction); + jfloat rawX = env->GetFloatField(eventObj, gMotionEventClassInfo.mRawX); + jfloat rawY = env->GetFloatField(eventObj, gMotionEventClassInfo.mRawY); + jfloat xPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mXPrecision); + jfloat yPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mYPrecision); + jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId); + jint edgeFlags = env->GetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags); + jint metaState = env->GetIntField(eventObj, gMotionEventClassInfo.mMetaState); + jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers); + jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples); + jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj, + gMotionEventClassInfo.mPointerIdentifiers)); + jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj, + gMotionEventClassInfo.mDataSamples)); + jlongArray timeSampleArray = jlongArray(env->GetObjectField(eventObj, + gMotionEventClassInfo.mTimeSamples)); + + LOG_FATAL_IF(numPointers == 0, "numPointers was zero"); + LOG_FATAL_IF(numSamples == 0, "numSamples was zero"); + + jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL); + jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL); + jlong* timeSamples = (jlong*)env->GetPrimitiveArrayCritical(timeSampleArray, NULL); + + // Most recent data is in first slot of the DVM array, followed by the oldest, + // and then all others are in order. eventTimeNano is the time of the oldest sample + // since MotionEvent.addBatch does not update it. + + jint numHistoricalSamples = numSamples - 1; + jint dataSampleStride = numPointers * NUM_SAMPLE_DATA; + + const jfloat* currentDataSample; + const jlong* currentTimeSample; + if (numHistoricalSamples == 0) { + currentDataSample = dataSamples; + currentTimeSample = timeSamples; + } else { + currentDataSample = dataSamples + dataSampleStride; + currentTimeSample = timeSamples + 1; + } + + PointerCoords pointerCoords[MAX_POINTERS]; + for (jint j = 0; j < numPointers; j++) { + pointerCoords[j].x = *(currentDataSample++); + pointerCoords[j].y = *(currentDataSample++); + pointerCoords[j].pressure = *(currentDataSample++); + pointerCoords[j].size = *(currentDataSample++); + } + + event->initialize(deviceId, nature, action, edgeFlags, metaState, + rawX, rawY, xPrecision, yPrecision, + milliseconds_to_nanoseconds(downTime), eventTimeNano, + numPointers, pointerIdentifiers, pointerCoords); + + while (numHistoricalSamples > 0) { + numHistoricalSamples -= 1; + if (numHistoricalSamples == 0) { + currentDataSample = dataSamples; + currentTimeSample = timeSamples; + } + + nsecs_t sampleEventTime = milliseconds_to_nanoseconds(*(currentTimeSample++)); + + for (jint j = 0; j < numPointers; j++) { + pointerCoords[j].x = *(currentDataSample++); + pointerCoords[j].y = *(currentDataSample++); + pointerCoords[j].pressure = *(currentDataSample++); + pointerCoords[j].size = *(currentDataSample++); + } + + event->addSample(sampleEventTime, pointerCoords); + } + + env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(timeSampleArray, timeSamples, JNI_ABORT); + + env->DeleteLocalRef(pointerIdentifierArray); + env->DeleteLocalRef(dataSampleArray); + env->DeleteLocalRef(timeSampleArray); +} + +void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) { + env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle); + if (env->ExceptionCheck()) { + LOGW("An exception occurred while recycling a motion event."); + LOGW_EX(env); + env->ExceptionClear(); + } +} + +// ---------------------------------------------------------------------------- + +#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_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method" methodName); + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_view_MotionEvent(JNIEnv* env) { + FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + + GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz, + "obtain", "(II)Landroid/view/MotionEvent;"); + GET_METHOD_ID(gMotionEventClassInfo.recycle, gMotionEventClassInfo.clazz, + "recycle", "()V"); + + GET_FIELD_ID(gMotionEventClassInfo.mDownTime, gMotionEventClassInfo.clazz, + "mDownTime", "J"); + GET_FIELD_ID(gMotionEventClassInfo.mEventTimeNano, gMotionEventClassInfo.clazz, + "mEventTimeNano", "J"); + GET_FIELD_ID(gMotionEventClassInfo.mAction, gMotionEventClassInfo.clazz, + "mAction", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mRawX, gMotionEventClassInfo.clazz, + "mRawX", "F"); + GET_FIELD_ID(gMotionEventClassInfo.mRawY, gMotionEventClassInfo.clazz, + "mRawY", "F"); + GET_FIELD_ID(gMotionEventClassInfo.mXPrecision, gMotionEventClassInfo.clazz, + "mXPrecision", "F"); + GET_FIELD_ID(gMotionEventClassInfo.mYPrecision, gMotionEventClassInfo.clazz, + "mYPrecision", "F"); + GET_FIELD_ID(gMotionEventClassInfo.mDeviceId, gMotionEventClassInfo.clazz, + "mDeviceId", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mEdgeFlags, gMotionEventClassInfo.clazz, + "mEdgeFlags", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mMetaState, gMotionEventClassInfo.clazz, + "mMetaState", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mNumPointers, gMotionEventClassInfo.clazz, + "mNumPointers", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mNumSamples, gMotionEventClassInfo.clazz, + "mNumSamples", "I"); + GET_FIELD_ID(gMotionEventClassInfo.mPointerIdentifiers, gMotionEventClassInfo.clazz, + "mPointerIdentifiers", "[I"); + GET_FIELD_ID(gMotionEventClassInfo.mDataSamples, gMotionEventClassInfo.clazz, + "mDataSamples", "[F"); + GET_FIELD_ID(gMotionEventClassInfo.mTimeSamples, gMotionEventClassInfo.clazz, + "mTimeSamples", "[J"); + + return 0; +} + +} // namespace android diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h new file mode 100644 index 0000000..03ee32f --- /dev/null +++ b/core/jni/android_view_MotionEvent.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_VIEW_MOTIONEVENT_H +#define _ANDROID_VIEW_MOTIONEVENT_H + +#include "jni.h" + +namespace android { + +class MotionEvent; + +/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance. */ +extern jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event); + +/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance. */ +extern void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature, + MotionEvent* event); + +/* Recycles a DVM MotionEvent object. */ +extern void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj); + +} // namespace android + +#endif // _ANDROID_OS_KEYEVENT_H diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewRoot.cpp index 70ad8c5..5173bb8 100644 --- a/core/jni/android_view_ViewRoot.cpp +++ b/core/jni/android_view_ViewRoot.cpp @@ -80,38 +80,6 @@ static void android_view_ViewRoot_abandonGlCaches(JNIEnv* env, jobject) { SkGLCanvas::AbandonAllTextures(); } -static jintArray android_view_ViewRoot_makeInputChannel(JNIEnv* env, jobject) { - int fd[2]; - jint* arrayData = NULL; - - // Create the pipe - int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); - if (err != 0) { - fprintf(stderr, "socketpair() failed: %d\n", errno); - doThrow(env, "java/lang/RuntimeException", "Unable to create pipe"); - return NULL; - } - - // Set up the return array - jintArray array = env->NewIntArray(2); - if (env->ExceptionCheck()) { - fprintf(stderr, "Exception allocating fd array"); - goto bail; - } - - arrayData = env->GetIntArrayElements(array, 0); - arrayData[0] = fd[0]; - arrayData[1] = fd[1]; - env->ReleaseIntArrayElements(array, arrayData, 0); - - return array; - -bail: - env->DeleteLocalRef(array); - close(fd[0]); - close(fd[1]); - return NULL; -} // ---------------------------------------------------------------------------- @@ -121,9 +89,7 @@ static JNINativeMethod gMethods[] = { { "nativeShowFPS", "(Landroid/graphics/Canvas;I)V", (void*)android_view_ViewRoot_showFPS }, { "nativeAbandonGlCaches", "()V", - (void*)android_view_ViewRoot_abandonGlCaches }, - { "makeInputChannel", "()[I", - (void*)android_view_ViewRoot_makeInputChannel } + (void*)android_view_ViewRoot_abandonGlCaches } }; int register_android_view_ViewRoot(JNIEnv* env) { |