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