summaryrefslogtreecommitdiffstats
path: root/core/jni
diff options
context:
space:
mode:
Diffstat (limited to 'core/jni')
-rw-r--r--core/jni/Android.mk5
-rw-r--r--core/jni/AndroidRuntime.cpp10
-rw-r--r--core/jni/android_os_MessageQueue.cpp348
-rw-r--r--core/jni/android_os_MessageQueue.h30
-rw-r--r--core/jni/android_view_InputChannel.cpp288
-rw-r--r--core/jni/android_view_InputChannel.h40
-rw-r--r--core/jni/android_view_InputQueue.cpp471
-rw-r--r--core/jni/android_view_InputTarget.cpp97
-rw-r--r--core/jni/android_view_InputTarget.h31
-rw-r--r--core/jni/android_view_KeyEvent.cpp124
-rw-r--r--core/jni/android_view_KeyEvent.h35
-rw-r--r--core/jni/android_view_MotionEvent.cpp310
-rw-r--r--core/jni/android_view_MotionEvent.h38
-rw-r--r--core/jni/android_view_ViewRoot.cpp36
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) {