diff options
-rw-r--r-- | core/java/android/view/InputEventReceiver.java | 57 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 12 | ||||
-rw-r--r-- | core/jni/android_app_NativeActivity.cpp | 18 | ||||
-rw-r--r-- | core/jni/android_view_InputEventReceiver.cpp | 192 | ||||
-rw-r--r-- | include/android_runtime/android_app_NativeActivity.h | 4 | ||||
-rw-r--r-- | include/ui/InputTransport.h | 59 | ||||
-rw-r--r-- | libs/ui/InputTransport.cpp | 314 | ||||
-rw-r--r-- | libs/ui/tests/InputChannel_test.cpp | 3 | ||||
-rw-r--r-- | libs/ui/tests/InputPublisherAndConsumer_test.cpp | 32 | ||||
-rw-r--r-- | services/input/InputDispatcher.cpp | 57 | ||||
-rw-r--r-- | services/input/InputDispatcher.h | 15 |
11 files changed, 557 insertions, 206 deletions
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 764d8dc..6a457ec 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -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. @@ -104,12 +106,25 @@ public abstract class InputEventReceiver { } /** + * 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); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 940d4c6..1930a5e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -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; diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index f383c5d..536681b 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -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) { - ALOGW("channel '%s' ~ Failed to consume input event. status=%d", - mConsumer.getChannel()->getName().string(), res); - mConsumer.sendFinishedSignal(false); + if (res != android::WOULD_BLOCK) { + ALOGW("channel '%s' ~ Failed to consume input event. status=%d", + mConsumer.getChannel()->getName().string(), res); + } 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); diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 23e1590..4b737ed 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -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()); + ALOGD("channel '%s' ~ Finished input event.", getInputChannelName()); #endif - mEventInProgress = false; - status_t status = mInputConsumer.sendFinishedSignal(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; + status_t status = mInputConsumer.sendFinishedSignal(seq, handled); + if (status) { + ALOGW("Failed to send finished signal on channel '%s'. status=%d", + getInputChannelName(), status); } + return status; } int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) { @@ -132,79 +126,101 @@ 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; +} - InputEvent* inputEvent; - status_t status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent); - if (status) { - ALOGW("channel '%s' ~ Failed to consume input event. status=%d", - r->getInputChannelName(), status); - r->mInputConsumer.sendFinishedSignal(false); - return 1; +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(); - jobject inputEventObj; - switch (inputEvent->getType()) { - case AINPUT_EVENT_TYPE_KEY: + for (;;) { + uint32_t seq; + InputEvent* inputEvent; + status_t status = mInputConsumer.consume(&mInputEventFactory, + consumeBatches, &seq, &inputEvent); + if (status) { + 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' ~ Received key event.", - r->getInputChannelName()); + ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", + getInputChannelName()); #endif - inputEventObj = android_view_KeyEvent_fromNative(env, - static_cast<KeyEvent*>(inputEvent)); - break; + 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); - case AINPUT_EVENT_TYPE_MOTION: + jobject inputEventObj; + switch (inputEvent->getType()) { + case AINPUT_EVENT_TYPE_KEY: #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Received motion event.", - r->getInputChannelName()); + ALOGD("channel '%s' ~ Received key event.", getInputChannelName()); #endif - inputEventObj = android_view_MotionEvent_obtainAsCopy(env, - static_cast<MotionEvent*>(inputEvent)); - break; + inputEventObj = android_view_KeyEvent_fromNative(env, + static_cast<KeyEvent*>(inputEvent)); + break; - default: - assert(false); // InputConsumer should prevent this from ever happening - inputEventObj = NULL; - } + case AINPUT_EVENT_TYPE_MOTION: +#if DEBUG_DISPATCH_CYCLE + ALOGD("channel '%s' ~ Received motion event.", getInputChannelName()); +#endif + inputEventObj = android_view_MotionEvent_obtainAsCopy(env, + static_cast<MotionEvent*>(inputEvent)); + break; - if (!inputEventObj) { - ALOGW("channel '%s' ~ Failed to obtain event object.", - r->getInputChannelName()); - r->mInputConsumer.sendFinishedSignal(false); - return 1; - } + default: + assert(false); // InputConsumer should prevent this from ever happening + inputEventObj = NULL; + } - r->mEventInProgress = true; + if (!inputEventObj) { + ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName()); + mInputConsumer.sendFinishedSignal(seq, false); + return NO_MEMORY; + } #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); - if (env->ExceptionCheck()) { - ALOGE("channel '%s' ~ An exception occurred while dispatching an event.", - r->getInputChannelName()); - LOGE_EX(env); - env->ExceptionClear(); + env->DeleteLocalRef(inputEventObj); - if (r->mEventInProgress) { - r->mInputConsumer.sendFinishedSignal(false); - r->mEventInProgress = false; + if (env->ExceptionCheck()) { + ALOGE("channel '%s' ~ An exception occurred while dispatching an event.", + getInputChannelName()); + LOGE_EX(env); + env->ExceptionClear(); + + 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; } diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h index 990143b..93fcf69 100644 --- a/include/android_runtime/android_app_NativeActivity.h +++ b/include/android_runtime/android_app_NativeActivity.h @@ -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 { diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index f3a39c3..bdd2fb9 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -52,6 +52,7 @@ struct InputMessage { union Body { struct Key { + uint32_t seq; nsecs_t eventTime; int32_t deviceId; int32_t source; @@ -69,6 +70,7 @@ struct InputMessage { } key; struct Motion { + uint32_t seq; nsecs_t eventTime; int32_t deviceId; int32_t source; @@ -95,6 +97,7 @@ struct InputMessage { } motion; struct Finished { + uint32_t seq; bool handled; inline size_t size() const { @@ -181,9 +184,11 @@ public: * Returns OK on success. * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ status_t publishKeyEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -200,10 +205,11 @@ public: * Returns OK on success. * Returns WOULD_BLOCK if the channel is full. * Returns DEAD_OBJECT if the channel's peer has been closed. - * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS. + * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -222,14 +228,17 @@ public: const PointerCoords* pointerCoords); /* Receives the finished signal from the consumer in reply to the original dispatch signal. - * Returns whether the consumer handled the message. + * If a signal was received, returns the message sequence number, + * and whether the consumer handled the message. + * + * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. * Returns WOULD_BLOCK if there is no signal present. * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t receiveFinishedSignal(bool* outHandled); + status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: sp<InputChannel> mChannel; @@ -252,24 +261,60 @@ public: /* Consumes an input event from the input channel and copies its contents into * an InputEvent object created using the specified factory. * + * Tries to combine a series of move events into larger batches whenever possible. + * + * If consumeBatches is false, then defers consuming pending batched events if it + * is possible for additional samples to be added to them later. Call hasPendingBatch() + * to determine whether a pending batch is available to be consumed. + * + * If consumeBatches is true, then events are still batched but they are consumed + * immediately as soon as the input channel is exhausted. + * + * The returned sequence number is never 0 unless the operation failed. + * * Returns OK on success. * Returns WOULD_BLOCK if there is no event present. * Returns DEAD_OBJECT if the channel's peer has been closed. * Returns NO_MEMORY if the event could not be created. * Other errors probably indicate that the channel is broken. */ - status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent); + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, + uint32_t* outSeq, InputEvent** outEvent); - /* Sends a finished signal to the publisher to inform it that the current message is - * finished processing and specifies whether the message was handled by the consumer. + /* Sends a finished signal to the publisher to inform it that the message + * with the specified sequence number has finished being process and whether + * the message was handled by the consumer. * * Returns OK on success. + * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t sendFinishedSignal(bool handled); + status_t sendFinishedSignal(uint32_t seq, bool handled); + + /* Returns true if there is a pending batch. */ + bool hasPendingBatch() const; private: sp<InputChannel> mChannel; + + // State about an event that consume would have returned except that it had to + // return a completed batch first. Sequence number is non-zero if an event was deferred. + uint32_t mDeferredEventSeq; + MotionEvent mDeferredEvent; + + // Batched motion events per device and source. + struct Batch { + uint32_t seq; + MotionEvent event; + }; + Vector<Batch> mBatches; + + ssize_t findBatch(int32_t deviceId, int32_t source) const; + + static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); + static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static bool canAppendSamples(const MotionEvent* event, const InputMessage* msg); + static void appendSamples(MotionEvent* event, const InputMessage* msg); }; } // namespace android diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index f7bcb68..98c4bdd 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -13,8 +13,8 @@ // Log debug messages whenever InputChannel objects are created/destroyed #define DEBUG_CHANNEL_LIFECYCLE 0 -#define DEBUG_TRANSPORT_ACTIONS 0 // Log debug messages about transport actions +#define DEBUG_TRANSPORT_ACTIONS 0 #include <cutils/log.h> @@ -203,6 +203,7 @@ InputPublisher::~InputPublisher() { } status_t InputPublisher::publishKeyEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -214,16 +215,22 @@ status_t InputPublisher::publishKeyEvent( nsecs_t downTime, nsecs_t eventTime) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," "downTime=%lld, eventTime=%lld", - mChannel->getName().string(), + mChannel->getName().string(), seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); #endif + if (!seq) { + ALOGE("Attempted to publish a key event with sequence number 0."); + return BAD_VALUE; + } + InputMessage msg; msg.header.type = InputMessage::TYPE_KEY; + msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.action = action; @@ -238,6 +245,7 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -255,16 +263,21 @@ status_t InputPublisher::publishMotionEvent( const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " "xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " "pointerCount=%d", - mChannel->getName().string(), + mChannel->getName().string(), seq, deviceId, source, action, flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); #endif + if (!seq) { + ALOGE("Attempted to publish a motion event with sequence number 0."); + return BAD_VALUE; + } + if (pointerCount > MAX_POINTERS || pointerCount < 1) { ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", mChannel->getName().string(), pointerCount); @@ -273,6 +286,7 @@ status_t InputPublisher::publishMotionEvent( InputMessage msg; msg.header.type = InputMessage::TYPE_MOTION; + msg.body.motion.seq = seq; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.action = action; @@ -294,7 +308,7 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } -status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { +status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().string()); @@ -303,6 +317,7 @@ status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { InputMessage msg; status_t result = mChannel->receiveMessage(&msg); if (result) { + *outSeq = 0; *outHandled = false; return result; } @@ -311,6 +326,7 @@ status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { mChannel->getName().string(), msg.header.type); return UNKNOWN_ERROR; } + *outSeq = msg.body.finished.seq; *outHandled = msg.body.finished.handled; return OK; } @@ -318,98 +334,256 @@ status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { // --- InputConsumer --- InputConsumer::InputConsumer(const sp<InputChannel>& channel) : - mChannel(channel) { + mChannel(channel), mDeferredEventSeq(0) { } InputConsumer::~InputConsumer() { } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, + bool consumeBatches, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consume", - mChannel->getName().string()); + ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s", + mChannel->getName().string(), consumeBatches ? "true" : "false"); #endif + *outSeq = 0; *outEvent = NULL; - InputMessage msg; - status_t result = mChannel->receiveMessage(&msg); - if (result) { - return result; - } + // Report deferred event first, if we had to end a batch earlier than we expected + // during the previous time consume was called. + if (mDeferredEventSeq) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; - switch (msg.header.type) { - case InputMessage::TYPE_KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (!keyEvent) return NO_MEMORY; - - keyEvent->initialize( - msg.body.key.deviceId, - msg.body.key.source, - msg.body.key.action, - msg.body.key.flags, - msg.body.key.keyCode, - msg.body.key.scanCode, - msg.body.key.metaState, - msg.body.key.repeatCount, - msg.body.key.downTime, - msg.body.key.eventTime); - *outEvent = keyEvent; - break; + motionEvent->copyFrom(&mDeferredEvent, true /*keepHistory*/); + *outSeq = mDeferredEventSeq; + *outEvent = motionEvent; + mDeferredEventSeq = 0; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed deferred event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + return OK; } - case AINPUT_EVENT_TYPE_MOTION: { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; + // Fetch the next input message. + // Loop until an event can be returned or no additional events are received. + while (!*outEvent) { + InputMessage msg; + status_t result = mChannel->receiveMessage(&msg); + if (result) { + // Consume the next batched event unless batches are being held for later. + if (!mBatches.isEmpty() && (consumeBatches || result != WOULD_BLOCK)) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + const Batch& batch = mBatches.top(); + motionEvent->copyFrom(&batch.event, true /*keepHistory*/); + *outSeq = batch.seq; + *outEvent = motionEvent; + mBatches.pop(); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + return result; + } - size_t pointerCount = msg.body.motion.pointerCount; - PointerProperties pointerProperties[pointerCount]; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(msg.body.motion.pointers[i].properties); - pointerCoords[i].copyFrom(msg.body.motion.pointers[i].coords); + switch (msg.header.type) { + case InputMessage::TYPE_KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; + + initializeKeyEvent(keyEvent, &msg); + *outSeq = msg.body.key.seq; + *outEvent = keyEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; } - motionEvent->initialize( - msg.body.motion.deviceId, - msg.body.motion.source, - msg.body.motion.action, - msg.body.motion.flags, - msg.body.motion.edgeFlags, - msg.body.motion.metaState, - msg.body.motion.buttonState, - msg.body.motion.xOffset, - msg.body.motion.yOffset, - msg.body.motion.xPrecision, - msg.body.motion.yPrecision, - msg.body.motion.downTime, - msg.body.motion.eventTime, - pointerCount, - pointerProperties, - pointerCoords); - *outEvent = motionEvent; - break; - } + case AINPUT_EVENT_TYPE_MOTION: { + ssize_t batchIndex = findBatch(msg.body.motion.deviceId, msg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches.editItemAt(batchIndex); + if (canAppendSamples(&batch.event, &msg)) { + // Send finished message for the earlier part of the batch. + // Claim that we handled the event. (The dispatcher doesn't care either + // way at the moment.) + status_t status = sendFinishedSignal(batch.seq, true); + if (status) { + return status; + } + + // Append to the batch and save the new sequence number for the tail end. + appendSamples(&batch.event, &msg); + batch.seq = msg.body.motion.seq; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ appended to batch event", + mChannel->getName().string()); +#endif + break; + } else { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new event until later. + mDeferredEventSeq = msg.body.motion.seq; + initializeMotionEvent(&mDeferredEvent, &msg); + + // Return the end of the previous batch. + motionEvent->copyFrom(&batch.event, true /*keepHistory*/); + *outSeq = batch.seq; + *outEvent = motionEvent; + mBatches.removeAt(batchIndex); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + } + + // Start a new batch if needed. + if (msg.body.motion.action == AMOTION_EVENT_ACTION_MOVE + || msg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mBatches.push(); + Batch& batch = mBatches.editTop(); + batch.seq = msg.body.motion.seq; + initializeMotionEvent(&batch.event, &msg); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ started batch event", + mChannel->getName().string()); +#endif + break; + } - default: - ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", - mChannel->getName().string(), msg.header.type); - return UNKNOWN_ERROR; - } + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + initializeMotionEvent(motionEvent, &msg); + *outSeq = msg.body.motion.seq; + *outEvent = motionEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + + default: + ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", + mChannel->getName().string(), msg.header.type); + return UNKNOWN_ERROR; + } + } return OK; } -status_t InputConsumer::sendFinishedSignal(bool handled) { +status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d", - mChannel->getName().string(), handled); + ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().string(), seq, handled ? "true" : "false"); #endif + if (!seq) { + ALOGE("Attempted to send a finished signal with sequence number 0."); + return BAD_VALUE; + } + InputMessage msg; msg.header.type = InputMessage::TYPE_FINISHED; + msg.body.finished.seq = seq; msg.body.finished.handled = handled; return mChannel->sendMessage(&msg); } +bool InputConsumer::hasPendingBatch() const { + return !mBatches.isEmpty(); +} + +ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mBatches.size(); i++) { + const Batch& batch = mBatches.itemAt(i); + if (batch.event.getDeviceId() == deviceId && batch.event.getSource() == source) { + return i; + } + } + return -1; +} + +void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { + event->initialize( + msg->body.key.deviceId, + msg->body.key.source, + msg->body.key.action, + msg->body.key.flags, + msg->body.key.keyCode, + msg->body.key.scanCode, + msg->body.key.metaState, + msg->body.key.repeatCount, + msg->body.key.downTime, + msg->body.key.eventTime); +} + +void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->initialize( + msg->body.motion.deviceId, + msg->body.motion.source, + msg->body.motion.action, + msg->body.motion.flags, + msg->body.motion.edgeFlags, + msg->body.motion.metaState, + msg->body.motion.buttonState, + msg->body.motion.xOffset, + msg->body.motion.yOffset, + msg->body.motion.xPrecision, + msg->body.motion.yPrecision, + msg->body.motion.downTime, + msg->body.motion.eventTime, + pointerCount, + pointerProperties, + pointerCoords); +} + +bool InputConsumer::canAppendSamples(const MotionEvent* event, const InputMessage *msg) { + size_t pointerCount = msg->body.motion.pointerCount; + if (event->getPointerCount() != pointerCount + || event->getAction() != msg->body.motion.action) { + return false; + } + for (size_t i = 0; i < pointerCount; i++) { + if (*event->getPointerProperties(i) != msg->body.motion.pointers[i].properties) { + return false; + } + } + return true; +} + +void InputConsumer::appendSamples(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->setMetaState(event->getMetaState() | msg->body.motion.metaState); + event->addSample(msg->body.motion.eventTime, pointerCoords); +} + } // namespace android diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp index c73dc2f..ee422fe 100644 --- a/libs/ui/tests/InputChannel_test.cpp +++ b/libs/ui/tests/InputChannel_test.cpp @@ -90,6 +90,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { InputMessage clientReply; memset(&clientReply, 0, sizeof(InputMessage)); clientReply.header.type = InputMessage::TYPE_FINISHED; + clientReply.body.finished.seq = 0x11223344; clientReply.body.finished.handled = true; EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) << "client channel should be able to send message to server channel"; @@ -99,6 +100,8 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "server channel should be able to receive message from client channel"; EXPECT_EQ(clientReply.header.type, serverReply.header.type) << "server channel should receive the correct message from client channel"; + EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) + << "server channel should receive the correct message from client channel"; EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) << "server channel should receive the correct message from client channel"; } diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index a0ee94b..3303053 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -69,6 +69,7 @@ TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; + const uint32_t seq = 15; const int32_t deviceId = 1; const int32_t source = AINPUT_SOURCE_KEYBOARD; const int32_t action = AKEY_EVENT_ACTION_DOWN; @@ -80,13 +81,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { const nsecs_t downTime = 3; const nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(deviceId, source, action, flags, + status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(& mEventFactory, & event); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; @@ -96,6 +98,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { << "consumer should have returned a key event"; KeyEvent* keyEvent = static_cast<KeyEvent*>(event); + EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(action, keyEvent->getAction()); @@ -107,14 +110,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(downTime, keyEvent->getDownTime()); EXPECT_EQ(eventTime, keyEvent->getEventTime()); - status = mConsumer->sendFinishedSignal(true); + status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&handled); + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } @@ -122,6 +128,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; + const uint32_t seq = 15; const int32_t deviceId = 1; const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; const int32_t action = AMOTION_EVENT_ACTION_MOVE; @@ -155,15 +162,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags, + status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; + uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(& mEventFactory, & event); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; @@ -173,6 +181,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { << "consumer should have returned a motion event"; MotionEvent* motionEvent = static_cast<MotionEvent*>(event); + EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(action, motionEvent->getAction()); @@ -216,14 +225,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { motionEvent->getOrientation(i)); } - status = mConsumer->sendFinishedSignal(false); + status = mConsumer->sendFinishedSignal(seq, false); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + uint32_t finishedSeq = 0; bool handled = true; - status = mPublisher->receiveFinishedSignal(&handled); + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_FALSE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } @@ -242,7 +254,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; @@ -258,7 +270,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreater pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index a63b6f5..d04aa68 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -1872,7 +1872,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); // Publish the key event. - status = connection->inputPublisher.publishKeyEvent( + status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, @@ -1916,7 +1916,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Publish the motion event. - status = connection->inputPublisher.publishMotionEvent( + status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, @@ -1967,10 +1967,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, bool handled) { + const sp<Connection>& connection, uint32_t seq, bool handled) { #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ finishDispatchCycle - handled=%s", - connection->getInputChannelName(), toString(handled)); + ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", + connection->getInputChannelName(), seq, toString(handled)); #endif connection->inputPublisherBlocked = false; @@ -1981,7 +1981,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, } // Notify other system components and prepare to start the next dispatch cycle. - onDispatchCycleFinishedLocked(currentTime, connection, handled); + onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); } void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, @@ -2047,12 +2047,13 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { bool gotOne = false; status_t status; for (;;) { - bool handled = false; - status = connection->inputPublisher.receiveFinishedSignal(&handled); + uint32_t seq; + bool handled; + status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); if (status) { break; } - d->finishDispatchCycleLocked(currentTime, connection, handled); + d->finishDispatchCycleLocked(currentTime, connection, seq, handled); gotOne = true; } if (gotOne) { @@ -3171,10 +3172,11 @@ ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputC } void InputDispatcher::onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection, bool handled) { + nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; + commandEntry->seq = seq; commandEntry->handled = handled; } @@ -3268,12 +3270,13 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; + uint32_t seq = commandEntry->seq; bool handled = commandEntry->handled; - if (!connection->waitQueue.isEmpty()) { - // Handle post-event policy actions. + // Handle post-event policy actions. + DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq); + if (dispatchEntry) { bool restartEvent; - DispatchEntry* dispatchEntry = connection->waitQueue.head; if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); restartEvent = afterKeyEventLockedInterruptible(connection, @@ -3290,8 +3293,8 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( // Note that because the lock might have been released, it is possible that the // contents of the wait queue to have been drained, so we need to double-check // a few things. - if (connection->waitQueue.head == dispatchEntry) { - connection->waitQueue.dequeueAtHead(); + if (dispatchEntry == connection->findWaitQueueEntry(seq)) { + connection->waitQueue.dequeue(dispatchEntry); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.enqueueAtHead(dispatchEntry); } else { @@ -3635,8 +3638,11 @@ InputDispatcher::MotionEntry::~MotionEntry() { // --- InputDispatcher::DispatchEntry --- +volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic; + InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) : + seq(nextSeq()), eventEntry(eventEntry), targetFlags(targetFlags), xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor), resolvedAction(0), resolvedFlags(0) { @@ -3647,6 +3653,15 @@ InputDispatcher::DispatchEntry::~DispatchEntry() { eventEntry->release(); } +uint32_t InputDispatcher::DispatchEntry::nextSeq() { + // Sequence number 0 is reserved and will never be returned. + uint32_t seq; + do { + seq = android_atomic_inc(&sNextSeqAtomic); + } while (!seq); + return seq; +} + // --- InputDispatcher::InputState --- @@ -3999,11 +4014,21 @@ const char* InputDispatcher::Connection::getStatusLabel() const { } } +InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { + for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) { + if (entry->seq == seq) { + return entry; + } + } + return NULL; +} + // --- InputDispatcher::CommandEntry --- InputDispatcher::CommandEntry::CommandEntry(Command command) : - command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), handled(false) { + command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), + seq(0), handled(false) { } InputDispatcher::CommandEntry::~CommandEntry() { diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 1311465..03a824b 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -27,6 +27,7 @@ #include <utils/String8.h> #include <utils/Looper.h> #include <utils/BitSet.h> +#include <cutils/atomic.h> #include <stddef.h> #include <unistd.h> @@ -522,6 +523,8 @@ private: // Tracks the progress of dispatching a particular event to a particular connection. struct DispatchEntry : Link<DispatchEntry> { + const uint32_t seq; // unique sequence number, never 0 + EventEntry* eventEntry; // the event to dispatch int32_t targetFlags; float xOffset; @@ -543,6 +546,11 @@ private: inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + + private: + static volatile int32_t sNextSeqAtomic; + + static uint32_t nextSeq(); }; // A command entry captures state and behavior for an action to be performed in the @@ -578,6 +586,7 @@ private: sp<InputApplicationHandle> inputApplicationHandle; sp<InputWindowHandle> inputWindowHandle; int32_t userActivityEventType; + uint32_t seq; bool handled; }; @@ -797,6 +806,8 @@ private: inline const char* getInputChannelName() const { return inputChannel->getName().string(); } const char* getStatusLabel() const; + + DispatchEntry* findWaitQueueEntry(uint32_t seq); }; enum DropReason { @@ -1000,7 +1011,7 @@ private: EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode); void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - bool handled); + uint32_t seq, bool handled); void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool notify); void drainDispatchQueueLocked(Queue<DispatchEntry>* queue); @@ -1034,7 +1045,7 @@ private: // Interesting events that we might like to log or tell the framework about. void onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection, bool handled); + nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled); void onDispatchCycleBrokenLocked( nsecs_t currentTime, const sp<Connection>& connection); void onANRLocked( |