• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <inttypes.h>
22 
23 #include <nativehelper/JNIHelp.h>
24 
25 #include <android_runtime/AndroidRuntime.h>
26 #include <log/log.h>
27 #include <utils/Looper.h>
28 #include <utils/Vector.h>
29 #include <input/InputTransport.h>
30 #include "android_os_MessageQueue.h"
31 #include "android_view_InputChannel.h"
32 #include "android_view_KeyEvent.h"
33 #include "android_view_MotionEvent.h"
34 
35 #include <nativehelper/ScopedLocalRef.h>
36 
37 #include "core_jni_helpers.h"
38 
39 namespace android {
40 
41 static const bool kDebugDispatchCycle = false;
42 
43 static struct {
44     jclass clazz;
45 
46     jmethodID dispatchInputEvent;
47     jmethodID dispatchBatchedInputEventPending;
48 } gInputEventReceiverClassInfo;
49 
50 
51 class NativeInputEventReceiver : public LooperCallback {
52 public:
53     NativeInputEventReceiver(JNIEnv* env,
54             jobject receiverWeak, const sp<InputChannel>& inputChannel,
55             const sp<MessageQueue>& messageQueue);
56 
57     status_t initialize();
58     void dispose();
59     status_t finishInputEvent(uint32_t seq, bool handled);
60     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
61             bool* outConsumedBatch);
62 
63 protected:
64     virtual ~NativeInputEventReceiver();
65 
66 private:
67     struct Finish {
68         uint32_t seq;
69         bool handled;
70     };
71 
72     jobject mReceiverWeakGlobal;
73     InputConsumer mInputConsumer;
74     sp<MessageQueue> mMessageQueue;
75     PreallocatedInputEventFactory mInputEventFactory;
76     bool mBatchedInputEventPending;
77     int mFdEvents;
78     Vector<Finish> mFinishQueue;
79 
80     void setFdEvents(int events);
81 
getInputChannelName()82     const std::string getInputChannelName() {
83         return mInputConsumer.getChannel()->getName();
84     }
85 
86     virtual int handleEvent(int receiveFd, int events, void* data);
87 };
88 
89 
NativeInputEventReceiver(JNIEnv * env,jobject receiverWeak,const sp<InputChannel> & inputChannel,const sp<MessageQueue> & messageQueue)90 NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
91         jobject receiverWeak, const sp<InputChannel>& inputChannel,
92         const sp<MessageQueue>& messageQueue) :
93         mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
94         mInputConsumer(inputChannel), mMessageQueue(messageQueue),
95         mBatchedInputEventPending(false), mFdEvents(0) {
96     if (kDebugDispatchCycle) {
97         ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
98     }
99 }
100 
~NativeInputEventReceiver()101 NativeInputEventReceiver::~NativeInputEventReceiver() {
102     JNIEnv* env = AndroidRuntime::getJNIEnv();
103     env->DeleteGlobalRef(mReceiverWeakGlobal);
104 }
105 
initialize()106 status_t NativeInputEventReceiver::initialize() {
107     setFdEvents(ALOOPER_EVENT_INPUT);
108     return OK;
109 }
110 
dispose()111 void NativeInputEventReceiver::dispose() {
112     if (kDebugDispatchCycle) {
113         ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName().c_str());
114     }
115 
116     setFdEvents(0);
117 }
118 
finishInputEvent(uint32_t seq,bool handled)119 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
120     if (kDebugDispatchCycle) {
121         ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
122     }
123 
124     status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
125     if (status) {
126         if (status == WOULD_BLOCK) {
127             if (kDebugDispatchCycle) {
128                 ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
129                         "Enqueued for later.", getInputChannelName().c_str());
130             }
131             Finish finish;
132             finish.seq = seq;
133             finish.handled = handled;
134             mFinishQueue.add(finish);
135             if (mFinishQueue.size() == 1) {
136                 setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
137             }
138             return OK;
139         }
140         ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
141                 getInputChannelName().c_str(), status);
142     }
143     return status;
144 }
145 
setFdEvents(int events)146 void NativeInputEventReceiver::setFdEvents(int events) {
147     if (mFdEvents != events) {
148         mFdEvents = events;
149         int fd = mInputConsumer.getChannel()->getFd();
150         if (events) {
151             mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
152         } else {
153             mMessageQueue->getLooper()->removeFd(fd);
154         }
155     }
156 }
157 
handleEvent(int receiveFd,int events,void * data)158 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
159     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
160         // This error typically occurs when the publisher has closed the input channel
161         // as part of removing a window or finishing an IME session, in which case
162         // the consumer will soon be disposed as well.
163         if (kDebugDispatchCycle) {
164             ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
165                     "events=0x%x", getInputChannelName().c_str(), events);
166         }
167         return 0; // remove the callback
168     }
169 
170     if (events & ALOOPER_EVENT_INPUT) {
171         JNIEnv* env = AndroidRuntime::getJNIEnv();
172         status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
173         mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
174         return status == OK || status == NO_MEMORY ? 1 : 0;
175     }
176 
177     if (events & ALOOPER_EVENT_OUTPUT) {
178         for (size_t i = 0; i < mFinishQueue.size(); i++) {
179             const Finish& finish = mFinishQueue.itemAt(i);
180             status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
181             if (status) {
182                 mFinishQueue.removeItemsAt(0, i);
183 
184                 if (status == WOULD_BLOCK) {
185                     if (kDebugDispatchCycle) {
186                         ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
187                                 getInputChannelName().c_str(), i, mFinishQueue.size());
188                     }
189                     return 1; // keep the callback, try again later
190                 }
191 
192                 ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
193                         getInputChannelName().c_str(), status);
194                 if (status != DEAD_OBJECT) {
195                     JNIEnv* env = AndroidRuntime::getJNIEnv();
196                     String8 message;
197                     message.appendFormat("Failed to finish input event.  status=%d", status);
198                     jniThrowRuntimeException(env, message.string());
199                     mMessageQueue->raiseAndClearException(env, "finishInputEvent");
200                 }
201                 return 0; // remove the callback
202             }
203         }
204         if (kDebugDispatchCycle) {
205             ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
206                     getInputChannelName().c_str(), mFinishQueue.size());
207         }
208         mFinishQueue.clear();
209         setFdEvents(ALOOPER_EVENT_INPUT);
210         return 1;
211     }
212 
213     ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
214             "events=0x%x", getInputChannelName().c_str(), events);
215     return 1;
216 }
217 
consumeEvents(JNIEnv * env,bool consumeBatches,nsecs_t frameTime,bool * outConsumedBatch)218 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
219         bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
220     if (kDebugDispatchCycle) {
221         ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
222                 getInputChannelName().c_str(),
223                 consumeBatches ? "true" : "false", frameTime);
224     }
225 
226     if (consumeBatches) {
227         mBatchedInputEventPending = false;
228     }
229     if (outConsumedBatch) {
230         *outConsumedBatch = false;
231     }
232 
233     ScopedLocalRef<jobject> receiverObj(env, NULL);
234     bool skipCallbacks = false;
235     for (;;) {
236         uint32_t seq;
237         InputEvent* inputEvent;
238         status_t status = mInputConsumer.consume(&mInputEventFactory,
239                 consumeBatches, frameTime, &seq, &inputEvent);
240         if (status) {
241             if (status == WOULD_BLOCK) {
242                 if (!skipCallbacks && !mBatchedInputEventPending
243                         && mInputConsumer.hasPendingBatch()) {
244                     // There is a pending batch.  Come back later.
245                     if (!receiverObj.get()) {
246                         receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
247                         if (!receiverObj.get()) {
248                             ALOGW("channel '%s' ~ Receiver object was finalized "
249                                     "without being disposed.", getInputChannelName().c_str());
250                             return DEAD_OBJECT;
251                         }
252                     }
253 
254                     mBatchedInputEventPending = true;
255                     if (kDebugDispatchCycle) {
256                         ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
257                                 getInputChannelName().c_str());
258                     }
259                     env->CallVoidMethod(receiverObj.get(),
260                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
261                     if (env->ExceptionCheck()) {
262                         ALOGE("Exception dispatching batched input events.");
263                         mBatchedInputEventPending = false; // try again later
264                     }
265                 }
266                 return OK;
267             }
268             ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
269                     getInputChannelName().c_str(), status);
270             return status;
271         }
272         assert(inputEvent);
273 
274         if (!skipCallbacks) {
275             if (!receiverObj.get()) {
276                 receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
277                 if (!receiverObj.get()) {
278                     ALOGW("channel '%s' ~ Receiver object was finalized "
279                             "without being disposed.", getInputChannelName().c_str());
280                     return DEAD_OBJECT;
281                 }
282             }
283 
284             jobject inputEventObj;
285             switch (inputEvent->getType()) {
286             case AINPUT_EVENT_TYPE_KEY:
287                 if (kDebugDispatchCycle) {
288                     ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
289                 }
290                 inputEventObj = android_view_KeyEvent_fromNative(env,
291                         static_cast<KeyEvent*>(inputEvent));
292                 break;
293 
294             case AINPUT_EVENT_TYPE_MOTION: {
295                 if (kDebugDispatchCycle) {
296                     ALOGD("channel '%s' ~ Received motion event.", getInputChannelName().c_str());
297                 }
298                 MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
299                 if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
300                     *outConsumedBatch = true;
301                 }
302                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
303                 break;
304             }
305 
306             default:
307                 assert(false); // InputConsumer should prevent this from ever happening
308                 inputEventObj = NULL;
309             }
310 
311             if (inputEventObj) {
312                 if (kDebugDispatchCycle) {
313                     ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
314                 }
315                 env->CallVoidMethod(receiverObj.get(),
316                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
317                 if (env->ExceptionCheck()) {
318                     ALOGE("Exception dispatching input event.");
319                     skipCallbacks = true;
320                 }
321                 env->DeleteLocalRef(inputEventObj);
322             } else {
323                 ALOGW("channel '%s' ~ Failed to obtain event object.",
324                         getInputChannelName().c_str());
325                 skipCallbacks = true;
326             }
327         }
328 
329         if (skipCallbacks) {
330             mInputConsumer.sendFinishedSignal(seq, false);
331         }
332     }
333 }
334 
335 
nativeInit(JNIEnv * env,jclass clazz,jobject receiverWeak,jobject inputChannelObj,jobject messageQueueObj)336 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
337         jobject inputChannelObj, jobject messageQueueObj) {
338     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
339             inputChannelObj);
340     if (inputChannel == NULL) {
341         jniThrowRuntimeException(env, "InputChannel is not initialized.");
342         return 0;
343     }
344 
345     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
346     if (messageQueue == NULL) {
347         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
348         return 0;
349     }
350 
351     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
352             receiverWeak, inputChannel, messageQueue);
353     status_t status = receiver->initialize();
354     if (status) {
355         String8 message;
356         message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
357         jniThrowRuntimeException(env, message.string());
358         return 0;
359     }
360 
361     receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
362     return reinterpret_cast<jlong>(receiver.get());
363 }
364 
nativeDispose(JNIEnv * env,jclass clazz,jlong receiverPtr)365 static void nativeDispose(JNIEnv* env, jclass clazz, jlong receiverPtr) {
366     sp<NativeInputEventReceiver> receiver =
367             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
368     receiver->dispose();
369     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
370 }
371 
nativeFinishInputEvent(JNIEnv * env,jclass clazz,jlong receiverPtr,jint seq,jboolean handled)372 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
373         jint seq, jboolean handled) {
374     sp<NativeInputEventReceiver> receiver =
375             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
376     status_t status = receiver->finishInputEvent(seq, handled);
377     if (status && status != DEAD_OBJECT) {
378         String8 message;
379         message.appendFormat("Failed to finish input event.  status=%d", status);
380         jniThrowRuntimeException(env, message.string());
381     }
382 }
383 
nativeConsumeBatchedInputEvents(JNIEnv * env,jclass clazz,jlong receiverPtr,jlong frameTimeNanos)384 static jboolean nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jlong receiverPtr,
385         jlong frameTimeNanos) {
386     sp<NativeInputEventReceiver> receiver =
387             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
388     bool consumedBatch;
389     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
390             &consumedBatch);
391     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
392         String8 message;
393         message.appendFormat("Failed to consume batched input event.  status=%d", status);
394         jniThrowRuntimeException(env, message.string());
395         return JNI_FALSE;
396     }
397     return consumedBatch ? JNI_TRUE : JNI_FALSE;
398 }
399 
400 
401 static const JNINativeMethod gMethods[] = {
402     /* name, signature, funcPtr */
403     { "nativeInit",
404             "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
405             (void*)nativeInit },
406     { "nativeDispose", "(J)V",
407             (void*)nativeDispose },
408     { "nativeFinishInputEvent", "(JIZ)V",
409             (void*)nativeFinishInputEvent },
410     { "nativeConsumeBatchedInputEvents", "(JJ)Z",
411             (void*)nativeConsumeBatchedInputEvents },
412 };
413 
register_android_view_InputEventReceiver(JNIEnv * env)414 int register_android_view_InputEventReceiver(JNIEnv* env) {
415     int res = RegisterMethodsOrDie(env, "android/view/InputEventReceiver",
416             gMethods, NELEM(gMethods));
417 
418     jclass clazz = FindClassOrDie(env, "android/view/InputEventReceiver");
419     gInputEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
420 
421     gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
422             gInputEventReceiverClassInfo.clazz,
423             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
424     gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
425             gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
426 
427     return res;
428 }
429 
430 } // namespace android
431