From f4eef69721570d2f1eddaaa799f10c7785aafad3 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 21 May 2013 20:37:51 -0700 Subject: [PATCH] Add a queue of pending finished input events. Although in practice an application or IME is unlikely to fill up the input channel with finish events, it can happen when events are being delivered very rapidly. Handle this situation by queuing up the pending finish events until the socket becomes writable again. Bug: 9034301 Change-Id: I938a62a75d12106a19cff2d016ba7af0db877ecf --- core/jni/android_view_InputEventReceiver.cpp | 93 +++++++++++++++++--- 1 file changed, 82 insertions(+), 11 deletions(-) diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index c350521a01164..8e1e04abeca1f 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include "android_os_MessageQueue.h" @@ -61,11 +62,20 @@ protected: virtual ~NativeInputEventReceiver(); private: + struct Finish { + uint32_t seq; + bool handled; + }; + jobject mReceiverWeakGlobal; InputConsumer mInputConsumer; sp mMessageQueue; PreallocatedInputEventFactory mInputEventFactory; bool mBatchedInputEventPending; + int mFdEvents; + Vector mFinishQueue; + + void setFdEvents(int events); const char* getInputChannelName() { return mInputConsumer.getChannel()->getName().string(); @@ -80,7 +90,7 @@ NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, const sp& messageQueue) : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), - mBatchedInputEventPending(false) { + mBatchedInputEventPending(false), mFdEvents(0) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName()); #endif @@ -92,8 +102,7 @@ NativeInputEventReceiver::~NativeInputEventReceiver() { } status_t NativeInputEventReceiver::initialize() { - int receiveFd = mInputConsumer.getChannel()->getFd(); - mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); + setFdEvents(ALOOPER_EVENT_INPUT); return OK; } @@ -102,7 +111,7 @@ void NativeInputEventReceiver::dispose() { ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName()); #endif - mMessageQueue->getLooper()->removeFd(mInputConsumer.getChannel()->getFd()); + setFdEvents(0); } status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) { @@ -112,12 +121,38 @@ status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) status_t status = mInputConsumer.sendFinishedSignal(seq, handled); if (status) { + if (status == WOULD_BLOCK) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Could not send finished signal immediately. " + "Enqueued for later.", getInputChannelName()); +#endif + Finish finish; + finish.seq = seq; + finish.handled = handled; + mFinishQueue.add(finish); + if (mFinishQueue.size() == 1) { + setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT); + } + return OK; + } ALOGW("Failed to send finished signal on channel '%s'. status=%d", getInputChannelName(), status); } return status; } +void NativeInputEventReceiver::setFdEvents(int events) { + if (mFdEvents != events) { + mFdEvents = events; + int fd = mInputConsumer.getChannel()->getFd(); + if (events) { + mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL); + } else { + mMessageQueue->getLooper()->removeFd(fd); + } + } +} + int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { #if DEBUG_DISPATCH_CYCLE @@ -130,16 +165,52 @@ int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) return 0; // remove the callback } - if (!(events & ALOOPER_EVENT_INPUT)) { - ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", getInputChannelName(), events); + if (events & ALOOPER_EVENT_INPUT) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + status_t status = consumeEvents(env, false /*consumeBatches*/, -1); + mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); + return status == OK || status == NO_MEMORY ? 1 : 0; + } + + if (events & ALOOPER_EVENT_OUTPUT) { + for (size_t i = 0; i < mFinishQueue.size(); i++) { + const Finish& finish = mFinishQueue.itemAt(i); + status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled); + if (status) { + mFinishQueue.removeItemsAt(0, i); + + if (status == WOULD_BLOCK) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Sent %u queued finish events; %u left.", + getInputChannelName(), i, mFinishQueue.size()); +#endif + return 1; // keep the callback, try again later + } + + ALOGW("Failed to send finished signal on channel '%s'. status=%d", + getInputChannelName(), status); + if (status != DEAD_OBJECT) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + String8 message; + message.appendFormat("Failed to finish input event. status=%d", status); + jniThrowRuntimeException(env, message.string()); + mMessageQueue->raiseAndClearException(env, "finishInputEvent"); + } + return 0; // remove the callback + } + } +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Sent %u queued finish events; none left.", + getInputChannelName(), mFinishQueue.size()); +#endif + mFinishQueue.clear(); + setFdEvents(ALOOPER_EVENT_INPUT); return 1; } - JNIEnv* env = AndroidRuntime::getJNIEnv(); - status_t status = consumeEvents(env, false /*consumeBatches*/, -1); - mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); - return status == OK || status == NO_MEMORY ? 1 : 0; + ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " + "events=0x%x", getInputChannelName(), events); + return 1; } status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,