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