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

Commit 684f4443 authored by Jeff Brown's avatar Jeff Brown Committed by Android Git Automerger
Browse files

am 072ec96a: Implement batching of input events on the consumer side.

* commit '072ec96a':
  Implement batching of input events on the consumer side.
parents eae4f928 072ec96a
Loading
Loading
Loading
Loading
+48 −9
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import dalvik.system.CloseGuard;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
import android.util.SparseIntArray;

/**
 * Provides a low-level mechanism for an application to receive input events.
@@ -38,13 +39,14 @@ public abstract class InputEventReceiver {
    private InputChannel mInputChannel;
    private MessageQueue mMessageQueue;

    // The sequence number of the event that is in progress.
    private int mEventSequenceNumberInProgress = -1;
    // Map from InputEvent sequence numbers to dispatcher sequence numbers.
    private final SparseIntArray mSeqMap = new SparseIntArray();

    private static native int nativeInit(InputEventReceiver receiver,
            InputChannel inputChannel, MessageQueue messageQueue);
    private static native void nativeDispose(int receiverPtr);
    private static native void nativeFinishInputEvent(int receiverPtr, boolean handled);
    private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled);
    private static native void nativeConsumeBatchedInputEvents(int receiverPtr);

    /**
     * Creates an input event receiver bound to the specified input channel.
@@ -103,13 +105,26 @@ public abstract class InputEventReceiver {
        finishInputEvent(event, false);
    }

    /**
     * Called when a batched input event is pending.
     *
     * The batched input event will continue to accumulate additional movement
     * samples until the recipient calls {@link #consumeBatchedInputEvents} or
     * an event is received that ends the batch and causes it to be consumed
     * immediately (such as a pointer up event).
     */
    public void onBatchedInputEventPending() {
        consumeBatchedInputEvents();
    }

    /**
     * Finishes an input event and indicates whether it was handled.
     * Must be called on the same Looper thread to which the receiver is attached.
     *
     * @param event The input event that was finished.
     * @param handled True if the event was handled.
     */
    public void finishInputEvent(InputEvent event, boolean handled) {
    public final void finishInputEvent(InputEvent event, boolean handled) {
        if (event == null) {
            throw new IllegalArgumentException("event must not be null");
        }
@@ -117,23 +132,47 @@ public abstract class InputEventReceiver {
            Log.w(TAG, "Attempted to finish an input event but the input event "
                    + "receiver has already been disposed.");
        } else {
            if (event.getSequenceNumber() != mEventSequenceNumberInProgress) {
            int index = mSeqMap.indexOfKey(event.getSequenceNumber());
            if (index < 0) {
                Log.w(TAG, "Attempted to finish an input event that is not in progress.");
            } else {
                mEventSequenceNumberInProgress = -1;
                nativeFinishInputEvent(mReceiverPtr, handled);
                int seq = mSeqMap.valueAt(index);
                mSeqMap.removeAt(index);
                nativeFinishInputEvent(mReceiverPtr, seq, handled);
            }
        }
        event.recycleIfNeededAfterDispatch();
    }

    /**
     * Consumes all pending batched input events.
     * Must be called on the same Looper thread to which the receiver is attached.
     *
     * This method forces all batched input events to be delivered immediately.
     * Should be called just before animating or drawing a new frame in the UI.
     */
    public final void consumeBatchedInputEvents() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to consume batched input events but the input event "
                    + "receiver has already been disposed.");
        } else {
            nativeConsumeBatchedInputEvents(mReceiverPtr);
        }
    }

    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchInputEvent(InputEvent event) {
        mEventSequenceNumberInProgress = event.getSequenceNumber();
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchBatchedInputEventPending() {
        onBatchedInputEventPending();
    }

    public static interface Factory {
        public InputEventReceiver createInputEventReceiver(
                InputChannel inputChannel, Looper looper);
+10 −2
Original line number Diff line number Diff line
@@ -851,6 +851,11 @@ public final class ViewRootImpl extends Handler implements ViewParent,

    @Override
    public void onDraw() {
        if (mInputEventReceiver != null) {
            mInputEventReceiver.consumeBatchedInputEvents();
        }
        doProcessInputEvents();

        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            doTraversal();
@@ -891,8 +896,6 @@ public final class ViewRootImpl extends Handler implements ViewParent,
    }

    private void doTraversal() {
        doProcessInputEvents();

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }
@@ -3929,6 +3932,11 @@ public final class ViewRootImpl extends Handler implements ViewParent,
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }

        @Override
        public void onBatchedInputEventPending() {
            mChoreographer.scheduleDraw();
        }
    }
    WindowInputEventReceiver mInputEventReceiver;

