Loading core/java/android/view/InputEventReceiver.java +48 −9 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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. Loading Loading @@ -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"); } Loading @@ -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); Loading core/java/android/view/ViewRootImpl.java +10 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -891,8 +896,6 @@ public final class ViewRootImpl extends Handler implements ViewParent, } private void doTraversal() { doProcessInputEvents(); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } Loading Loading @@ -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; Loading core/jni/android_app_NativeActivity.cpp +10 −8 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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); Loading core/jni/android_view_InputEventReceiver.cpp +112 −80 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ static struct { jclass clazz; jmethodID dispatchInputEvent; jmethodID dispatchBatchedInputEventPending; } gInputEventReceiverClassInfo; Loading @@ -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: Loading @@ -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(); Loading @@ -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 Loading @@ -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); Loading @@ -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) { Loading @@ -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)); Loading @@ -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)); Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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) \ Loading @@ -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; } Loading include/android_runtime/android_app_NativeActivity.h +2 −2 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/view/InputEventReceiver.java +48 −9 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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. Loading Loading @@ -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"); } Loading @@ -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); Loading
core/java/android/view/ViewRootImpl.java +10 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -891,8 +896,6 @@ public final class ViewRootImpl extends Handler implements ViewParent, } private void doTraversal() { doProcessInputEvents(); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } Loading Loading @@ -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; Loading
core/jni/android_app_NativeActivity.cpp +10 −8 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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); Loading
core/jni/android_view_InputEventReceiver.cpp +112 −80 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ static struct { jclass clazz; jmethodID dispatchInputEvent; jmethodID dispatchBatchedInputEventPending; } gInputEventReceiverClassInfo; Loading @@ -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: Loading @@ -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(); Loading @@ -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 Loading @@ -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); Loading @@ -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) { Loading @@ -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)); Loading @@ -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)); Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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) \ Loading @@ -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; } Loading
include/android_runtime/android_app_NativeActivity.h +2 −2 Original line number Diff line number Diff line Loading @@ -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