2 * Copyright (C) 2013 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #define LOG_TAG "InputQueue"
23 #include <android/input.h>
24 #include <android_runtime/AndroidRuntime.h>
25 #include <android_runtime/android_view_InputQueue.h>
26 #include <input/Input.h>
27 #include <utils/Looper.h>
28 #include <utils/TypeHelpers.h>
29 #include <ScopedLocalRef.h>
32 #include "android_os_MessageQueue.h"
33 #include "android_view_KeyEvent.h"
34 #include "android_view_MotionEvent.h"
39 jmethodID finishInputEvent;
40 } gInputQueueClassInfo;
46 InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper,
47 int dispatchReadFd, int dispatchWriteFd) :
48 mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
49 mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
50 JNIEnv* env = AndroidRuntime::getJNIEnv();
51 mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
54 InputQueue::~InputQueue() {
55 mDispatchLooper->removeMessages(mHandler);
56 JNIEnv* env = AndroidRuntime::getJNIEnv();
57 env->DeleteGlobalRef(mInputQueueWeakGlobal);
58 close(mDispatchReadFd);
59 close(mDispatchWriteFd);
62 void InputQueue::attachLooper(Looper* looper, int ident,
63 ALooper_callbackFunc callback, void* data) {
64 Mutex::Autolock _l(mLock);
65 for (size_t i = 0; i < mAppLoopers.size(); i++) {
66 if (looper == mAppLoopers[i]) {
70 mAppLoopers.push(looper);
71 looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
74 void InputQueue::detachLooper() {
75 Mutex::Autolock _l(mLock);
79 void InputQueue::detachLooperLocked() {
80 for (size_t i = 0; i < mAppLoopers.size(); i++) {
81 mAppLoopers[i]->removeFd(mDispatchReadFd);
86 bool InputQueue::hasEvents() {
87 Mutex::Autolock _l(mLock);
88 return mPendingEvents.size() > 0;
91 status_t InputQueue::getEvent(InputEvent** outEvent) {
92 Mutex::Autolock _l(mLock);
94 if (!mPendingEvents.isEmpty()) {
95 *outEvent = mPendingEvents[0];
96 mPendingEvents.removeAt(0);
99 if (mPendingEvents.isEmpty()) {
103 nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread)));
104 if (nRead < 0 && errno != EAGAIN) {
105 ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno));
110 return *outEvent != NULL ? OK : WOULD_BLOCK;
113 bool InputQueue::preDispatchEvent(InputEvent* e) {
114 if (e->getType() == AINPUT_EVENT_TYPE_KEY) {
115 KeyEvent* keyEvent = static_cast<KeyEvent*>(e);
116 if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) {
117 finishEvent(e, false);
124 void InputQueue::finishEvent(InputEvent* event, bool handled) {
125 Mutex::Autolock _l(mLock);
126 mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled));
127 if (mFinishedEvents.size() == 1) {
128 mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT));
132 void InputQueue::handleMessage(const Message& message) {
133 switch(message.what) {
134 case MSG_FINISH_INPUT:
135 JNIEnv* env = AndroidRuntime::getJNIEnv();
136 ScopedLocalRef<jobject> inputQueueObj(env, jniGetReferent(env, mInputQueueWeakGlobal));
137 if (!inputQueueObj.get()) {
138 ALOGW("InputQueue was finalized without being disposed");
145 Mutex::Autolock _l(mLock);
146 if (mFinishedEvents.isEmpty()) {
149 event = mFinishedEvents[0].getKey();
150 handled = mFinishedEvents[0].getValue();
151 mFinishedEvents.removeAt(0);
153 env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
154 reinterpret_cast<jlong>(event), handled);
155 recycleInputEvent(event);
161 void InputQueue::recycleInputEvent(InputEvent* event) {
162 mPooledInputEventFactory.recycle(event);
165 KeyEvent* InputQueue::createKeyEvent() {
166 return mPooledInputEventFactory.createKeyEvent();
169 MotionEvent* InputQueue::createMotionEvent() {
170 return mPooledInputEventFactory.createMotionEvent();
173 void InputQueue::enqueueEvent(InputEvent* event) {
174 Mutex::Autolock _l(mLock);
175 mPendingEvents.push(event);
176 if (mPendingEvents.size() == 1) {
178 int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &dummy, sizeof(dummy)));
179 if (res < 0 && errno != EAGAIN) {
180 ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
185 InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
188 ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
191 fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
192 fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
193 return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
196 static jlong nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) {
197 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
198 if (messageQueue == NULL) {
199 jniThrowRuntimeException(env, "MessageQueue is not initialized.");
202 sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
204 jniThrowRuntimeException(env, "InputQueue failed to initialize");
207 queue->incStrong(&gInputQueueClassInfo);
208 return reinterpret_cast<jlong>(queue.get());
211 static void nativeDispose(JNIEnv* env, jobject clazz, jlong ptr) {
212 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
213 queue->detachLooper();
214 queue->decStrong(&gInputQueueClassInfo);
217 static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj,
218 jboolean predispatch) {
219 InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
220 KeyEvent* event = queue->createKeyEvent();
221 status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
223 queue->recycleInputEvent(event);
224 jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
229 event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
232 queue->enqueueEvent(event);
233 return reinterpret_cast<jlong>(event);
236 static jlong nativeSendMotionEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj) {
237 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
238 MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
239 if (!originalEvent) {
240 jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer.");
243 MotionEvent* event = queue->createMotionEvent();
244 event->copyFrom(originalEvent, true /* keepHistory */);
245 queue->enqueueEvent(event);
246 return reinterpret_cast<jlong>(event);
249 static const JNINativeMethod g_methods[] = {
250 { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
251 (void*) nativeInit },
252 { "nativeDispose", "(J)V", (void*) nativeDispose },
253 { "nativeSendKeyEvent", "(JLandroid/view/KeyEvent;Z)J", (void*) nativeSendKeyEvent },
254 { "nativeSendMotionEvent", "(JLandroid/view/MotionEvent;)J", (void*) nativeSendMotionEvent },
257 static const char* const kInputQueuePathName = "android/view/InputQueue";
259 #define FIND_CLASS(var, className) \
261 var = env->FindClass(className); \
262 LOG_FATAL_IF(! var, "Unable to find class %s", className); \
265 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
267 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
268 LOG_FATAL_IF(! var, "Unable to find method" methodName); \
271 int register_android_view_InputQueue(JNIEnv* env)
274 FIND_CLASS(clazz, kInputQueuePathName);
275 GET_METHOD_ID(gInputQueueClassInfo.finishInputEvent, clazz, "finishInputEvent", "(JZ)V");
277 return AndroidRuntime::registerNativeMethods(
278 env, kInputQueuePathName,
279 g_methods, NELEM(g_methods));
282 } // namespace android