1 /* 2 * Copyright (C) 2010 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 "InputQueue-JNI" 18 19 //#define LOG_NDEBUG 0 20 21 // Log debug messages about the dispatch cycle. 22 #define DEBUG_DISPATCH_CYCLE 0 23 24 // Log debug messages about registrations. 25 #define DEBUG_REGISTRATION 0 26 27 28 #include "JNIHelp.h" 29 30 #include <android_runtime/AndroidRuntime.h> 31 #include <utils/Log.h> 32 #include <utils/Looper.h> 33 #include <utils/KeyedVector.h> 34 #include <utils/threads.h> 35 #include <ui/InputTransport.h> 36 #include "android_os_MessageQueue.h" 37 #include "android_view_InputChannel.h" 38 #include "android_view_KeyEvent.h" 39 #include "android_view_MotionEvent.h" 40 41 namespace android { 42 43 // ---------------------------------------------------------------------------- 44 45 static struct { 46 jclass clazz; 47 48 jmethodID dispatchKeyEvent; 49 jmethodID dispatchMotionEvent; 50 } gInputQueueClassInfo; 51 52 // ---------------------------------------------------------------------------- 53 54 class NativeInputQueue { 55 public: 56 NativeInputQueue(); 57 ~NativeInputQueue(); 58 59 status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj, 60 jobject inputHandlerObj, jobject messageQueueObj); 61 62 status_t unregisterInputChannel(JNIEnv* env, jobject inputChannelObj); 63 64 status_t finished(JNIEnv* env, jlong finishedToken, bool handled, bool ignoreSpuriousFinish); 65 66 private: 67 class Connection : public RefBase { 68 protected: 69 virtual ~Connection(); 70 71 public: 72 enum Status { 73 // Everything is peachy. 74 STATUS_NORMAL, 75 // The input channel has been unregistered. 76 STATUS_ZOMBIE 77 }; 78 79 Connection(uint16_t id, 80 const sp<InputChannel>& inputChannel, const sp<Looper>& looper); 81 82 inline const char* getInputChannelName() const { return inputChannel->getName().string(); } 83 84 // A unique id for this connection. 85 uint16_t id; 86 87 Status status; 88 89 sp<InputChannel> inputChannel; 90 InputConsumer inputConsumer; 91 sp<Looper> looper; 92 jobject inputHandlerObjGlobal; 93 PreallocatedInputEventFactory inputEventFactory; 94 95 // The sequence number of the current event being dispatched. 96 // This is used as part of the finished token as a way to determine whether the finished 97 // token is still valid before sending a finished signal back to the publisher. 98 uint16_t messageSeqNum; 99 100 // True if a message has been received from the publisher but not yet finished. 101 bool messageInProgress; 102 }; 103 104 Mutex mLock; 105 uint16_t mNextConnectionId; 106 KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd; 107 108 ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel); 109 110 static void handleInputChannelDisposed(JNIEnv* env, 111 jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data); 112 113 static int handleReceiveCallback(int receiveFd, int events, void* data); 114 115 static jlong generateFinishedToken(int32_t receiveFd, 116 uint16_t connectionId, uint16_t messageSeqNum); 117 118 static void parseFinishedToken(jlong finishedToken, 119 int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex); 120 }; 121 122 // ---------------------------------------------------------------------------- 123 124 NativeInputQueue::NativeInputQueue() : 125 mNextConnectionId(0) { 126 } 127 128 NativeInputQueue::~NativeInputQueue() { 129 } 130 131 status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj, 132 jobject inputHandlerObj, jobject messageQueueObj) { 133 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, 134 inputChannelObj); 135 if (inputChannel == NULL) { 136 LOGW("Input channel is not initialized."); 137 return BAD_VALUE; 138 } 139 140 #if DEBUG_REGISTRATION 141 LOGD("channel '%s' - Registered", inputChannel->getName().string()); 142 #endif 143 144 sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); 145 146 { // acquire lock 147 AutoMutex _l(mLock); 148 149 if (getConnectionIndex(inputChannel) >= 0) { 150 LOGW("Attempted to register already registered input channel '%s'", 151 inputChannel->getName().string()); 152 return BAD_VALUE; 153 } 154 155 uint16_t connectionId = mNextConnectionId++; 156 sp<Connection> connection = new Connection(connectionId, inputChannel, looper); 157 status_t result = connection->inputConsumer.initialize(); 158 if (result) { 159 LOGW("Failed to initialize input consumer for input channel '%s', status=%d", 160 inputChannel->getName().string(), result); 161 return result; 162 } 163 164 connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj); 165 166 int32_t receiveFd = inputChannel->getReceivePipeFd(); 167 mConnectionsByReceiveFd.add(receiveFd, connection); 168 169 looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); 170 } // release lock 171 172 android_view_InputChannel_setDisposeCallback(env, inputChannelObj, 173 handleInputChannelDisposed, this); 174 return OK; 175 } 176 177 status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) { 178 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, 179 inputChannelObj); 180 if (inputChannel == NULL) { 181 LOGW("Input channel is not initialized."); 182 return BAD_VALUE; 183 } 184 185 #if DEBUG_REGISTRATION 186 LOGD("channel '%s' - Unregistered", inputChannel->getName().string()); 187 #endif 188 189 { // acquire lock 190 AutoMutex _l(mLock); 191 192 ssize_t connectionIndex = getConnectionIndex(inputChannel); 193 if (connectionIndex < 0) { 194 LOGW("Attempted to unregister already unregistered input channel '%s'", 195 inputChannel->getName().string()); 196 return BAD_VALUE; 197 } 198 199 sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); 200 mConnectionsByReceiveFd.removeItemsAt(connectionIndex); 201 202 connection->status = Connection::STATUS_ZOMBIE; 203 204 connection->looper->removeFd(inputChannel->getReceivePipeFd()); 205 206 env->DeleteGlobalRef(connection->inputHandlerObjGlobal); 207 connection->inputHandlerObjGlobal = NULL; 208 209 if (connection->messageInProgress) { 210 LOGI("Sending finished signal for input channel '%s' since it is being unregistered " 211 "while an input message is still in progress.", 212 connection->getInputChannelName()); 213 connection->messageInProgress = false; 214 connection->inputConsumer.sendFinishedSignal(false); // ignoring result 215 } 216 } // release lock 217 218 android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); 219 return OK; 220 } 221 222 ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChannel) { 223 ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd()); 224 if (connectionIndex >= 0) { 225 sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); 226 if (connection->inputChannel.get() == inputChannel.get()) { 227 return connectionIndex; 228 } 229 } 230 231 return -1; 232 } 233 234 status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, 235 bool handled, bool ignoreSpuriousFinish) { 236 int32_t receiveFd; 237 uint16_t connectionId; 238 uint16_t messageSeqNum; 239 parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum); 240 241 { // acquire lock 242 AutoMutex _l(mLock); 243 244 ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd); 245 if (connectionIndex < 0) { 246 if (! ignoreSpuriousFinish) { 247 LOGI("Ignoring finish signal on channel that is no longer registered."); 248 } 249 return DEAD_OBJECT; 250 } 251 252 sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); 253 if (connectionId != connection->id) { 254 if (! ignoreSpuriousFinish) { 255 LOGI("Ignoring finish signal on channel that is no longer registered."); 256 } 257 return DEAD_OBJECT; 258 } 259 260 if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) { 261 if (! ignoreSpuriousFinish) { 262 LOGW("Attempted to finish input twice on channel '%s'. " 263 "finished messageSeqNum=%d, current messageSeqNum=%d, messageInProgress=%d", 264 connection->getInputChannelName(), 265 messageSeqNum, connection->messageSeqNum, connection->messageInProgress); 266 } 267 return INVALID_OPERATION; 268 } 269 270 connection->messageInProgress = false; 271 272 status_t status = connection->inputConsumer.sendFinishedSignal(handled); 273 if (status) { 274 LOGW("Failed to send finished signal on channel '%s'. status=%d", 275 connection->getInputChannelName(), status); 276 return status; 277 } 278 279 #if DEBUG_DISPATCH_CYCLE 280 LOGD("channel '%s' ~ Finished event.", 281 connection->getInputChannelName()); 282 #endif 283 } // release lock 284 285 return OK; 286 } 287 288 void NativeInputQueue::handleInputChannelDisposed(JNIEnv* env, 289 jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) { 290 LOGW("Input channel object '%s' was disposed without first being unregistered with " 291 "the input queue!", inputChannel->getName().string()); 292 293 NativeInputQueue* q = static_cast<NativeInputQueue*>(data); 294 q->unregisterInputChannel(env, inputChannelObj); 295 } 296 297 int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) { 298 NativeInputQueue* q = static_cast<NativeInputQueue*>(data); 299 JNIEnv* env = AndroidRuntime::getJNIEnv(); 300 301 sp<Connection> connection; 302 InputEvent* inputEvent; 303 jobject inputHandlerObjLocal; 304 jlong finishedToken; 305 { // acquire lock 306 AutoMutex _l(q->mLock); 307 308 ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd); 309 if (connectionIndex < 0) { 310 LOGE("Received spurious receive callback for unknown input channel. " 311 "fd=%d, events=0x%x", receiveFd, events); 312 return 0; // remove the callback 313 } 314 315 connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex); 316 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { 317 LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " 318 "events=0x%x", connection->getInputChannelName(), events); 319 return 0; // remove the callback 320 } 321 322 if (! (events & ALOOPER_EVENT_INPUT)) { 323 LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " 324 "events=0x%x", connection->getInputChannelName(), events); 325 return 1; 326 } 327 328 status_t status = connection->inputConsumer.receiveDispatchSignal(); 329 if (status) { 330 LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", 331 connection->getInputChannelName(), status); 332 return 0; // remove the callback 333 } 334 335 if (connection->messageInProgress) { 336 LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.", 337 connection->getInputChannelName()); 338 return 1; 339 } 340 341 status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent); 342 if (status) { 343 LOGW("channel '%s' ~ Failed to consume input event. status=%d", 344 connection->getInputChannelName(), status); 345 connection->inputConsumer.sendFinishedSignal(false); 346 return 1; 347 } 348 349 connection->messageInProgress = true; 350 connection->messageSeqNum += 1; 351 352 finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum); 353 354 inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal); 355 } // release lock 356 357 // Invoke the handler outside of the lock. 358 // 359 // Note: inputEvent is stored in a field of the connection object which could potentially 360 // become disposed due to the input channel being unregistered concurrently. 361 // For this reason, we explicitly keep the connection object alive by holding 362 // a strong pointer to it within this scope. We also grabbed a local reference to 363 // the input handler object itself for the same reason. 364 365 int32_t inputEventType = inputEvent->getType(); 366 367 jobject inputEventObj; 368 jmethodID dispatchMethodId; 369 switch (inputEventType) { 370 case AINPUT_EVENT_TYPE_KEY: 371 #if DEBUG_DISPATCH_CYCLE 372 LOGD("channel '%s' ~ Received key event.", connection->getInputChannelName()); 373 #endif 374 inputEventObj = android_view_KeyEvent_fromNative(env, 375 static_cast<KeyEvent*>(inputEvent)); 376 dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent; 377 break; 378 379 case AINPUT_EVENT_TYPE_MOTION: 380 #if DEBUG_DISPATCH_CYCLE 381 LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName()); 382 #endif 383 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, 384 static_cast<MotionEvent*>(inputEvent)); 385 dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent; 386 break; 387 388 default: 389 assert(false); // InputConsumer should prevent this from ever happening 390 inputEventObj = NULL; 391 } 392 393 if (! inputEventObj) { 394 LOGW("channel '%s' ~ Failed to obtain DVM event object.", 395 connection->getInputChannelName()); 396 env->DeleteLocalRef(inputHandlerObjLocal); 397 q->finished(env, finishedToken, false, false); 398 return 1; 399 } 400 401 #if DEBUG_DISPATCH_CYCLE 402 LOGD("Invoking input handler."); 403 #endif 404 env->CallStaticVoidMethod(gInputQueueClassInfo.clazz, 405 dispatchMethodId, inputHandlerObjLocal, inputEventObj, 406 jlong(finishedToken)); 407 #if DEBUG_DISPATCH_CYCLE 408 LOGD("Returned from input handler."); 409 #endif 410 411 if (env->ExceptionCheck()) { 412 LOGE("An exception occurred while invoking the input handler for an event."); 413 LOGE_EX(env); 414 env->ExceptionClear(); 415 416 q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/); 417 } 418 419 env->DeleteLocalRef(inputEventObj); 420 env->DeleteLocalRef(inputHandlerObjLocal); 421 return 1; 422 } 423 424 jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId, 425 uint16_t messageSeqNum) { 426 return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum); 427 } 428 429 void NativeInputQueue::parseFinishedToken(jlong finishedToken, 430 int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) { 431 *outReceiveFd = int32_t(finishedToken >> 32); 432 *outConnectionId = uint16_t(finishedToken >> 16); 433 *outMessageIndex = uint16_t(finishedToken); 434 } 435 436 // ---------------------------------------------------------------------------- 437 438 NativeInputQueue::Connection::Connection(uint16_t id, 439 const sp<InputChannel>& inputChannel, const sp<Looper>& looper) : 440 id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel), 441 looper(looper), inputHandlerObjGlobal(NULL), 442 messageSeqNum(0), messageInProgress(false) { 443 } 444 445 NativeInputQueue::Connection::~Connection() { 446 } 447 448 // ---------------------------------------------------------------------------- 449 450 static NativeInputQueue gNativeInputQueue; 451 452 static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz, 453 jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) { 454 status_t status = gNativeInputQueue.registerInputChannel( 455 env, inputChannelObj, inputHandlerObj, messageQueueObj); 456 457 if (status) { 458 String8 message; 459 message.appendFormat("Failed to register input channel. status=%d", status); 460 jniThrowRuntimeException(env, message.string()); 461 } 462 } 463 464 static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz, 465 jobject inputChannelObj) { 466 status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj); 467 468 if (status) { 469 String8 message; 470 message.appendFormat("Failed to unregister input channel. status=%d", status); 471 jniThrowRuntimeException(env, message.string()); 472 } 473 } 474 475 static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz, 476 jlong finishedToken, bool handled) { 477 status_t status = gNativeInputQueue.finished( 478 env, finishedToken, handled, false /*ignoreSpuriousFinish*/); 479 480 // We ignore the case where an event could not be finished because the input channel 481 // was no longer registered (DEAD_OBJECT) since it is a common race that can occur 482 // during application shutdown. The input dispatcher recovers gracefully anyways. 483 if (status != OK && status != DEAD_OBJECT) { 484 String8 message; 485 message.appendFormat("Failed to finish input event. status=%d", status); 486 jniThrowRuntimeException(env, message.string()); 487 } 488 } 489 490 // ---------------------------------------------------------------------------- 491 492 static JNINativeMethod gInputQueueMethods[] = { 493 /* name, signature, funcPtr */ 494 { "nativeRegisterInputChannel", 495 "(Landroid/view/InputChannel;Landroid/view/InputHandler;Landroid/os/MessageQueue;)V", 496 (void*)android_view_InputQueue_nativeRegisterInputChannel }, 497 { "nativeUnregisterInputChannel", 498 "(Landroid/view/InputChannel;)V", 499 (void*)android_view_InputQueue_nativeUnregisterInputChannel }, 500 { "nativeFinished", "(JZ)V", 501 (void*)android_view_InputQueue_nativeFinished } 502 }; 503 504 #define FIND_CLASS(var, className) \ 505 var = env->FindClass(className); \ 506 LOG_FATAL_IF(! var, "Unable to find class " className); \ 507 var = jclass(env->NewGlobalRef(var)); 508 509 #define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \ 510 var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \ 511 LOG_FATAL_IF(! var, "Unable to find static method " methodName); 512 513 int register_android_view_InputQueue(JNIEnv* env) { 514 int res = jniRegisterNativeMethods(env, "android/view/InputQueue", 515 gInputQueueMethods, NELEM(gInputQueueMethods)); 516 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 517 518 FIND_CLASS(gInputQueueClassInfo.clazz, "android/view/InputQueue"); 519 520 GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchKeyEvent, gInputQueueClassInfo.clazz, 521 "dispatchKeyEvent", 522 "(Landroid/view/InputHandler;Landroid/view/KeyEvent;J)V"); 523 524 GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchMotionEvent, gInputQueueClassInfo.clazz, 525 "dispatchMotionEvent", 526 "(Landroid/view/InputHandler;Landroid/view/MotionEvent;J)V"); 527 return 0; 528 } 529 530 } // namespace android 531