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