+10 −8
Original line number Diff line number Diff line
@@ -172,7 +172,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
            in_flight_event inflight;
            inflight.event = kevent;
            inflight.seq = -1;
            inflight.doFinish = false;
            inflight.finishSeq = 0;
            mInFlightEvents.push(inflight);
        }
        if (mFinishPreDispatches.size() > 0) {
@@ -201,19 +201,21 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
        }
    }

    uint32_t consumerSeq;
    InputEvent* myEvent = NULL;
    status_t res = mConsumer.consume(this, &myEvent);
    status_t res = mConsumer.consume(this, true /*consumeBatches*/, &consumerSeq, &myEvent);
    if (res != android::OK) {
        if (res != android::WOULD_BLOCK) {
            ALOGW("channel '%s' ~ Failed to consume input event.  status=%d",
                    mConsumer.getChannel()->getName().string(), res);
        mConsumer.sendFinishedSignal(false);
        }
        return -1;
    }

    in_flight_event inflight;
    inflight.event = myEvent;
    inflight.seq = -1;
    inflight.doFinish = true;
    inflight.finishSeq = consumerSeq;
    mInFlightEvents.push(inflight);

    *outEvent = myEvent;
@@ -255,8 +257,8 @@ void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultH
    for (size_t i=0; i<N; i++) {
        const in_flight_event& inflight(mInFlightEvents[i]);
        if (inflight.event == event) {
            if (inflight.doFinish) {
                int32_t res = mConsumer.sendFinishedSignal(handled);
            if (inflight.finishSeq) {
                status_t res = mConsumer.sendFinishedSignal(inflight.finishSeq, handled);
                if (res != android::OK) {
                    ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                            mConsumer.getChannel()->getName().string(), res);
+112 −80
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ static struct {
    jclass clazz;

    jmethodID dispatchInputEvent;
    jmethodID dispatchBatchedInputEventPending;
} gInputEventReceiverClassInfo;


@@ -50,7 +51,8 @@ public:
            const sp<Looper>& looper);

    status_t initialize();
    status_t finishInputEvent(bool handled);
    status_t finishInputEvent(uint32_t seq, bool handled);
    status_t consumeEvents(bool consumeBatches);
    static int handleReceiveCallback(int receiveFd, int events, void* data);

protected:
@@ -60,8 +62,8 @@ private:
    jobject mReceiverObjGlobal;
    InputConsumer mInputConsumer;
    sp<Looper> mLooper;
    bool mEventInProgress;
    PreallocatedInputEventFactory mInputEventFactory;
    bool mBatchedInputEventPending;

    const char* getInputChannelName() {
        return mInputConsumer.getChannel()->getName().string();
@@ -72,7 +74,8 @@ private:
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
        mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
        mInputConsumer(inputChannel), mLooper(looper), mEventInProgress(false) {
        mInputConsumer(inputChannel), mLooper(looper),
        mBatchedInputEventPending(false) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
#endif
@@ -84,9 +87,6 @@ NativeInputEventReceiver::~NativeInputEventReceiver() {
#endif

    mLooper->removeFd(mInputConsumer.getChannel()->getFd());
    if (mEventInProgress) {
        mInputConsumer.sendFinishedSignal(false); // ignoring result
    }

    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->DeleteGlobalRef(mReceiverObjGlobal);
@@ -98,23 +98,17 @@ status_t NativeInputEventReceiver::initialize() {
    return OK;
}

status_t NativeInputEventReceiver::finishInputEvent(bool handled) {
    if (mEventInProgress) {
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
#endif
        mEventInProgress = false;

        status_t status = mInputConsumer.sendFinishedSignal(handled);
    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    if (status) {
        ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                getInputChannelName(), status);
    }
    return status;
    } else {
        ALOGW("Ignoring attempt to finish input event while no event is in progress.");
        return OK;
    }
}

int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -132,28 +126,59 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v
        return 1;
    }

    if (r->mEventInProgress) {
        ALOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
                r->getInputChannelName());
        return 1;
    status_t status = r->consumeEvents(false /*consumeBatches*/);
    return status == OK || status == NO_MEMORY ? 1 : 0;
}

status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(),
            consumeBatches ? "true" : "false");
#endif

    if (consumeBatches) {
        mBatchedInputEventPending = false;
    }

    JNIEnv* env = AndroidRuntime::getJNIEnv();
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
    status_t status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, &seq, &inputEvent);
        if (status) {
        ALOGW("channel '%s' ~ Failed to consume input event.  status=%d",
                r->getInputChannelName(), status);
        r->mInputConsumer.sendFinishedSignal(false);
        return 1;
            if (status == WOULD_BLOCK) {
                if (mInputConsumer.hasPendingBatch() && !mBatchedInputEventPending) {
                    // There is a pending batch.  Come back later.
                    mBatchedInputEventPending = true;
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
                            getInputChannelName());
#endif
                    env->CallVoidMethod(mReceiverObjGlobal,
                            gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);

                    if (env->ExceptionCheck()) {
                        ALOGE("channel '%s' ~ An exception occurred while dispatching that "
                                "batched input events are pending.", getInputChannelName());
                        LOGE_EX(env);
                        env->ExceptionClear();
                        mBatchedInputEventPending = false; // try again later
                    }
                }
                return OK;
            }
            ALOGE("channel '%s' ~ Failed to consume input event.  status=%d",
                    getInputChannelName(), status);
            return status;
        }
        assert(inputEvent);

    JNIEnv* env = AndroidRuntime::getJNIEnv();
        jobject inputEventObj;
        switch (inputEvent->getType()) {
        case AINPUT_EVENT_TYPE_KEY:
#if DEBUG_DISPATCH_CYCLE
        ALOGD("channel '%s' ~ Received key event.",
                r->getInputChannelName());
            ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
#endif
            inputEventObj = android_view_KeyEvent_fromNative(env,
                    static_cast<KeyEvent*>(inputEvent));
@@ -161,8 +186,7 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v

        case AINPUT_EVENT_TYPE_MOTION:
#if DEBUG_DISPATCH_CYCLE
        ALOGD("channel '%s' ~ Received motion event.",
                r->getInputChannelName());
            ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
#endif
            inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
                    static_cast<MotionEvent*>(inputEvent));
