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-base/stringprintf.h>
26 #include <android_runtime/AndroidRuntime.h>
27 #include <input/InputTransport.h>
28 #include <log/log.h>
29 #include <utils/Looper.h>
30 #include <variant>
31 #include <vector>
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 #include <nativehelper/ScopedLocalRef.h>
38 
39 #include "core_jni_helpers.h"
40 
41 namespace android {
42 
43 static const bool kDebugDispatchCycle = false;
44 
45 static const char* toString(bool value) {
46     return value ? "true" : "false";
47 }
48 
49 static struct {
50     jclass clazz;
51 
52     jmethodID dispatchInputEvent;
53     jmethodID onFocusEvent;
54     jmethodID onPointerCaptureEvent;
55     jmethodID onDragEvent;
56     jmethodID onBatchedInputEventPending;
57 } gInputEventReceiverClassInfo;
58 
59 // Add prefix to the beginning of each line in 'str'
60 static std::string addPrefix(std::string str, std::string_view prefix) {
61     str.insert(0, prefix); // insert at the beginning of the first line
62     const size_t prefixLength = prefix.length();
63     size_t pos = prefixLength; // just inserted prefix. start at the end of it
64     while (true) {             // process all newline characters in 'str'
65         pos = str.find('\n', pos);
66         if (pos == std::string::npos) {
67             break;
68         }
69         str.insert(pos + 1, prefix); // insert prefix just after the '\n' character
70         pos += prefixLength + 1;     // advance the position past the newly inserted prefix
71     }
72     return str;
73 }
74 
75 class NativeInputEventReceiver : public LooperCallback {
76 public:
77     NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
78                              const std::shared_ptr<InputChannel>& inputChannel,
79                              const sp<MessageQueue>& messageQueue);
80 
81     status_t initialize();
82     void dispose();
83     status_t finishInputEvent(uint32_t seq, bool handled);
84     status_t reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
85     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
86             bool* outConsumedBatch);
87     std::string dump(const char* prefix);
88 
89 protected:
90     virtual ~NativeInputEventReceiver();
91 
92 private:
93     struct Finish {
94         uint32_t seq;
95         bool handled;
96     };
97 
98     struct Timeline {
99         int32_t inputEventId;
100         std::array<nsecs_t, GraphicsTimeline::SIZE> timeline;
101     };
102     typedef std::variant<Finish, Timeline> OutboundEvent;
103 
104     jobject mReceiverWeakGlobal;
105     InputConsumer mInputConsumer;
106     sp<MessageQueue> mMessageQueue;
107     PreallocatedInputEventFactory mInputEventFactory;
108     bool mBatchedInputEventPending;
109     int mFdEvents;
110     std::vector<OutboundEvent> mOutboundQueue;
111 
112     void setFdEvents(int events);
113 
114     const std::string getInputChannelName() {
115         return mInputConsumer.getChannel()->getName();
116     }
117 
118     status_t processOutboundEvents();
119     // From 'LooperCallback'
120     int handleEvent(int receiveFd, int events, void* data) override;
121 };
122 
123 NativeInputEventReceiver::NativeInputEventReceiver(
124         JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
125         const sp<MessageQueue>& messageQueue)
126       : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
127         mInputConsumer(inputChannel),
128         mMessageQueue(messageQueue),
129         mBatchedInputEventPending(false),
130         mFdEvents(0) {
131     if (kDebugDispatchCycle) {
132         ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
133     }
134 }
135 
136 NativeInputEventReceiver::~NativeInputEventReceiver() {
137     JNIEnv* env = AndroidRuntime::getJNIEnv();
138     env->DeleteGlobalRef(mReceiverWeakGlobal);
139 }
140 
141 status_t NativeInputEventReceiver::initialize() {
142     setFdEvents(ALOOPER_EVENT_INPUT);
143     return OK;
144 }
145 
146 void NativeInputEventReceiver::dispose() {
147     if (kDebugDispatchCycle) {
148         ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName().c_str());
149     }
150 
151     setFdEvents(0);
152 }
153 
154 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
155     if (kDebugDispatchCycle) {
156         ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
157     }
158 
159     Finish finish{
160             .seq = seq,
161             .handled = handled,
162     };
163     mOutboundQueue.push_back(finish);
164     return processOutboundEvents();
165 }
166 
167 status_t NativeInputEventReceiver::reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime,
168                                                   nsecs_t presentTime) {
169     if (kDebugDispatchCycle) {
170         ALOGD("channel '%s' ~ %s", getInputChannelName().c_str(), __func__);
171     }
172     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
173     graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = gpuCompletedTime;
174     graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime;
175     Timeline timeline{
176             .inputEventId = inputEventId,
177             .timeline = graphicsTimeline,
178     };
179     mOutboundQueue.push_back(timeline);
180     return processOutboundEvents();
181 }
182 
183 void NativeInputEventReceiver::setFdEvents(int events) {
184     if (mFdEvents != events) {
185         mFdEvents = events;
186         int fd = mInputConsumer.getChannel()->getFd();
187         if (events) {
188             mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
189         } else {
190             mMessageQueue->getLooper()->removeFd(fd);
191         }
192     }
193 }
194 
195 /**
196  * Receiver's primary role is to receive input events, but it has an additional duty of sending
197  * 'ack' for events (using the call 'finishInputEvent') and reporting input event timeline.
198  *
199  * If we are looking at the communication between InputPublisher and InputConsumer, we can say that
200  * from the InputConsumer's perspective, InputMessage's that are sent from publisher to consumer are
201  * called 'inbound / incoming' events, and the InputMessage's sent from InputConsumer to
202  * InputPublisher are 'outbound / outgoing' events.
203  *
204  * NativeInputEventReceiver owns (and acts like) an InputConsumer. So the finish events are outbound
205  * from InputEventReceiver (and will be sent to the InputPublisher). Likewise, timeline events are
206  * outbound events.
207  *
208  * In this function, send as many events from 'mOutboundQueue' as possible across the socket to the
209  * InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up
210  * unnecessarily.
211  */
212 status_t NativeInputEventReceiver::processOutboundEvents() {
213     while (!mOutboundQueue.empty()) {
214         OutboundEvent& outbound = *mOutboundQueue.begin();
215         status_t status;
216 
217         if (std::holds_alternative<Finish>(outbound)) {
218             const Finish& finish = std::get<Finish>(outbound);
219             status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
220         } else if (std::holds_alternative<Timeline>(outbound)) {
221             const Timeline& timeline = std::get<Timeline>(outbound);
222             status = mInputConsumer.sendTimeline(timeline.inputEventId, timeline.timeline);
223         } else {
224             LOG_ALWAYS_FATAL("Unexpected event type in std::variant");
225             status = BAD_VALUE;
226         }
227         if (status == OK) {
228             // Successful send. Erase the entry and keep trying to send more
229             mOutboundQueue.erase(mOutboundQueue.begin());
230             continue;
231         }
232 
233         // Publisher is busy, try again later. Keep this entry (do not erase)
234         if (status == WOULD_BLOCK) {
235             if (kDebugDispatchCycle) {
236                 ALOGD("channel '%s' ~ Remaining outbound events: %zu.",
237                       getInputChannelName().c_str(), mOutboundQueue.size());
238             }
239             setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
240             return WOULD_BLOCK; // try again later
241         }
242 
243         // Some other error. Give up
244         ALOGW("Failed to send outbound event on channel '%s'.  status=%s(%d)",
245               getInputChannelName().c_str(), statusToString(status).c_str(), status);
246         if (status != DEAD_OBJECT) {
247             JNIEnv* env = AndroidRuntime::getJNIEnv();
248             std::string message =
249                     android::base::StringPrintf("Failed to send outbound event.  status=%s(%d)",
250                                                 statusToString(status).c_str(), status);
251             jniThrowRuntimeException(env, message.c_str());
252             mMessageQueue->raiseAndClearException(env, "finishInputEvent");
253         }
254         return status;
255     }
256 
257     // The queue is now empty. Tell looper there's no more output to expect.
258     setFdEvents(ALOOPER_EVENT_INPUT);
259     return OK;
260 }
261 
262 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
263     // Allowed return values of this function as documented in LooperCallback::handleEvent
264     constexpr int REMOVE_CALLBACK = 0;
265     constexpr int KEEP_CALLBACK = 1;
266 
267     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
268         // This error typically occurs when the publisher has closed the input channel
269         // as part of removing a window or finishing an IME session, in which case
270         // the consumer will soon be disposed as well.
271         if (kDebugDispatchCycle) {
272             ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred. events=0x%x",
273                   getInputChannelName().c_str(), events);
274         }
275         return REMOVE_CALLBACK;
276     }
277 
278     if (events & ALOOPER_EVENT_INPUT) {
279         JNIEnv* env = AndroidRuntime::getJNIEnv();
280         status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
281         mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
282         return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
283     }
284 
285     if (events & ALOOPER_EVENT_OUTPUT) {
286         const status_t status = processOutboundEvents();
287         if (status == OK || status == WOULD_BLOCK) {
288             return KEEP_CALLBACK;
289         } else {
290             return REMOVE_CALLBACK;
291         }
292     }
293 
294     ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  events=0x%x",
295           getInputChannelName().c_str(), events);
296     return KEEP_CALLBACK;
297 }
298 
299 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
300         bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
301     if (kDebugDispatchCycle) {
302         ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
303               getInputChannelName().c_str(), toString(consumeBatches), frameTime);
304     }
305 
306     if (consumeBatches) {
307         mBatchedInputEventPending = false;
308     }
309     if (outConsumedBatch) {
310         *outConsumedBatch = false;
311     }
312 
313     ScopedLocalRef<jobject> receiverObj(env, nullptr);
314     bool skipCallbacks = false;
315     for (;;) {
316         uint32_t seq;
317         InputEvent* inputEvent;
318 
319         status_t status = mInputConsumer.consume(&mInputEventFactory,
320                 consumeBatches, frameTime, &seq, &inputEvent);
321         if (status != OK && status != WOULD_BLOCK) {
322             ALOGE("channel '%s' ~ Failed to consume input event.  status=%s(%d)",
323                   getInputChannelName().c_str(), statusToString(status).c_str(), status);
324             return status;
325         }
326 
327         if (status == WOULD_BLOCK) {
328             if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) {
329                 // There is a pending batch.  Come back later.
330                 if (!receiverObj.get()) {
331                     receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
332                     if (!receiverObj.get()) {
333                         ALOGW("channel '%s' ~ Receiver object was finalized "
334                               "without being disposed.",
335                               getInputChannelName().c_str());
336                         return DEAD_OBJECT;
337                     }
338                 }
339 
340                 mBatchedInputEventPending = true;
341                 if (kDebugDispatchCycle) {
342                     ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
343                           getInputChannelName().c_str());
344                 }
345 
346                 env->CallVoidMethod(receiverObj.get(),
347                                     gInputEventReceiverClassInfo.onBatchedInputEventPending,
348                                     mInputConsumer.getPendingBatchSource());
349                 if (env->ExceptionCheck()) {
350                     ALOGE("Exception dispatching batched input events.");
351                     mBatchedInputEventPending = false; // try again later
352                 }
353             }
354             return OK;
355         }
356         assert(inputEvent);
357 
358         if (!skipCallbacks) {
359             if (!receiverObj.get()) {
360                 receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
361                 if (!receiverObj.get()) {
362                     ALOGW("channel '%s' ~ Receiver object was finalized "
363                             "without being disposed.", getInputChannelName().c_str());
364                     return DEAD_OBJECT;
365                 }
366             }
367 
368             jobject inputEventObj;
369             switch (inputEvent->getType()) {
370             case AINPUT_EVENT_TYPE_KEY:
371                 if (kDebugDispatchCycle) {
372                     ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
373                 }
374                 inputEventObj = android_view_KeyEvent_fromNative(env,
375                         static_cast<KeyEvent*>(inputEvent));
376                 break;
377 
378             case AINPUT_EVENT_TYPE_MOTION: {
379                 if (kDebugDispatchCycle) {
380                     ALOGD("channel '%s' ~ Received motion event.", getInputChannelName().c_str());
381                 }
382                 MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
383                 if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
384                     *outConsumedBatch = true;
385                 }
386                 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
387                 break;
388             }
389             case AINPUT_EVENT_TYPE_FOCUS: {
390                 FocusEvent* focusEvent = static_cast<FocusEvent*>(inputEvent);
391                 if (kDebugDispatchCycle) {
392                     ALOGD("channel '%s' ~ Received focus event: hasFocus=%s, inTouchMode=%s.",
393                           getInputChannelName().c_str(), toString(focusEvent->getHasFocus()),
394                           toString(focusEvent->getInTouchMode()));
395                 }
396                 env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onFocusEvent,
397                                     jboolean(focusEvent->getHasFocus()),
398                                     jboolean(focusEvent->getInTouchMode()));
399                 finishInputEvent(seq, true /* handled */);
400                 continue;
401             }
402             case AINPUT_EVENT_TYPE_CAPTURE: {
403                 const CaptureEvent* captureEvent = static_cast<CaptureEvent*>(inputEvent);
404                 if (kDebugDispatchCycle) {
405                     ALOGD("channel '%s' ~ Received capture event: pointerCaptureEnabled=%s",
406                           getInputChannelName().c_str(),
407                           toString(captureEvent->getPointerCaptureEnabled()));
408                 }
409                 env->CallVoidMethod(receiverObj.get(),
410                                     gInputEventReceiverClassInfo.onPointerCaptureEvent,
411                                     jboolean(captureEvent->getPointerCaptureEnabled()));
412                 finishInputEvent(seq, true /* handled */);
413                 continue;
414             }
415             case AINPUT_EVENT_TYPE_DRAG: {
416                 const DragEvent* dragEvent = static_cast<DragEvent*>(inputEvent);
417                 if (kDebugDispatchCycle) {
418                     ALOGD("channel '%s' ~ Received drag event: isExiting=%s",
419                           getInputChannelName().c_str(), toString(dragEvent->isExiting()));
420                 }
421                 env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onDragEvent,
422                                     jboolean(dragEvent->isExiting()), dragEvent->getX(),
423                                     dragEvent->getY());
424                 finishInputEvent(seq, true /* handled */);
425                 continue;
426             }
427 
428             default:
429                 assert(false); // InputConsumer should prevent this from ever happening
430                 inputEventObj = nullptr;
431             }
432 
433             if (inputEventObj) {
434                 if (kDebugDispatchCycle) {
435                     ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
436                 }
437                 env->CallVoidMethod(receiverObj.get(),
438                         gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
439                 if (env->ExceptionCheck()) {
440                     ALOGE("Exception dispatching input event.");
441                     skipCallbacks = true;
442                 }
443                 env->DeleteLocalRef(inputEventObj);
444             } else {
445                 ALOGW("channel '%s' ~ Failed to obtain event object.",
446                         getInputChannelName().c_str());
447                 skipCallbacks = true;
448             }
449         }
450 
451         if (skipCallbacks) {
452             mInputConsumer.sendFinishedSignal(seq, false);
453         }
454     }
455 }
456 
457 std::string NativeInputEventReceiver::dump(const char* prefix) {
458     std::string out;
459     std::string consumerDump = addPrefix(mInputConsumer.dump(), "  ");
460     out = out + "mInputConsumer:\n" + consumerDump + "\n";
461 
462     out += android::base::StringPrintf("mBatchedInputEventPending: %s\n",
463                                        toString(mBatchedInputEventPending));
464     out = out + "mOutboundQueue:\n";
465     for (const OutboundEvent& outbound : mOutboundQueue) {
466         if (std::holds_alternative<Finish>(outbound)) {
467             const Finish& finish = std::get<Finish>(outbound);
468             out += android::base::StringPrintf("  Finish: seq=%" PRIu32 " handled=%s\n", finish.seq,
469                                                toString(finish.handled));
470         } else if (std::holds_alternative<Timeline>(outbound)) {
471             const Timeline& timeline = std::get<Timeline>(outbound);
472             out += android::base::
473                     StringPrintf("  Timeline: inputEventId=%" PRId32 " gpuCompletedTime=%" PRId64
474                                  ", presentTime=%" PRId64 "\n",
475                                  timeline.inputEventId,
476                                  timeline.timeline[GraphicsTimeline::GPU_COMPLETED_TIME],
477                                  timeline.timeline[GraphicsTimeline::PRESENT_TIME]);
478         }
479     }
480     if (mOutboundQueue.empty()) {
481         out = out + "  <empty>\n";
482     }
483     return addPrefix(out, prefix);
484 }
485 
486 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
487         jobject inputChannelObj, jobject messageQueueObj) {
488     std::shared_ptr<InputChannel> inputChannel =
489             android_view_InputChannel_getInputChannel(env, inputChannelObj);
490     if (inputChannel == nullptr) {
491         jniThrowRuntimeException(env, "InputChannel is not initialized.");
492         return 0;
493     }
494 
495     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
496     if (messageQueue == nullptr) {
497         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
498         return 0;
499     }
500 
501     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
502             receiverWeak, inputChannel, messageQueue);
503     status_t status = receiver->initialize();
504     if (status) {
505         std::string message = android::base::
506                 StringPrintf("Failed to initialize input event receiver.  status=%s(%d)",
507                              statusToString(status).c_str(), status);
508         jniThrowRuntimeException(env, message.c_str());
509         return 0;
510     }
511 
512     receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
513     return reinterpret_cast<jlong>(receiver.get());
514 }
515 
516 static void nativeDispose(JNIEnv* env, jclass clazz, jlong receiverPtr) {
517     sp<NativeInputEventReceiver> receiver =
518             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
519     receiver->dispose();
520     receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
521 }
522 
523 static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
524         jint seq, jboolean handled) {
525     sp<NativeInputEventReceiver> receiver =
526             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
527     status_t status = receiver->finishInputEvent(seq, handled);
528     if (status == OK || status == WOULD_BLOCK) {
529         return; // normal operation
530     }
531     if (status != DEAD_OBJECT) {
532         std::string message =
533                 android::base::StringPrintf("Failed to finish input event.  status=%s(%d)",
534                                             statusToString(status).c_str(), status);
535         jniThrowRuntimeException(env, message.c_str());
536     }
537 }
538 
539 static void nativeReportTimeline(JNIEnv* env, jclass clazz, jlong receiverPtr, jint inputEventId,
540                                  jlong gpuCompletedTime, jlong presentTime) {
541     if (IdGenerator::getSource(inputEventId) != IdGenerator::Source::INPUT_READER) {
542         // skip this event, it did not originate from hardware
543         return;
544     }
545     sp<NativeInputEventReceiver> receiver =
546             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
547     status_t status = receiver->reportTimeline(inputEventId, gpuCompletedTime, presentTime);
548     if (status == OK || status == WOULD_BLOCK) {
549         return; // normal operation
550     }
551     if (status != DEAD_OBJECT) {
552         std::string message = android::base::StringPrintf("Failed to send timeline.  status=%s(%d)",
553                                                           strerror(-status), status);
554         jniThrowRuntimeException(env, message.c_str());
555     }
556 }
557 
558 static jboolean nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jlong receiverPtr,
559         jlong frameTimeNanos) {
560     sp<NativeInputEventReceiver> receiver =
561             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
562     bool consumedBatch;
563     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
564             &consumedBatch);
565     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
566         std::string message =
567                 android::base::StringPrintf("Failed to consume batched input event.  status=%s(%d)",
568                                             statusToString(status).c_str(), status);
569         jniThrowRuntimeException(env, message.c_str());
570         return JNI_FALSE;
571     }
572     return consumedBatch ? JNI_TRUE : JNI_FALSE;
573 }
574 
575 static jstring nativeDump(JNIEnv* env, jclass clazz, jlong receiverPtr, jstring prefix) {
576     sp<NativeInputEventReceiver> receiver =
577             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
578     ScopedUtfChars prefixChars(env, prefix);
579     return env->NewStringUTF(receiver->dump(prefixChars.c_str()).c_str());
580 }
581 
582 static const JNINativeMethod gMethods[] = {
583         /* name, signature, funcPtr */
584         {"nativeInit",
585          "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
586          (void*)nativeInit},
587         {"nativeDispose", "(J)V", (void*)nativeDispose},
588         {"nativeFinishInputEvent", "(JIZ)V", (void*)nativeFinishInputEvent},
589         {"nativeReportTimeline", "(JIJJ)V", (void*)nativeReportTimeline},
590         {"nativeConsumeBatchedInputEvents", "(JJ)Z", (void*)nativeConsumeBatchedInputEvents},
591         {"nativeDump", "(JLjava/lang/String;)Ljava/lang/String;", (void*)nativeDump},
592 };
593 
594 int register_android_view_InputEventReceiver(JNIEnv* env) {
595     int res = RegisterMethodsOrDie(env, "android/view/InputEventReceiver",
596             gMethods, NELEM(gMethods));
597 
598     jclass clazz = FindClassOrDie(env, "android/view/InputEventReceiver");
599     gInputEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
600 
601     gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
602             gInputEventReceiverClassInfo.clazz,
603             "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
604     gInputEventReceiverClassInfo.onFocusEvent =
605             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
606     gInputEventReceiverClassInfo.onPointerCaptureEvent =
607             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent",
608                              "(Z)V");
609     gInputEventReceiverClassInfo.onDragEvent =
610             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onDragEvent", "(ZFF)V");
611     gInputEventReceiverClassInfo.onBatchedInputEventPending =
612             GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending",
613                              "(I)V");
614 
615     return res;
616 }
617 
618 } // namespace android
619