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