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