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