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
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<jlong>(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 jlong 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<jlong>(queue.get());
209}
210
211static void nativeDispose(JNIEnv* env, jobject clazz, jlong ptr) {
212    sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr);
213    queue->detachLooper();
214    queue->decStrong(&gInputQueueClassInfo);
215}
216
217static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong 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<jlong>(event);
234}
235
236static jlong nativeSendMotionEvent(JNIEnv* env, jobject clazz, jlong 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<jlong>(event);
247}
248
249static const JNINativeMethod g_methods[] = {
250    { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J",
251        (void*) nativeInit },
252    { "nativeDispose", "(J)V", (void*) nativeDispose },
253    { "nativeSendKeyEvent", "(JLandroid/view/KeyEvent;Z)J", (void*) nativeSendKeyEvent },
254    { "nativeSendMotionEvent", "(JLandroid/view/MotionEvent;)J", (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", "(JZ)V");
276
277    return AndroidRuntime::registerNativeMethods(
278        env, kInputQueuePathName,
279        g_methods, NELEM(g_methods));
280}
281
282} // namespace android
283