• 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 <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 
36 namespace android {
37 
38 static struct {
39     jmethodID finishInputEvent;
40 } gInputQueueClassInfo;
41 
42 enum {
43     MSG_FINISH_INPUT = 1,
44 };
45 
InputQueue(jobject inputQueueObj,const sp<Looper> & looper,int dispatchReadFd,int dispatchWriteFd)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);
52 }
53 
~InputQueue()54 InputQueue::~InputQueue() {
55     mDispatchLooper->removeMessages(mHandler);
56     JNIEnv* env = AndroidRuntime::getJNIEnv();
57     env->DeleteGlobalRef(mInputQueueWeakGlobal);
58     close(mDispatchReadFd);
59     close(mDispatchWriteFd);
60 }
61 
attachLooper(Looper * looper,int ident,ALooper_callbackFunc callback,void * data)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]) {
67             return;
68         }
69     }
70     mAppLoopers.push(looper);
71     looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data);
72 }
73 
detachLooper()74 void InputQueue::detachLooper() {
75     Mutex::Autolock _l(mLock);
76     detachLooperLocked();
77 }
78 
detachLooperLocked()79 void InputQueue::detachLooperLocked() {
80     for (size_t i = 0; i < mAppLoopers.size(); i++) {
81         mAppLoopers[i]->removeFd(mDispatchReadFd);
82     }
83     mAppLoopers.clear();
84 }
85 
hasEvents()86 bool InputQueue::hasEvents() {
87     Mutex::Autolock _l(mLock);
88     return mPendingEvents.size() > 0;
89 }
90 
getEvent(InputEvent ** outEvent)91 status_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 
preDispatchEvent(InputEvent * e)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);
118             return true;
119         }
120     }
121     return false;
122 }
123 
finishEvent(InputEvent * event,bool handled)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));
129     }
130 }
131 
handleMessage(const Message & message)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");
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 
recycleInputEvent(InputEvent * event)161 void InputQueue::recycleInputEvent(InputEvent* event) {
162     mPooledInputEventFactory.recycle(event);
163 }
164 
createKeyEvent()165 KeyEvent* InputQueue::createKeyEvent() {
166     return mPooledInputEventFactory.createKeyEvent();
167 }
168 
createMotionEvent()169 MotionEvent* InputQueue::createMotionEvent() {
170     return mPooledInputEventFactory.createMotionEvent();
171 }
172 
enqueueEvent(InputEvent * event)173 void 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 
createQueue(jobject inputQueueObj,const sp<Looper> & looper)185 InputQueue* 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 
nativeInit(JNIEnv * env,jobject clazz,jobject queueWeak,jobject jMsgQueue)196 static 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 
nativeDispose(JNIEnv * env,jobject clazz,jint ptr)211 static 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 
nativeSendKeyEvent(JNIEnv * env,jobject clazz,jint ptr,jobject eventObj,jboolean predispatch)217 static 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 
nativeSendMotionEvent(JNIEnv * env,jobject clazz,jint ptr,jobject eventObj)236 static 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 
249 static 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 
257 static 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 
register_android_view_InputQueue(JNIEnv * env)271 int 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
283