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