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
38namespace android {
39
40static struct {
41    jmethodID finishInputEvent;
42} gInputQueueClassInfo;
43
44enum {
45    MSG_FINISH_INPUT = 1,
46};
47
48InputQueue::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
56InputQueue::~InputQueue() {
57    mDispatchLooper->removeMessages(mHandler);
58    JNIEnv* env = AndroidRuntime::getJNIEnv();
59    env->DeleteGlobalRef(mInputQueueWeakGlobal);
60    close(mDispatchReadFd);
61    close(mDispatchWriteFd);
62}
63
64void 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
76void InputQueue::detachLooper() {
77    Mutex::Autolock _l(mLock);
78    detachLooperLocked();
79}
80
81void InputQueue::detachLooperLocked() {
82    for (size_t i = 0; i < mAppLoopers.size(); i++) {
83        mAppLoopers[i]->removeFd(mDispatchReadFd);
84    }
85    mAppLoopers.clear();
86}
87
88bool InputQueue::hasEvents() {
89    Mutex::Autolock _l(mLock);
90    return mPendingEvents.size() > 0;
91}
92
93status_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
115bool 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
126void 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
134void 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
163void InputQueue::recycleInputEvent(InputEvent* event) {
164    mPooledInputEventFactory.recycle(event);
165}
166
167KeyEvent* InputQueue::createKeyEvent() {
168    return mPooledInputEventFactory.createKeyEvent();
169}
170
171MotionEvent* InputQueue::createMotionEvent() {
172    return mPooledInputEventFactory.createMotionEvent();
173}
174
175void InputQueue::enqueueEvent(InputEvent* event) {
176    Mutex::Autolock _l(mLock);
177    mPendingEvents.push(event);
178    if (mPendingEvents.size() == 1) {
179        char dummy = 0;
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));
183        }
184    }
185}
186
187InputQueue* 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
198static 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
213static 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
219static 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
238static 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
251static 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
259static const char* const kInputQueuePathName = "android/view/InputQueue";
260
261int 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