2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "InputEventReceiver"
19 //#define LOG_NDEBUG 0
21 // Log debug messages about the dispatch cycle.
22 #define DEBUG_DISPATCH_CYCLE 0
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"
42 jmethodID dispatchInputEvent;
43 jmethodID dispatchBatchedInputEventPending;
44 } gInputEventReceiverClassInfo;
47 class NativeInputEventReceiver : public LooperCallback {
49 NativeInputEventReceiver(JNIEnv* env,
50 jobject receiverObj, const sp<InputChannel>& inputChannel,
51 const sp<MessageQueue>& messageQueue);
53 status_t initialize();
55 status_t finishInputEvent(uint32_t seq, bool handled);
56 status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime);
59 virtual ~NativeInputEventReceiver();
62 jobject mReceiverObjGlobal;
63 InputConsumer mInputConsumer;
64 sp<MessageQueue> mMessageQueue;
65 PreallocatedInputEventFactory mInputEventFactory;
66 bool mBatchedInputEventPending;
68 const char* getInputChannelName() {
69 return mInputConsumer.getChannel()->getName().string();
72 virtual int handleEvent(int receiveFd, int events, void* data);
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());
87 NativeInputEventReceiver::~NativeInputEventReceiver() {
88 JNIEnv* env = AndroidRuntime::getJNIEnv();
89 env->DeleteGlobalRef(mReceiverObjGlobal);
92 status_t NativeInputEventReceiver::initialize() {
93 int receiveFd = mInputConsumer.getChannel()->getFd();
94 mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
98 void NativeInputEventReceiver::dispose() {
99 #if DEBUG_DISPATCH_CYCLE
100 ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
103 mMessageQueue->getLooper()->removeFd(mInputConsumer.getChannel()->getFd());
106 status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
107 #if DEBUG_DISPATCH_CYCLE
108 ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
111 status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
113 ALOGW("Failed to send finished signal on channel '%s'. status=%d",
114 getInputChannelName(), status);
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
126 if (!(events & ALOOPER_EVENT_INPUT)) {
127 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
128 "events=0x%x", getInputChannelName(), events);
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;
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);
145 if (consumeBatches) {
146 mBatchedInputEventPending = false;
149 bool skipCallbacks = false;
152 InputEvent* inputEvent;
153 status_t status = mInputConsumer.consume(&mInputEventFactory,
154 consumeBatches, frameTime, &seq, &inputEvent);
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());
165 env->CallVoidMethod(mReceiverObjGlobal,
166 gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
167 if (env->ExceptionCheck()) {
168 ALOGE("Exception dispatching batched input events.");
169 mBatchedInputEventPending = false; // try again later
174 ALOGE("channel '%s' ~ Failed to consume input event. status=%d",
175 getInputChannelName(), status);
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());
187 inputEventObj = android_view_KeyEvent_fromNative(env,
188 static_cast<KeyEvent*>(inputEvent));
191 case AINPUT_EVENT_TYPE_MOTION:
192 #if DEBUG_DISPATCH_CYCLE
193 ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
195 inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
196 static_cast<MotionEvent*>(inputEvent));
200 assert(false); // InputConsumer should prevent this from ever happening
201 inputEventObj = NULL;
205 #if DEBUG_DISPATCH_CYCLE
206 ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
208 env->CallVoidMethod(mReceiverObjGlobal,
209 gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
210 if (env->ExceptionCheck()) {
211 ALOGE("Exception dispatching input event.");
212 skipCallbacks = true;
215 ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
216 skipCallbacks = true;
221 mInputConsumer.sendFinishedSignal(seq, false);
227 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
228 jobject inputChannelObj, jobject messageQueueObj) {
229 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
231 if (inputChannel == NULL) {
232 jniThrowRuntimeException(env, "InputChannel is not initialized.");
236 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
237 if (messageQueue == NULL) {
238 jniThrowRuntimeException(env, "MessageQueue is not initialized.");
242 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
243 receiverObj, inputChannel, messageQueue);
244 status_t status = receiver->initialize();
247 message.appendFormat("Failed to initialize input event receiver. status=%d", status);
248 jniThrowRuntimeException(env, message.string());
252 receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
253 return reinterpret_cast<jint>(receiver.get());
256 static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
257 sp<NativeInputEventReceiver> receiver =
258 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
260 receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
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) {
270 message.appendFormat("Failed to finish input event. status=%d", status);
271 jniThrowRuntimeException(env, message.string());
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()) {
282 message.appendFormat("Failed to consume batched input event. status=%d", status);
283 jniThrowRuntimeException(env, message.string());
288 static JNINativeMethod gMethods[] = {
289 /* name, signature, funcPtr */
291 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
293 { "nativeDispose", "(I)V",
294 (void*)nativeDispose },
295 { "nativeFinishInputEvent", "(IIZ)V",
296 (void*)nativeFinishInputEvent },
297 { "nativeConsumeBatchedInputEvents", "(IJ)V",
298 (void*)nativeConsumeBatchedInputEvents },
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));
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);
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.");
315 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
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");
326 } // namespace android