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     jmethodID getNativePtr;
43 } gInputQueueClassInfo;
44 
45 enum {
46     MSG_FINISH_INPUT = 1,
47 };
48 
InputQueue(jobject inputQueueObj,const sp<Looper> & looper,int dispatchReadFd,int dispatchWriteFd)49 InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper,
50         int dispatchReadFd, int dispatchWriteFd) :
51         mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
52         mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
53     JNIEnv* env = AndroidRuntime::getJNIEnv();
54     mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
55 }
56 
~InputQueue()57 InputQueue::~InputQueue() {
58     mDispatchLooper->removeMessages(mHandler);
59     JNIEnv* env = AndroidRuntime::getJNIEnv();
60     env->DeleteGlobalRef(mInputQueueWeakGlobal);
61     close(mDispatchReadFd);
62     close(mDispatchWriteFd);
63 }
64 
attachLooper(Looper * looper,int ident,ALooper_callbackFunc callback,void * data)65 void InputQueue::attachLooper(Looper* looper, int ident,
66         ALooper_callbackFunc callback, void* data) {
67     Mutex::Autolock _l(mLock);
68     for (size_t i = 0; i < mAppLoopers.size(); i++) {
69         if (looper == mAppLoopers[i]) {
70             return;
71         }
72     }
73     mAppLoopers.push(looper);
74     looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
75 }
76 
detachLooper()77 void InputQueue::detachLooper() {
78     Mutex::Autolock _l(mLock);
79     detachLooperLocked();
80 }
81 
detachLooperLocked()82 void InputQueue::detachLooperLocked() {
83     for (size_t i = 0; i < mAppLoopers.size(); i++) {
84         mAppLoopers[i]->removeFd(mDispatchReadFd);
85     }
86     mAppLoopers.clear();
87 }
88 
hasEvents()89 bool InputQueue::hasEvents() {
90     Mutex::Autolock _l(mLock);
91     return mPendingEvents.size() > 0;
92 }
93 
getEvent(InputEvent ** outEvent)94 status_t InputQueue::getEvent(InputEvent** outEvent) {
95     Mutex::Autolock _l(mLock);
96     *outEvent = NULL;
97     if (!mPendingEvents.isEmpty()) {
98         *outEvent = mPendingEvents[0];
99         mPendingEvents.removeAt(0);
100     }
101 
102     if (mPendingEvents.isEmpty()) {
103         char byteread[16];
104         ssize_t nRead;
105         do {
106             nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread)));
107             if (nRead < 0 && errno != EAGAIN) {
108                 ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno));
109             }
110         } while (nRead > 0);
111     }
112 
113     return *outEvent != NULL ? OK : WOULD_BLOCK;
114 }
115 
preDispatchEvent(InputEvent * e)116 bool InputQueue::preDispatchEvent(InputEvent* e) {
117     if (e->getType() == InputEventType::KEY) {
118         KeyEvent* keyEvent = static_cast<KeyEvent*>(e);
119         if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) {
120             finishEvent(e, false);
121             return true;
122         }
123     }
124     return false;
125 }
126 
finishEvent(InputEvent * event,bool handled)127 void InputQueue::finishEvent(InputEvent* event, bool handled) {
128     Mutex::Autolock _l(mLock);
129     mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled));
130     if (mFinishedEvents.size() == 1) {
131         mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT));
132     }
133 }
134 
handleMessage(const Message & message)135 void InputQueue::handleMessage(const Message& message) {
136     switch(message.what) {
137     case MSG_FINISH_INPUT:
138         JNIEnv* env = AndroidRuntime::getJNIEnv();
139         ScopedLocalRef<jobject> inputQueueObj(env, GetReferent(env, mInputQueueWeakGlobal));
140         if (!inputQueueObj.get()) {
141             ALOGW("InputQueue was finalized without being disposed");
142             return;
143         }
144         while (true) {
145             InputEvent* event;
146             bool handled;
147             {
148                 Mutex::Autolock _l(mLock);
149                 if (mFinishedEvents.isEmpty()) {
150                     break;
151                 }
152                 event = mFinishedEvents[0].getKey();
153                 handled = mFinishedEvents[0].getValue();
154                 mFinishedEvents.removeAt(0);
155             }
156             env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent,
157                     reinterpret_cast<jlong>(event), handled);
158             recycleInputEvent(event);
159         }
160         break;
161     }
162 }
163 
recycleInputEvent(InputEvent * event)164 void InputQueue::recycleInputEvent(InputEvent* event) {
165     mPooledInputEventFactory.recycle(event);
166 }
167 
createKeyEvent()168 KeyEvent* InputQueue::createKeyEvent() {
169     return mPooledInputEventFactory.createKeyEvent();
170 }
171 
createMotionEvent()172 MotionEvent* InputQueue::createMotionEvent() {
173     return mPooledInputEventFactory.createMotionEvent();
174 }
175 
enqueueEvent(InputEvent * event)176 void InputQueue::enqueueEvent(InputEvent* event) {
177     Mutex::Autolock _l(mLock);
178     mPendingEvents.push(event);
179     if (mPendingEvents.size() == 1) {
180         char payload = '\0';
181         int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &payload, sizeof(payload)));
182         if (res < 0 && errno != EAGAIN) {
183             ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
184         }
185     }
186 }
187 
createQueue(jobject inputQueueObj,const sp<Looper> & looper)188 InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) {
189     int pipeFds[2];
190     if (pipe(pipeFds)) {
191         ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
192         return NULL;
193     }
194     fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
195     fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
196     return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
197 }
198 
nativeInit(JNIEnv * env,jobject clazz,jobject queueWeak,jobject jMsgQueue)199 static jlong nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) {
200     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
201     if (messageQueue == NULL) {
202         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
203         return 0;
204     }
205     sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
206     if (!queue.get()) {
207         jniThrowRuntimeException(env, "InputQueue failed to initialize");
208         return 0;
209     }
210     queue->incStrong(&gInputQueueClassInfo);
211     return reinterpret_cast<jlong>(queue.get());
212 }
213 
nativeDispose(JNIEnv * env,jobject clazz,jlong ptr)214 static void nativeDispose(JNIEnv* env, jobject clazz, jlong ptr) {
215     sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
216     queue->detachLooper();
217     queue->decStrong(&gInputQueueClassInfo);
218 }
219 
nativeSendKeyEvent(JNIEnv * env,jobject clazz,jlong ptr,jobject eventObj,jboolean predispatch)220 static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj,
221         jboolean predispatch) {
222     InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
223     KeyEvent* event = queue->createKeyEvent();
224     *event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
225 
226     if (predispatch) {
227         event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
228     }
229 
230     queue->enqueueEvent(event);
231     return reinterpret_cast<jlong>(event);
232 }
233 
nativeSendMotionEvent(JNIEnv * env,jobject clazz,jlong ptr,jobject eventObj)234 static jlong nativeSendMotionEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj) {
235     sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
236     MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
237     if (!originalEvent) {
238         jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer.");
239         return -1;
240     }
241     MotionEvent* event = queue->createMotionEvent();
242     event->copyFrom(originalEvent, /*keepHistory=*/true);
243     queue->enqueueEvent(event);
244     return reinterpret_cast<jlong>(event);
245 }
246 
247 static const JNINativeMethod g_methods[] = {
248     { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
249         (void*) nativeInit },
250     { "nativeDispose", "(J)V", (void*) nativeDispose },
251     { "nativeSendKeyEvent", "(JLandroid/view/KeyEvent;Z)J", (void*) nativeSendKeyEvent },
252     { "nativeSendMotionEvent", "(JLandroid/view/MotionEvent;)J", (void*) nativeSendMotionEvent },
253 };
254 
255 static const char* const kInputQueuePathName = "android/view/InputQueue";
256 
register_android_view_InputQueue(JNIEnv * env)257 int register_android_view_InputQueue(JNIEnv* env)
258 {
259     jclass clazz = FindClassOrDie(env, kInputQueuePathName);
260     gInputQueueClassInfo.finishInputEvent = GetMethodIDOrDie(env, clazz, "finishInputEvent",
261                                                              "(JZ)V");
262     gInputQueueClassInfo.getNativePtr = GetMethodIDOrDie(env, clazz, "getNativePtr", "()J");
263 
264     return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods));
265 }
266 
android_view_InputQueue_getNativePtr(JNIEnv * env,jobject inputQueue)267 AInputQueue* android_view_InputQueue_getNativePtr(JNIEnv* env, jobject inputQueue) {
268     jlong ptr = env->CallLongMethod(inputQueue, gInputQueueClassInfo.getNativePtr);
269     return reinterpret_cast<AInputQueue*>(ptr);
270 }
271 
272 } // namespace android
273