blob: ec56afa58795403fe37b411f3a759b4f580b75ea [file] [log] [blame]
Michael Wrighta44dd262013-04-10 21:12:00 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#define LOG_TAG "InputQueue"
18
19#include <fcntl.h>
20#include <string.h>
21#include <unistd.h>
22
23#include <android/input.h>
24#include <android_runtime/AndroidRuntime.h>
25#include <android_runtime/android_view_InputQueue.h>
26#include <androidfw/Input.h>
27#include <utils/Looper.h>
28#include <utils/TypeHelpers.h>
29#include <ScopedLocalRef.h>
30
31#include "JNIHelp.h"
32#include "android_os_MessageQueue.h"
33#include "android_view_KeyEvent.h"
34#include "android_view_MotionEvent.h"
35
36namespace android {
37
38static struct {
39 jmethodID finishInputEvent;
40} gInputQueueClassInfo;
41
42enum {
43 MSG_FINISH_INPUT = 1,
44};
45
46InputQueue::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);
52}
53
54InputQueue::~InputQueue() {
55 mDispatchLooper->removeMessages(mHandler);
56 JNIEnv* env = AndroidRuntime::getJNIEnv();
57 env->DeleteGlobalRef(mInputQueueWeakGlobal);
58 close(mDispatchReadFd);
59 close(mDispatchWriteFd);
60}
61
62void 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]) {
67 return;
68 }
69 }
70 mAppLoopers.push(looper);
71 looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
72}
73
74void InputQueue::detachLooper() {
75 Mutex::Autolock _l(mLock);
76 detachLooperLocked();
77}
78
79void InputQueue::detachLooperLocked() {
80 for (size_t i = 0; i < mAppLoopers.size(); i++) {
81 mAppLoopers[i]->removeFd(mDispatchReadFd);
82 }
83 mAppLoopers.clear();
84}
85
86bool InputQueue::hasEvents() {
87 Mutex::Autolock _l(mLock);
88 return mPendingEvents.size() > 0;
89}
90
91status_t InputQueue::getEvent(InputEvent** outEvent) {
92 Mutex::Autolock _l(mLock);
93 *outEvent = NULL;
94 if (!mPendingEvents.isEmpty()) {
95 *outEvent = mPendingEvents[0];
96 mPendingEvents.removeAt(0);
97 }
98
99 if (mPendingEvents.isEmpty()) {
100 char byteread[16];
101 ssize_t nRead;
102 do {
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));
106 }
107 } while (nRead > 0);
108 }
109
110 return *outEvent != NULL ? OK : WOULD_BLOCK;
111}
112
113bool 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);
118 return true;
119 }
120 }
121 return false;
122}
123
124void 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));
129 }
130}
131
132void 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");
139 return;
140 }
141 while (true) {
142 InputEvent* event;
143 bool handled;
144 {
145 Mutex::Autolock _l(mLock);
146 if (mFinishedEvents.isEmpty()) {
147 break;
148 }
149 event = mFinishedEvents[0].getKey();
150 handled = mFinishedEvents[0].getValue();
151 mFinishedEvents.removeAt(0);
152 }
153 env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
154 reinterpret_cast<jint>(event), handled);
155 recycleInputEvent(event);
156 }
157 break;
158 }
159}
160
161void InputQueue::recycleInputEvent(InputEvent* event) {
162 mPooledInputEventFactory.recycle(event);
163}
164
165KeyEvent* InputQueue::createKeyEvent() {
166 return mPooledInputEventFactory.createKeyEvent();
167}
168
169MotionEvent* InputQueue::createMotionEvent() {
170 return mPooledInputEventFactory.createMotionEvent();
171}
172
173void InputQueue::enqueueEvent(InputEvent* event) {
174 Mutex::Autolock _l(mLock);
175 mPendingEvents.push(event);
176 if (mPendingEvents.size() == 1) {
177 char dummy = 0;
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));
181 }
182 }
183}
184
185InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
186 int pipeFds[2];
187 if (pipe(pipeFds)) {
188 ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
189 return NULL;
190 }
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]);
194}
195
196static jint 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.");
200 return 0;
201 }
202 sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
203 if (!queue.get()) {
204 jniThrowRuntimeException(env, "InputQueue failed to initialize");
205 return 0;
206 }
207 queue->incStrong(&gInputQueueClassInfo);
208 return reinterpret_cast<jint>(queue.get());
209}
210
211static void nativeDispose(JNIEnv* env, jobject clazz, jint ptr) {
212 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
213 queue->detachLooper();
214 queue->decStrong(&gInputQueueClassInfo);
215}
216
217static jint nativeSendKeyEvent(JNIEnv* env, jobject clazz, jint 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);
222 if (status) {
223 queue->recycleInputEvent(event);
224 jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
225 return -1;
226 }
227
228 if (predispatch) {
229 event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
230 }
231
232 queue->enqueueEvent(event);
233 return reinterpret_cast<jint>(event);
234}
235
236static jint nativeSendMotionEvent(JNIEnv* env, jobject clazz, jint 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.");
241 return -1;
242 }
243 MotionEvent* event = queue->createMotionEvent();
244 event->copyFrom(originalEvent, true /* keepHistory */);
245 queue->enqueueEvent(event);
246 return reinterpret_cast<jint>(event);
247}
248
249static const JNINativeMethod g_methods[] = {
250 { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)I",
251 (void*) nativeInit },
252 { "nativeDispose", "(I)V", (void*) nativeDispose },
253 { "nativeSendKeyEvent", "(ILandroid/view/KeyEvent;Z)I", (void*) nativeSendKeyEvent },
254 { "nativeSendMotionEvent", "(ILandroid/view/MotionEvent;)I", (void*) nativeSendMotionEvent },
255};
256
257static const char* const kInputQueuePathName = "android/view/InputQueue";
258
259#define FIND_CLASS(var, className) \
260 do { \
261 var = env->FindClass(className); \
262 LOG_FATAL_IF(! var, "Unable to find class %s", className); \
263 } while(0)
264
265#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
266 do { \
267 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
268 LOG_FATAL_IF(! var, "Unable to find method" methodName); \
269 } while(0)
270
271int register_android_view_InputQueue(JNIEnv* env)
272{
273 jclass clazz;
274 FIND_CLASS(clazz, kInputQueuePathName);
275 GET_METHOD_ID(gInputQueueClassInfo.finishInputEvent, clazz, "finishInputEvent", "(IZ)V");
276
277 return AndroidRuntime::registerNativeMethods(
278 env, kInputQueuePathName,
279 g_methods, NELEM(g_methods));
280}
281
282} // namespace android