OSDN Git Service

am 336fcac3: Merge "Fix bug #8194572 Chrome SIGBUS\'es on launch around TextLayoutCac...
[android-x86/frameworks-base.git] / core / jni / android_view_InputEventReceiver.cpp
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 namespace android {
38
39 static struct {
40     jclass clazz;
41
42     jmethodID dispatchInputEvent;
43     jmethodID dispatchBatchedInputEventPending;
44 } gInputEventReceiverClassInfo;
45
46
47 class NativeInputEventReceiver : public LooperCallback {
48 public:
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
58 protected:
59     virtual ~NativeInputEventReceiver();
60
61 private:
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
76 NativeInputEventReceiver::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
87 NativeInputEventReceiver::~NativeInputEventReceiver() {
88     JNIEnv* env = AndroidRuntime::getJNIEnv();
89     env->DeleteGlobalRef(mReceiverObjGlobal);
90 }
91
92 status_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
98 void 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
106 status_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
119 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
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", 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", getInputChannelName(), events);
129         return 1;
130     }
131
132     JNIEnv* env = AndroidRuntime::getJNIEnv();
133     status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
134     mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
135     return status == OK || status == NO_MEMORY ? 1 : 0;
136 }
137
138 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
139         bool consumeBatches, nsecs_t frameTime) {
140 #if DEBUG_DISPATCH_CYCLE
141     ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.",
142             getInputChannelName(), consumeBatches ? "true" : "false", frameTime);
143 #endif
144
145     if (consumeBatches) {
146         mBatchedInputEventPending = false;
147     }
148
149     bool skipCallbacks = false;
150     for (;;) {
151         uint32_t seq;
152         InputEvent* inputEvent;
153         status_t status = mInputConsumer.consume(&mInputEventFactory,
154                 consumeBatches, frameTime, &seq, &inputEvent);
155         if (status) {
156             if (status == WOULD_BLOCK) {
157                 if (!skipCallbacks && !mBatchedInputEventPending
158                         && mInputConsumer.hasPendingBatch()) {
159                     // There is a pending batch.  Come back later.
160                     mBatchedInputEventPending = true;
161 #if DEBUG_DISPATCH_CYCLE
162                     ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
163                             getInputChannelName());
164 #endif
165                     env->CallVoidMethod(mReceiverObjGlobal,
166                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
167                     if (env->ExceptionCheck()) {
168                         ALOGE("Exception dispatching batched input events.");
169                         mBatchedInputEventPending = false; // try again later
170                     }
171                 }
172                 return OK;
173             }
174             ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
175                     getInputChannelName(), status);
176             return status;
177         }
178         assert(inputEvent);
179
180         if (!skipCallbacks) {
181             jobject inputEventObj;
182             switch (inputEvent->getType()) {
183             case AINPUT_EVENT_TYPE_KEY:
184 #if DEBUG_DISPATCH_CYCLE
185                 ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
186 #endif
187                 inputEventObj = android_view_KeyEvent_fromNative(env,
188                         static_cast<KeyEvent*>(inputEvent));
189                 break;
190
191             case AINPUT_EVENT_TYPE_MOTION:
192 #if DEBUG_DISPATCH_CYCLE
193                 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
194 #endif
195                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
196                         static_cast<MotionEvent*>(inputEvent));
197                 break;
198
199             default:
200                 assert(false); // InputConsumer should prevent this from ever happening
201                 inputEventObj = NULL;
202             }
203
204             if (inputEventObj) {
205 #if DEBUG_DISPATCH_CYCLE
206                 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
207 #endif
208                 env->CallVoidMethod(mReceiverObjGlobal,
209                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
210                 if (env->ExceptionCheck()) {
211                     ALOGE("Exception dispatching input event.");
212                     skipCallbacks = true;
213                 }
214             } else {
215                 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
216                 skipCallbacks = true;
217             }
218         }
219
220         if (skipCallbacks) {
221             mInputConsumer.sendFinishedSignal(seq, false);
222         }
223     }
224 }
225
226
227 static 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<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
237     if (messageQueue == NULL) {
238         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
239         return 0;
240     }
241
242     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
243             receiverObj, inputChannel, messageQueue);
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
256 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
257     sp<NativeInputEventReceiver> receiver =
258             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
259     receiver->dispose();
260     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
261 }
262
263 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
264         jint seq, jboolean handled) {
265     sp<NativeInputEventReceiver> receiver =
266             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
267     status_t status = receiver->finishInputEvent(seq, handled);
268     if (status && status != DEAD_OBJECT) {
269         String8 message;
270         message.appendFormat("Failed to finish input event.  status=%d", status);
271         jniThrowRuntimeException(env, message.string());
272     }
273 }
274
275 static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr,
276         jlong frameTimeNanos) {
277     sp<NativeInputEventReceiver> receiver =
278             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
279     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos);
280     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
281         String8 message;
282         message.appendFormat("Failed to consume batched input event.  status=%d", status);
283         jniThrowRuntimeException(env, message.string());
284     }
285 }
286
287
288 static JNINativeMethod gMethods[] = {
289     /* name, signature, funcPtr */
290     { "nativeInit",
291             "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
292             (void*)nativeInit },
293     { "nativeDispose", "(I)V",
294             (void*)nativeDispose },
295     { "nativeFinishInputEvent", "(IIZ)V",
296             (void*)nativeFinishInputEvent },
297     { "nativeConsumeBatchedInputEvents", "(IJ)V",
298             (void*)nativeConsumeBatchedInputEvents },
299 };
300
301 #define FIND_CLASS(var, className) \
302         var = env->FindClass(className); \
303         LOG_FATAL_IF(! var, "Unable to find class " className); \
304         var = jclass(env->NewGlobalRef(var));
305
306 #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
307         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
308         LOG_FATAL_IF(! var, "Unable to find method " methodName);
309
310 int register_android_view_InputEventReceiver(JNIEnv* env) {
311     int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
312             gMethods, NELEM(gMethods));
313     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
314
315     FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
316
317     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
318             gInputEventReceiverClassInfo.clazz,
319             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
320     GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
321             gInputEventReceiverClassInfo.clazz,
322             "dispatchBatchedInputEventPending", "()V");
323     return 0;
324 }
325
326 } // namespace android