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"
36 #include "core_jni_helpers.h"
41 jmethodID finishInputEvent;
42 } gInputQueueClassInfo;
48 InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper,
49 int dispatchReadFd, int dispatchWriteFd) :
50 mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
51 mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
52 JNIEnv* env = AndroidRuntime::getJNIEnv();
53 mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
56 InputQueue::~InputQueue() {
57 mDispatchLooper->removeMessages(mHandler);
58 JNIEnv* env = AndroidRuntime::getJNIEnv();
59 env->DeleteGlobalRef(mInputQueueWeakGlobal);
60 close(mDispatchReadFd);
61 close(mDispatchWriteFd);
64 void InputQueue::attachLooper(Looper* looper, int ident,
65 ALooper_callbackFunc callback, void* data) {
66 Mutex::Autolock _l(mLock);
67 for (size_t i = 0; i < mAppLoopers.size(); i++) {
68 if (looper == mAppLoopers[i]) {
72 mAppLoopers.push(looper);
73 looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
76 void InputQueue::detachLooper() {
77 Mutex::Autolock _l(mLock);
81 void InputQueue::detachLooperLocked() {
82 for (size_t i = 0; i < mAppLoopers.size(); i++) {
83 mAppLoopers[i]->removeFd(mDispatchReadFd);
88 bool InputQueue::hasEvents() {
89 Mutex::Autolock _l(mLock);
90 return mPendingEvents.size() > 0;
93 status_t InputQueue::getEvent(InputEvent** outEvent) {
94 Mutex::Autolock _l(mLock);
96 if (!mPendingEvents.isEmpty()) {
97 *outEvent = mPendingEvents[0];
98 mPendingEvents.removeAt(0);
101 if (mPendingEvents.isEmpty()) {
105 nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread)));
106 if (nRead < 0 && errno != EAGAIN) {
107 ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno));
112 return *outEvent != NULL ? OK : WOULD_BLOCK;
115 bool InputQueue::preDispatchEvent(InputEvent* e) {
116 if (e->getType() == AINPUT_EVENT_TYPE_KEY) {
117 KeyEvent* keyEvent = static_cast<KeyEvent*>(e);
118 if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) {
119 finishEvent(e, false);
126 void InputQueue::finishEvent(InputEvent* event, bool handled) {
127 Mutex::Autolock _l(mLock);
128 mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled));
129 if (mFinishedEvents.size() == 1) {
130 mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT));
134 void InputQueue::handleMessage(const Message& message) {
135 switch(message.what) {
136 case MSG_FINISH_INPUT:
137 JNIEnv* env = AndroidRuntime::getJNIEnv();
138 ScopedLocalRef<jobject> inputQueueObj(env, jniGetReferent(env, mInputQueueWeakGlobal));
139 if (!inputQueueObj.get()) {
140 ALOGW("InputQueue was finalized without being disposed");
147 Mutex::Autolock _l(mLock);
148 if (mFinishedEvents.isEmpty()) {
151 event = mFinishedEvents[0].getKey();
152 handled = mFinishedEvents[0].getValue();
153 mFinishedEvents.removeAt(0);
155 env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
156 reinterpret_cast<jlong>(event), handled);
157 recycleInputEvent(event);
163 void InputQueue::recycleInputEvent(InputEvent* event) {
164 mPooledInputEventFactory.recycle(event);
167 KeyEvent* InputQueue::createKeyEvent() {
168 return mPooledInputEventFactory.createKeyEvent();
171 MotionEvent* InputQueue::createMotionEvent() {
172 return mPooledInputEventFactory.createMotionEvent();
175 void InputQueue::enqueueEvent(InputEvent* event) {
176 Mutex::Autolock _l(mLock);
177 mPendingEvents.push(event);
178 if (mPendingEvents.size() == 1) {
180 int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &dummy, sizeof(dummy)));
181 if (res < 0 && errno != EAGAIN) {
182 ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
187 InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
190 ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
193 fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
194 fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
195 return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
198 static jlong nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) {
199 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
200 if (messageQueue == NULL) {
201 jniThrowRuntimeException(env, "MessageQueue is not initialized.");
204 sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
206 jniThrowRuntimeException(env, "InputQueue failed to initialize");
209 queue->incStrong(&gInputQueueClassInfo);
210 return reinterpret_cast<jlong>(queue.get());
213 static void nativeDispose(JNIEnv* env, jobject clazz, jlong ptr) {
214 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
215 queue->detachLooper();
216 queue->decStrong(&gInputQueueClassInfo);
219 static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj,
220 jboolean predispatch) {
221 InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
222 KeyEvent* event = queue->createKeyEvent();
223 status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
225 queue->recycleInputEvent(event);
226 jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
231 event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
234 queue->enqueueEvent(event);
235 return reinterpret_cast<jlong>(event);
238 static jlong nativeSendMotionEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj) {
239 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
240 MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
241 if (!originalEvent) {
242 jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer.");
245 MotionEvent* event = queue->createMotionEvent();
246 event->copyFrom(originalEvent, true /* keepHistory */);
247 queue->enqueueEvent(event);
248 return reinterpret_cast<jlong>(event);
251 static const JNINativeMethod g_methods[] = {
252 { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
253 (void*) nativeInit },
254 { "nativeDispose", "(J)V", (void*) nativeDispose },
255 { "nativeSendKeyEvent", "(JLandroid/view/KeyEvent;Z)J", (void*) nativeSendKeyEvent },
256 { "nativeSendMotionEvent", "(JLandroid/view/MotionEvent;)J", (void*) nativeSendMotionEvent },
259 static const char* const kInputQueuePathName = "android/view/InputQueue";
261 int register_android_view_InputQueue(JNIEnv* env)
263 jclass clazz = FindClassOrDie(env, kInputQueuePathName);
264 gInputQueueClassInfo.finishInputEvent = GetMethodIDOrDie(env, clazz, "finishInputEvent",
267 return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods));
270 } // namespace android