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