1/*
2 * Copyright (C) 2011 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 "InputEventReceiver"
18
19//#define LOG_NDEBUG 0
20
21#include <inttypes.h>
22
23#include <nativehelper/JNIHelp.h>
24
25#include <android_runtime/AndroidRuntime.h>
26#include <utils/Log.h>
27#include <utils/Looper.h>
28#include <utils/Vector.h>
29#include <utils/threads.h>
30#include <input/InputTransport.h>
31#include "android_os_MessageQueue.h"
32#include "android_view_InputChannel.h"
33#include "android_view_KeyEvent.h"
34#include "android_view_MotionEvent.h"
35
36#include <nativehelper/ScopedLocalRef.h>
37
38#include "core_jni_helpers.h"
39
40namespace android {
41
42static const bool kDebugDispatchCycle = false;
43
44static struct {
45    jclass clazz;
46
47    jmethodID dispatchInputEvent;
48    jmethodID dispatchBatchedInputEventPending;
49} gInputEventReceiverClassInfo;
50
51
52class NativeInputEventReceiver : public LooperCallback {
53public:
54    NativeInputEventReceiver(JNIEnv* env,
55            jobject receiverWeak, const sp<InputChannel>& inputChannel,
56            const sp<MessageQueue>& messageQueue);
57
58    status_t initialize();
59    void dispose();
60    status_t finishInputEvent(uint32_t seq, bool handled);
61    status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
62            bool* outConsumedBatch);
63
64protected:
65    virtual ~NativeInputEventReceiver();
66
67private:
68    struct Finish {
69        uint32_t seq;
70        bool handled;
71    };
72
73    jobject mReceiverWeakGlobal;
74    InputConsumer mInputConsumer;
75    sp<MessageQueue> mMessageQueue;
76    PreallocatedInputEventFactory mInputEventFactory;
77    bool mBatchedInputEventPending;
78    int mFdEvents;
79    Vector<Finish> mFinishQueue;
80
81    void setFdEvents(int events);
82
83    const std::string getInputChannelName() {
84        return mInputConsumer.getChannel()->getName();
85    }
86
87    virtual int handleEvent(int receiveFd, int events, void* data);
88};
89
90
91NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
92        jobject receiverWeak, const sp<InputChannel>& inputChannel,
93        const sp<MessageQueue>& messageQueue) :
94        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
95        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
96        mBatchedInputEventPending(false), mFdEvents(0) {
97    if (kDebugDispatchCycle) {
98        ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
99    }
100}
101
102NativeInputEventReceiver::~NativeInputEventReceiver() {
103    JNIEnv* env = AndroidRuntime::getJNIEnv();
104    env->DeleteGlobalRef(mReceiverWeakGlobal);
105}
106
107status_t NativeInputEventReceiver::initialize() {
108    setFdEvents(ALOOPER_EVENT_INPUT);
109    return OK;
110}
111
112void NativeInputEventReceiver::dispose() {
113    if (kDebugDispatchCycle) {
114        ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName().c_str());
115    }
116
117    setFdEvents(0);
118}
119
120status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
121    if (kDebugDispatchCycle) {
122        ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
123    }
124
125    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
126    if (status) {
127        if (status == WOULD_BLOCK) {
128            if (kDebugDispatchCycle) {
129                ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
130                        "Enqueued for later.", getInputChannelName().c_str());
131            }
132            Finish finish;
133            finish.seq = seq;
134            finish.handled = handled;
135            mFinishQueue.add(finish);
136            if (mFinishQueue.size() == 1) {
137                setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
138            }
139            return OK;
140        }
141        ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
142                getInputChannelName().c_str(), status);
143    }
144    return status;
145}
146
147void NativeInputEventReceiver::setFdEvents(int events) {
148    if (mFdEvents != events) {
149        mFdEvents = events;
150        int fd = mInputConsumer.getChannel()->getFd();
151        if (events) {
152            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
153        } else {
154            mMessageQueue->getLooper()->removeFd(fd);
155        }
156    }
157}
158
159int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
160    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
161        // This error typically occurs when the publisher has closed the input channel
162        // as part of removing a window or finishing an IME session, in which case
163        // the consumer will soon be disposed as well.
164        if (kDebugDispatchCycle) {
165            ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
166                    "events=0x%x", getInputChannelName().c_str(), events);
167        }
168        return 0; // remove the callback
169    }
170
171    if (events & ALOOPER_EVENT_INPUT) {
172        JNIEnv* env = AndroidRuntime::getJNIEnv();
173        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
174        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
175        return status == OK || status == NO_MEMORY ? 1 : 0;
176    }
177
178    if (events & ALOOPER_EVENT_OUTPUT) {
179        for (size_t i = 0; i < mFinishQueue.size(); i++) {
180            const Finish& finish = mFinishQueue.itemAt(i);
181            status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
182            if (status) {
183                mFinishQueue.removeItemsAt(0, i);
184
185                if (status == WOULD_BLOCK) {
186                    if (kDebugDispatchCycle) {
187                        ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
188                                getInputChannelName().c_str(), i, mFinishQueue.size());
189                    }
190                    return 1; // keep the callback, try again later
191                }
192
193                ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
194                        getInputChannelName().c_str(), status);
195                if (status != DEAD_OBJECT) {
196                    JNIEnv* env = AndroidRuntime::getJNIEnv();
197                    String8 message;
198                    message.appendFormat("Failed to finish input event.  status=%d", status);
199                    jniThrowRuntimeException(env, message.string());
200                    mMessageQueue->raiseAndClearException(env, "finishInputEvent");
201                }
202                return 0; // remove the callback
203            }
204        }
205        if (kDebugDispatchCycle) {
206            ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
207                    getInputChannelName().c_str(), mFinishQueue.size());
208        }
209        mFinishQueue.clear();
210        setFdEvents(ALOOPER_EVENT_INPUT);
211        return 1;
212    }
213
214    ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
215            "events=0x%x", getInputChannelName().c_str(), events);
216    return 1;
217}
218
219status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
220        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
221    if (kDebugDispatchCycle) {
222        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
223                getInputChannelName().c_str(),
224                consumeBatches ? "true" : "false", frameTime);
225    }
226
227    if (consumeBatches) {
228        mBatchedInputEventPending = false;
229    }
230    if (outConsumedBatch) {
231        *outConsumedBatch = false;
232    }
233
234    ScopedLocalRef<jobject> receiverObj(env, NULL);
235    bool skipCallbacks = false;
236    for (;;) {
237        uint32_t seq;
238        InputEvent* inputEvent;
239        int32_t displayId;
240        status_t status = mInputConsumer.consume(&mInputEventFactory,
241                consumeBatches, frameTime, &seq, &inputEvent, &displayId);
242        if (status) {
243            if (status == WOULD_BLOCK) {
244                if (!skipCallbacks && !mBatchedInputEventPending
245                        && mInputConsumer.hasPendingBatch()) {
246                    // There is a pending batch.  Come back later.
247                    if (!receiverObj.get()) {
248                        receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
249                        if (!receiverObj.get()) {
250                            ALOGW("channel '%s' ~ Receiver object was finalized "
251                                    "without being disposed.", getInputChannelName().c_str());
252                            return DEAD_OBJECT;
253                        }
254                    }
255
256                    mBatchedInputEventPending = true;
257                    if (kDebugDispatchCycle) {
258                        ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
259                                getInputChannelName().c_str());
260                    }
261                    env->CallVoidMethod(receiverObj.get(),
262                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
263                    if (env->ExceptionCheck()) {
264                        ALOGE("Exception dispatching batched input events.");
265                        mBatchedInputEventPending = false; // try again later
266                    }
267                }
268                return OK;
269            }
270            ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
271                    getInputChannelName().c_str(), status);
272            return status;
273        }
274        assert(inputEvent);
275
276        if (!skipCallbacks) {
277            if (!receiverObj.get()) {
278                receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
279                if (!receiverObj.get()) {
280                    ALOGW("channel '%s' ~ Receiver object was finalized "
281                            "without being disposed.", getInputChannelName().c_str());
282                    return DEAD_OBJECT;
283                }
284            }
285
286            jobject inputEventObj;
287            switch (inputEvent->getType()) {
288            case AINPUT_EVENT_TYPE_KEY:
289                if (kDebugDispatchCycle) {
290                    ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
291                }
292                inputEventObj = android_view_KeyEvent_fromNative(env,
293                        static_cast<KeyEvent*>(inputEvent));
294                break;
295
296            case AINPUT_EVENT_TYPE_MOTION: {
297                if (kDebugDispatchCycle) {
298                    ALOGD("channel '%s' ~ Received motion event.", getInputChannelName().c_str());
299                }
300                MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
301                if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
302                    *outConsumedBatch = true;
303                }
304                inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
305                break;
306            }
307
308            default:
309                assert(false); // InputConsumer should prevent this from ever happening
310                inputEventObj = NULL;
311            }
312
313            if (inputEventObj) {
314                if (kDebugDispatchCycle) {
315                    ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
316                }
317                env->CallVoidMethod(receiverObj.get(),
318                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
319                        displayId);
320                if (env->ExceptionCheck()) {
321                    ALOGE("Exception dispatching input event.");
322                    skipCallbacks = true;
323                }
324                env->DeleteLocalRef(inputEventObj);
325            } else {
326                ALOGW("channel '%s' ~ Failed to obtain event object.",
327                        getInputChannelName().c_str());
328                skipCallbacks = true;
329            }
330        }
331
332        if (skipCallbacks) {
333            mInputConsumer.sendFinishedSignal(seq, false);
334        }
335    }
336}
337
338
339static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
340        jobject inputChannelObj, jobject messageQueueObj) {
341    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
342            inputChannelObj);
343    if (inputChannel == NULL) {
344        jniThrowRuntimeException(env, "InputChannel is not initialized.");
345        return 0;
346    }
347
348    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
349    if (messageQueue == NULL) {
350        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
351        return 0;
352    }
353
354    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
355            receiverWeak, inputChannel, messageQueue);
356    status_t status = receiver->initialize();
357    if (status) {
358        String8 message;
359        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
360        jniThrowRuntimeException(env, message.string());
361        return 0;
362    }
363
364    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
365    return reinterpret_cast<jlong>(receiver.get());
366}
367
368static void nativeDispose(JNIEnv* env, jclass clazz, jlong receiverPtr) {
369    sp<NativeInputEventReceiver> receiver =
370            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
371    receiver->dispose();
372    receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
373}
374
375static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
376        jint seq, jboolean handled) {
377    sp<NativeInputEventReceiver> receiver =
378            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
379    status_t status = receiver->finishInputEvent(seq, handled);
380    if (status && status != DEAD_OBJECT) {
381        String8 message;
382        message.appendFormat("Failed to finish input event.  status=%d", status);
383        jniThrowRuntimeException(env, message.string());
384    }
385}
386
387static jboolean nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jlong receiverPtr,
388        jlong frameTimeNanos) {
389    sp<NativeInputEventReceiver> receiver =
390            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
391    bool consumedBatch;
392    status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
393            &consumedBatch);
394    if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
395        String8 message;
396        message.appendFormat("Failed to consume batched input event.  status=%d", status);
397        jniThrowRuntimeException(env, message.string());
398        return JNI_FALSE;
399    }
400    return consumedBatch ? JNI_TRUE : JNI_FALSE;
401}
402
403
404static const JNINativeMethod gMethods[] = {
405    /* name, signature, funcPtr */
406    { "nativeInit",
407            "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
408            (void*)nativeInit },
409    { "nativeDispose", "(J)V",
410            (void*)nativeDispose },
411    { "nativeFinishInputEvent", "(JIZ)V",
412            (void*)nativeFinishInputEvent },
413    { "nativeConsumeBatchedInputEvents", "(JJ)Z",
414            (void*)nativeConsumeBatchedInputEvents },
415};
416
417int register_android_view_InputEventReceiver(JNIEnv* env) {
418    int res = RegisterMethodsOrDie(env, "android/view/InputEventReceiver",
419            gMethods, NELEM(gMethods));
420
421    jclass clazz = FindClassOrDie(env, "android/view/InputEventReceiver");
422    gInputEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
423
424    gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
425            gInputEventReceiverClassInfo.clazz,
426            "dispatchInputEvent", "(ILandroid/view/InputEvent;I)V");
427    gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
428            gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
429
430    return res;
431}
432
433} // namespace android
434