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

Commit 5cafb24b authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Process output events in a separate function

Currently, we are processing input events and output events inside
'handleEvent' looper callback. That function is quite large, and will
get more complex when we add another outgoing event type.

To simplify the code, move the outbound event processing into a separate
function. At the same time, slightly modify the logic to make it clear
that the goal of the loop is to send all of the outgoing events.

Bug: 169866723
Test: presubmit
Change-Id: I47b4b82087428a9a6d4c50caf985a8699de3cb86
parent b5b5aa24
Loading
Loading
Loading
Loading
+82 −41
Original line number Diff line number Diff line
@@ -45,6 +45,12 @@ static const char* toString(bool value) {
    return value ? "true" : "false";
}

enum class HandleEventResponse : int {
    // Allowed return values of 'handleEvent' function as documented in LooperCallback::handleEvent
    REMOVE_CALLBACK = 0,
    KEEP_CALLBACK = 1
};

static struct {
    jclass clazz;

@@ -70,6 +76,14 @@ static std::string addPrefix(std::string str, std::string_view prefix) {
    return str;
}

/**
 * Convert an enumeration to its underlying type. Replace with std::to_underlying when available.
 */
template <class T>
static std::underlying_type_t<T> toUnderlying(const T& t) {
    return static_cast<std::underlying_type_t<T>>(t);
}

class NativeInputEventReceiver : public LooperCallback {
public:
    NativeInputEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -106,9 +120,16 @@ private:
        return mInputConsumer.getChannel()->getName();
    }

    virtual int handleEvent(int receiveFd, int events, void* data) override;
    HandleEventResponse processOutboundEvents();
    // From 'LooperCallback'
    int handleEvent(int receiveFd, int events, void* data) override;
};

// Ensure HandleEventResponse underlying type matches the return type of LooperCallback::handleEvent
static_assert(std::is_same<std::underlying_type_t<HandleEventResponse>,
                           std::invoke_result_t<decltype(&LooperCallback::handleEvent),
                                                NativeInputEventReceiver, int, int, void*>>::value);

NativeInputEventReceiver::NativeInputEventReceiver(
        JNIEnv* env, jobject receiverWeak, const std::shared_ptr<InputChannel>& inputChannel,
        const sp<MessageQueue>& messageQueue)
@@ -179,68 +200,88 @@ void NativeInputEventReceiver::setFdEvents(int events) {
    }
}

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    // Allowed return values of this function as documented in LooperCallback::handleEvent
    constexpr int REMOVE_CALLBACK = 0;
    constexpr int KEEP_CALLBACK = 1;
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        // This error typically occurs when the publisher has closed the input channel
        // as part of removing a window or finishing an IME session, in which case
        // the consumer will soon be disposed as well.
        if (kDebugDispatchCycle) {
            ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
                    "events=0x%x", getInputChannelName().c_str(), events);
        }
        return REMOVE_CALLBACK;
    }

    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
    }

    if (events & ALOOPER_EVENT_OUTPUT) {
        for (size_t i = 0; i < mFinishQueue.size(); i++) {
            const Finish& finish = mFinishQueue[i];
/**
 * Receiver's primary role is to receive input events, but it has an additional duty of sending
 * 'ack' for events (using the call 'finishInputEvent').
 *
 * If we are looking at the communication between InputPublisher and InputConsumer, we can say that
 * from the InputConsumer's perspective, InputMessage's that are sent from publisher to consumer are
 * called 'inbound / incoming' events, and the InputMessage's sent from InputConsumer to
 * InputPublisher are 'outbound / outgoing' events.
 *
 * NativeInputEventReceiver owns (and acts like) an InputConsumer. So the finish events are outbound
 * from InputEventReceiver (and will be sent to the InputPublisher).
 *
 * In this function, send as many events from 'mFinishQueue' as possible across the socket to the
 * InputPublisher. If no events are remaining, let the looper know so that it doesn't wake up
 * unnecessarily.
 */
HandleEventResponse NativeInputEventReceiver::processOutboundEvents() {
    while (!mFinishQueue.empty()) {
        const Finish& finish = *mFinishQueue.begin();
        status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
            if (status != OK) {
                mFinishQueue.erase(mFinishQueue.begin(), mFinishQueue.begin() + i);
        if (status == OK) {
            // Successful send. Erase the entry and keep trying to send more
            mFinishQueue.erase(mFinishQueue.begin());
            continue;
        }

        // Publisher is busy, try again later. Keep this entry (do not erase)
        if (status == WOULD_BLOCK) {
            if (kDebugDispatchCycle) {
                        ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
                              getInputChannelName().c_str(), i, mFinishQueue.size());
                ALOGD("channel '%s' ~ Remaining outbound events: %zu.",
                      getInputChannelName().c_str(), mFinishQueue.size());
            }
                    return KEEP_CALLBACK; // try again later
            return HandleEventResponse::KEEP_CALLBACK; // try again later
        }

                ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
        // Some other error. Give up
        ALOGW("Failed to send outbound event on channel '%s'.  status=%d",
              getInputChannelName().c_str(), status);
        if (status != DEAD_OBJECT) {
            JNIEnv* env = AndroidRuntime::getJNIEnv();
            std::string message =
                            android::base::StringPrintf("Failed to finish input event.  status=%d",
                    android::base::StringPrintf("Failed to send outbound event.  status=%d",
                                                status);
            jniThrowRuntimeException(env, message.c_str());
            mMessageQueue->raiseAndClearException(env, "finishInputEvent");
        }
                return REMOVE_CALLBACK;
        return HandleEventResponse::REMOVE_CALLBACK;
    }

    // The queue is now empty. Tell looper there's no more output to expect.
    setFdEvents(ALOOPER_EVENT_INPUT);
    return HandleEventResponse::KEEP_CALLBACK;
}

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
        // This error typically occurs when the publisher has closed the input channel
        // as part of removing a window or finishing an IME session, in which case
        // the consumer will soon be disposed as well.
        if (kDebugDispatchCycle) {
            ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
                    getInputChannelName().c_str(), mFinishQueue.size());
            ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
                    "events=0x%x", getInputChannelName().c_str(), events);
        }
        mFinishQueue.clear();
        setFdEvents(ALOOPER_EVENT_INPUT);
        return KEEP_CALLBACK;
        return toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
    }

    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY
                ? toUnderlying(HandleEventResponse::KEEP_CALLBACK)
                : toUnderlying(HandleEventResponse::REMOVE_CALLBACK);
    }

    if (events & ALOOPER_EVENT_OUTPUT) {
        return toUnderlying(processOutboundEvents());
    }

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

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,