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