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