Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56513492 authored by Jeff Brown's avatar Jeff Brown
Browse files

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
parent 4c32885c
Loading
Loading
Loading
Loading
+82 −11
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/Vector.h>
#include <utils/threads.h>
#include <androidfw/InputTransport.h>
#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<MessageQueue> mMessageQueue;
    PreallocatedInputEventFactory mInputEventFactory;
    bool mBatchedInputEventPending;
    int mFdEvents;
    Vector<Finish> mFinishQueue;

    void setFdEvents(int events);

    const char* getInputChannelName() {
        return mInputConsumer.getChannel()->getName().string();
@@ -80,7 +90,7 @@ NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        const sp<MessageQueue>& 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,18 +165,54 @@ 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);
        return 1;
    }

    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;
    }

    ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
            "events=0x%x", getInputChannelName(), events);
    return 1;
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime) {
#if DEBUG_DISPATCH_CYCLE