• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 <input/Input.h>
27 #include <utils/Looper.h>
28 #include <utils/TypeHelpers.h>
29 #include <nativehelper/ScopedLocalRef.h>
30 
31 #include <nativehelper/JNIHelp.h>
32 #include "android_os_MessageQueue.h"
33 #include "android_view_KeyEvent.h"
34 #include "android_view_MotionEvent.h"
35 
36 #include "core_jni_helpers.h"
37 
38 namespace android {
39 
40 static struct {
41     jmethodID finishInputEvent;
42 } gInputQueueClassInfo;
43 
44 enum {
45     MSG_FINISH_INPUT = 1,
46 };
47 
InputQueue(jobject inputQueueObj,const sp<Looper> & looper,int dispatchReadFd,int dispatchWriteFd)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);
54 }
55 
~InputQueue()56 InputQueue::~InputQueue() {
57     mDispatchLooper->removeMessages(mHandler);
58     JNIEnv* env = AndroidRuntime::getJNIEnv();
59     env->DeleteGlobalRef(mInputQueueWeakGlobal);
60     close(mDispatchReadFd);
61     close(mDispatchWriteFd);
62 }
63 
attachLooper(Looper * looper,int ident,ALooper_callbackFunc callback,void * data)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]) {
69             return;
70         }
71     }
72     mAppLoopers.push(looper);
73     looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
74 }
75 
detachLooper()76 void InputQueue::detachLooper() {
77     Mutex::Autolock _l(mLock);
78     detachLooperLocked();
79 }
80 
detachLooperLocked()81 void InputQueue::detachLooperLocked() {
82     for (size_t i = 0; i < mAppLoopers.size(); i++) {
83         mAppLoopers[i]->removeFd(mDispatchReadFd);
84     }
85     mAppLoopers.clear();
86 }
87 
hasEvents()88 bool InputQueue::hasEvents() {
89     Mutex::Autolock _l(mLock);
90     return mPendingEvents.size() > 0;
91 }
92 
getEvent(InputEvent ** outEvent)93 status_t InputQueue::getEvent(InputEvent** outEvent) {
94     Mutex::Autolock _l(mLock);
95     *outEvent = NULL;
96     if (!mPendingEvents.isEmpty()) {
97         *outEvent = mPendingEvents[0];
98         mPendingEvents.removeAt(0);
99     }
100 
101     if (mPendingEvents.isEmpty()) {
102         char byteread[16];
103         ssize_t nRead;
104         do {
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));
108             }
109         } while (nRead > 0);
110     }
111 
112     return *outEvent != NULL ? OK : WOULD_BLOCK;
113 }
114 
preDispatchEvent(InputEvent * e)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);
120             return true;
121         }
122     }
123     return false;
124 }
125 
finishEvent(InputEvent * event,bool handled)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));
131     }
132 }
133 
handleMessage(const Message & message)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");
141             return;
142         }
143         while (true) {
144             InputEvent* event;
145             bool handled;
146             {
147                 Mutex::Autolock _l(mLock);
148                 if (mFinishedEvents.isEmpty()) {
149                     break;
150                 }
151                 event = mFinishedEvents[0].getKey();
152                 handled = mFinishedEvents[0].getValue();
153                 mFinishedEvents.removeAt(0);
154             }
155             env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
156                     reinterpret_cast<jlong>(event), handled);
157             recycleInputEvent(event);
158         }
159         break;
160     }
161 }
162 
recycleInputEvent(InputEvent * event)163 void InputQueue::recycleInputEvent(InputEvent* event) {
164     mPooledInputEventFactory.recycle(event);
165 }
166 
createKeyEvent()167 KeyEvent* InputQueue::createKeyEvent() {
168     return mPooledInputEventFactory.createKeyEvent();
169 }
170 
createMotionEvent()171 MotionEvent* InputQueue::createMotionEvent() {
172     return mPooledInputEventFactory.createMotionEvent();
173 }
174 
enqueueEvent(InputEvent * event)175 void InputQueue::enqueueEvent(InputEvent* event) {
176     Mutex::Autolock _l(mLock);
177     mPendingEvents.push(event);
178     if (mPendingEvents.size() == 1) {
179         char payload = '\0';
180         int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &payload, sizeof(payload)));
181         if (res < 0 && errno != EAGAIN) {
182             ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
183         }
184     }
185 }
186 
createQueue(jobject inputQueueObj,const sp<Looper> & looper)187 InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
188     int pipeFds[2];
189     if (pipe(pipeFds)) {
190         ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
191         return NULL;
192     }
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]);
196 }
197 
nativeInit(JNIEnv * env,jobject clazz,jobject queueWeak,jobject jMsgQueue)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.");
202         return 0;
203     }
204     sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
205     if (!queue.get()) {
206         jniThrowRuntimeException(env, "InputQueue failed to initialize");
207         return 0;
208     }
209     queue->incStrong(&gInputQueueClassInfo);
210     return reinterpret_cast<jlong>(queue.get());
211 }
212 
nativeDispose(JNIEnv * env,jobject clazz,jlong ptr)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);
217 }
218 
nativeSendKeyEvent(JNIEnv * env,jobject clazz,jlong ptr,jobject eventObj,jboolean predispatch)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);
224     if (status) {
225         queue->recycleInputEvent(event);
226         jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
227         return -1;
228     }
229 
230     if (predispatch) {
231         event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
232     }
233 
234     queue->enqueueEvent(event);
235     return reinterpret_cast<jlong>(event);
236 }
237 
nativeSendMotionEvent(JNIEnv * env,jobject clazz,jlong ptr,jobject eventObj)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.");
243         return -1;
244     }
245     MotionEvent* event = queue->createMotionEvent();
246     event->copyFrom(originalEvent, true /* keepHistory */);
247     queue->enqueueEvent(event);
248     return reinterpret_cast<jlong>(event);
249 }
250 
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 },
257 };
258 
259 static const char* const kInputQueuePathName = "android/view/InputQueue";
260 
register_android_view_InputQueue(JNIEnv * env)261 int register_android_view_InputQueue(JNIEnv* env)
262 {
263     jclass clazz = FindClassOrDie(env, kInputQueuePathName);
264     gInputQueueClassInfo.finishInputEvent = GetMethodIDOrDie(env, clazz, "finishInputEvent",
265                                                              "(JZ)V");
266 
267     return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods));
268 }
269 
270 } // namespace android
271