@@ -174,37 +198,29 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v
        }

        if (!inputEventObj) {
        ALOGW("channel '%s' ~ Failed to obtain event object.",
                r->getInputChannelName());
        r->mInputConsumer.sendFinishedSignal(false);
        return 1;
            ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
            mInputConsumer.sendFinishedSignal(seq, false);
            return NO_MEMORY;
        }

    r->mEventInProgress = true;

#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Invoking input handler.", r->getInputChannelName());
#endif
    env->CallVoidMethod(r->mReceiverObjGlobal,
            gInputEventReceiverClassInfo.dispatchInputEvent, inputEventObj);
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Returned from input handler.", r->getInputChannelName());
        ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
#endif
        env->CallVoidMethod(mReceiverObjGlobal,
                gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

        env->DeleteLocalRef(inputEventObj);

        if (env->ExceptionCheck()) {
            ALOGE("channel '%s' ~ An exception occurred while dispatching an event.",
                r->getInputChannelName());
                    getInputChannelName());
            LOGE_EX(env);
            env->ExceptionClear();

        if (r->mEventInProgress) {
            r->mInputConsumer.sendFinishedSignal(false);
            r->mEventInProgress = false;
            mInputConsumer.sendFinishedSignal(seq, false);
            return OK;
        }
    }

    env->DeleteLocalRef(inputEventObj);
    return 1;
}


@@ -243,10 +259,11 @@ static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
    receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
}

static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jboolean handled) {
static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
        jint seq, jboolean handled) {
    sp<NativeInputEventReceiver> receiver =
            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->finishInputEvent(handled);
    status_t status = receiver->finishInputEvent(seq, handled);
    if (status) {
        String8 message;
        message.appendFormat("Failed to finish input event.  status=%d", status);
@@ -254,17 +271,29 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
    }
}

static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr) {
    sp<NativeInputEventReceiver> receiver =
            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->consumeEvents(true /*consumeBatches*/);
    if (status) {
        String8 message;
        message.appendFormat("Failed to consume batched input event.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
    }
}


static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit",
            "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
            (void*)nativeInit },
    { "nativeDispose",
            "(I)V",
    { "nativeDispose", "(I)V",
            (void*)nativeDispose },
    { "nativeFinishInputEvent", "(IZ)V",
            (void*)nativeFinishInputEvent }
    { "nativeFinishInputEvent", "(IIZ)V",
            (void*)nativeFinishInputEvent },
    { "nativeConsumeBatchedInputEvents", "(I)V",
            (void*)nativeConsumeBatchedInputEvents },
};

#define FIND_CLASS(var, className) \
@@ -285,7 +314,10 @@ int register_android_view_InputEventReceiver(JNIEnv* env) {

    GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
            gInputEventReceiverClassInfo.clazz,
            "dispatchInputEvent", "(Landroid/view/InputEvent;)V");
            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
            gInputEventReceiverClassInfo.clazz,
            "dispatchBatchedInputEventPending", "()V");
    return 0;
}

+2 −2
Original line number Diff line number Diff line
@@ -114,8 +114,8 @@ private:

    struct in_flight_event {
        android::InputEvent* event;
        int seq;
        bool doFinish;
        int seq; // internal sequence number for synthetic pre-dispatch events
        uint32_t finishSeq; // sequence number for sendFinishedSignal, or 0 if finish not required
    };

    struct finish_pre_dispatch {
Loading