summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/InputEventReceiver.java57
-rw-r--r--core/java/android/view/ViewRootImpl.java12
-rw-r--r--core/jni/android_app_NativeActivity.cpp18
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp192
-rw-r--r--include/android_runtime/android_app_NativeActivity.h4
-rw-r--r--include/ui/InputTransport.h59
-rw-r--r--libs/ui/InputTransport.cpp314
-rw-r--r--libs/ui/tests/InputChannel_test.cpp3
-rw-r--r--libs/ui/tests/InputPublisherAndConsumer_test.cpp32
-rw-r--r--services/input/InputDispatcher.cpp57
-rw-r--r--services/input/InputDispatcher.h15
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(