Merge "Move BroadcastQueue out of the ActivityManager class."
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 95e4447..1f738cd 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -20,17 +20,13 @@
 /**
  * Native input transport.
  *
- * Uses anonymous shared memory as a whiteboard for sending input events from an
- * InputPublisher to an InputConsumer and ensuring appropriate synchronization.
- * One interesting feature is that published events can be updated in place as long as they
- * have not yet been consumed.
+ * The InputChannel provides a mechanism for exchanging InputMessage structures across processes.
  *
- * The InputPublisher and InputConsumer only take care of transferring event data
- * over an InputChannel and sending synchronization signals.  The InputDispatcher and InputQueue
- * build on these abstractions to add multiplexing and queueing.
+ * The InputPublisher and InputConsumer each handle one end-point of an input channel.
+ * The InputPublisher is used by the input dispatcher to send events to the application.
+ * The InputConsumer is used by the application to receive events from the input dispatcher.
  */
 
-#include <semaphore.h>
 #include <ui/Input.h>
 #include <utils/Errors.h>
 #include <utils/Timers.h>
@@ -40,88 +36,26 @@
 namespace android {
 
 /*
- * An input channel consists of a shared memory buffer and a pair of pipes
- * used to send input messages from an InputPublisher to an InputConsumer
- * across processes.  Each channel has a descriptive name for debugging purposes.
- *
- * Each endpoint has its own InputChannel object that specifies its own file descriptors.
- *
- * The input channel is closed when all references to it are released.
- */
-class InputChannel : public RefBase {
-protected:
-    virtual ~InputChannel();
-
-public:
-    InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
-            int32_t sendPipeFd);
-
-    /* Creates a pair of input channels and their underlying shared memory buffers
-     * and pipes.
-     *
-     * Returns OK on success.
-     */
-    static status_t openInputChannelPair(const String8& name,
-            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
-
-    inline String8 getName() const { return mName; }
-    inline int32_t getAshmemFd() const { return mAshmemFd; }
-    inline int32_t getReceivePipeFd() const { return mReceivePipeFd; }
-    inline int32_t getSendPipeFd() const { return mSendPipeFd; }
-
-    /* Sends a signal to the other endpoint.
-     *
-     * Returns OK on success.
-     * Returns DEAD_OBJECT if the channel's peer has been closed.
-     * Other errors probably indicate that the channel is broken.
-     */
-    status_t sendSignal(char signal);
-
-    /* Receives a signal send by the other endpoint.
-     * (Should only call this after poll() indicates that the receivePipeFd has available input.)
-     *
-     * 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 receiveSignal(char* outSignal);
-
-private:
-    String8 mName;
-    int32_t mAshmemFd;
-    int32_t mReceivePipeFd;
-    int32_t mSendPipeFd;
-};
-
-/*
- * Private intermediate representation of input events as messages written into an
- * ashmem buffer.
+ * Intermediate representation used to send input events and related signals.
  */
 struct InputMessage {
-    /* Semaphore count is set to 1 when the message is published.
-     * It becomes 0 transiently while the publisher updates the message.
-     * It becomes 0 permanently when the consumer consumes the message.
-     */
-    sem_t semaphore;
-
-    /* Initialized to false by the publisher.
-     * Set to true by the consumer when it consumes the message.
-     */
-    bool consumed;
-
-    int32_t type;
-
-    struct SampleData {
-        nsecs_t eventTime;
-        PointerCoords coords[0]; // variable length
+    enum {
+        TYPE_KEY = 1,
+        TYPE_MOTION = 2,
+        TYPE_FINISHED = 3,
     };
 
-    int32_t deviceId;
-    int32_t source;
+    struct Header {
+        uint32_t type;
+        uint32_t padding; // 8 byte alignment for the body that follows
+    } header;
 
-    union {
-        struct {
+    union Body {
+        struct Key {
+            uint32_t seq;
+            nsecs_t eventTime;
+            int32_t deviceId;
+            int32_t source;
             int32_t action;
             int32_t flags;
             int32_t keyCode;
@@ -129,10 +63,17 @@
             int32_t metaState;
             int32_t repeatCount;
             nsecs_t downTime;
-            nsecs_t eventTime;
+
+            inline size_t size() const {
+                return sizeof(Key);
+            }
         } key;
 
-        struct {
+        struct Motion {
+            uint32_t seq;
+            nsecs_t eventTime;
+            int32_t deviceId;
+            int32_t source;
             int32_t action;
             int32_t flags;
             int32_t metaState;
@@ -144,28 +85,88 @@
             float xPrecision;
             float yPrecision;
             size_t pointerCount;
-            PointerProperties pointerProperties[MAX_POINTERS];
-            size_t sampleCount;
-            SampleData sampleData[0]; // variable length
+            struct Pointer {
+                PointerProperties properties;
+                PointerCoords coords;
+            } pointers[MAX_POINTERS];
+
+            inline size_t size() const {
+                return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS
+                        + sizeof(Pointer) * pointerCount;
+            }
         } motion;
-    };
 
-    /* Gets the number of bytes to add to step to the next SampleData object in a motion
-     * event message for a given number of pointers.
-     */
-    static inline size_t sampleDataStride(size_t pointerCount) {
-        return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
-    }
+        struct Finished {
+            uint32_t seq;
+            bool handled;
 
-    /* Adds the SampleData stride to the given pointer. */
-    static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) {
-        return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride);
-    }
+            inline size_t size() const {
+                return sizeof(Finished);
+            }
+        } finished;
+    } body;
+
+    bool isValid(size_t actualSize) const;
+    size_t size() const;
 };
 
 /*
- * Publishes input events to an anonymous shared memory buffer.
- * Uses atomic operations to coordinate shared access with a single concurrent consumer.
+ * An input channel consists of a local unix domain socket used to send and receive
+ * input messages across processes.  Each channel has a descriptive name for debugging purposes.
+ *
+ * Each endpoint has its own InputChannel object that specifies its file descriptor.
+ *
+ * The input channel is closed when all references to it are released.
+ */
+class InputChannel : public RefBase {
+protected:
+    virtual ~InputChannel();
+
+public:
+    InputChannel(const String8& name, int32_t fd);
+
+    /* Creates a pair of input channels.
+     *
+     * Returns OK on success.
+     */
+    static status_t openInputChannelPair(const String8& name,
+            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+
+    inline String8 getName() const { return mName; }
+    inline int32_t getFd() const { return mFd; }
+
+    /* Sends a message to the other endpoint.
+     *
+     * If the channel is full then the message is guaranteed not to have been sent at all.
+     * Try again after the consumer has sent a finished signal indicating that it has
+     * consumed some of the pending messages from the channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t sendMessage(const InputMessage* msg);
+
+    /* Receives a message sent by the other endpoint.
+     *
+     * If there is no message present, try again after poll() indicates that the fd
+     * is readable.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if there is no message present.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t receiveMessage(InputMessage* msg);
+
+private:
+    String8 mName;
+    int32_t mFd;
+};
+
+/*
+ * Publishes input events to an input channel.
  */
 class InputPublisher {
 public:
@@ -178,26 +179,16 @@
     /* Gets the underlying input channel. */
     inline sp<InputChannel> getChannel() { return mChannel; }
 
-    /* Prepares the publisher for use.  Must be called before it is used.
-     * Returns OK on success.
-     *
-     * This method implicitly calls reset(). */
-    status_t initialize();
-
-    /* Resets the publisher to its initial state and unpins its ashmem buffer.
-     * Returns OK on success.
-     *
-     * Should be called after an event has been consumed to release resources used by the
-     * publisher until the next event is ready to be published.
-     */
-    status_t reset();
-
-    /* Publishes a key event to the ashmem buffer.
+    /* Publishes a key event to the input channel.
      *
      * Returns OK on success.
-     * Returns INVALID_OPERATION if the publisher has not been reset.
+     * 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,
@@ -209,13 +200,16 @@
             nsecs_t downTime,
             nsecs_t eventTime);
 
-    /* Publishes a motion event to the ashmem buffer.
+    /* Publishes a motion event to the input channel.
      *
      * Returns OK on success.
-     * Returns INVALID_OPERATION if the publisher has not been reset.
-     * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS.
+     * 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 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,
@@ -233,55 +227,25 @@
             const PointerProperties* pointerProperties,
             const PointerCoords* pointerCoords);
 
-    /* Appends a motion sample to a motion event unless already consumed.
-     *
-     * Returns OK on success.
-     * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event.
-     * Returns FAILED_TRANSACTION if the current event has already been consumed.
-     * Returns NO_MEMORY if the buffer is full and no additional samples can be added.
-     */
-    status_t appendMotionSample(
-            nsecs_t eventTime,
-            const PointerCoords* pointerCoords);
-
-    /* Sends a dispatch signal to the consumer to inform it that a new message is available.
-     *
-     * Returns OK on success.
-     * Errors probably indicate that the channel is broken.
-     */
-    status_t sendDispatchSignal();
-
     /* 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;
-
-    size_t mAshmemSize;
-    InputMessage* mSharedMessage;
-    bool mPinned;
-    bool mSemaphoreInitialized;
-    bool mWasDispatched;
-
-    size_t mMotionEventPointerCount;
-    InputMessage::SampleData* mMotionEventSampleDataTail;
-    size_t mMotionEventSampleDataStride;
-
-    status_t publishInputEvent(
-            int32_t type,
-            int32_t deviceId,
-            int32_t source);
 };
 
 /*
- * Consumes input events from an anonymous shared memory buffer.
- * Uses atomic operations to coordinate shared access with a single concurrent publisher.
+ * Consumes input events from an input channel.
  */
 class InputConsumer {
 public:
@@ -294,43 +258,76 @@
     /* Gets the underlying input channel. */
     inline sp<InputChannel> getChannel() { return mChannel; }
 
-    /* Prepares the consumer for use.  Must be called before it is used. */
-    status_t initialize();
-
-    /* Consumes the input event in the buffer and copies its contents into
+    /* Consumes an input event from the input channel and copies its contents into
      * an InputEvent object created using the specified factory.
-     * This operation will block if the publisher is updating the event.
+     *
+     * 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 INVALID_OPERATION if there is no currently published event.
+     * 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.
-     */
-    status_t consume(InputEventFactoryInterface* factory, 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.
-     *
-     * Returns OK on success.
-     * Errors probably indicate that the channel is broken.
-     */
-    status_t sendFinishedSignal(bool handled);
-
-    /* Receives the dispatched signal from the publisher.
-     *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no signal present.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t receiveDispatchSignal();
+    status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
+            uint32_t* outSeq, InputEvent** outEvent);
+
+    /* 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(uint32_t seq, bool handled);
+
+    /* Returns true if there is a pending batch. */
+    bool hasPendingBatch() const;
 
 private:
     sp<InputChannel> mChannel;
 
-    size_t mAshmemSize;
-    InputMessage* mSharedMessage;
+    // The current input message.
+    InputMessage mMsg;
 
-    void populateKeyEvent(KeyEvent* keyEvent) const;
-    void populateMotionEvent(MotionEvent* motionEvent) const;
+    // True if mMsg contains a valid input message that was deferred from the previous
+    // call to consume and that still needs to be handled.
+    bool mMsgDeferred;
+
+    // Batched motion events per device and source.
+    struct Batch {
+        uint32_t seq; // sequence number of last input message batched in the event
+        MotionEvent event;
+    };
+    Vector<Batch> mBatches;
+
+    // Chain of batched sequence numbers.  When multiple input messages are combined into
+    // a batch, we append a record here that associates the last sequence number in the
+    // batch with the previous one.  When the finished signal is sent, we traverse the
+    // chain to individually finish all input messages that were part of the batch.
+    struct SeqChain {
+        uint32_t seq;   // sequence number of batched input message
+        uint32_t chain; // sequence number of previous batched input message
+    };
+    Vector<SeqChain> mSeqChains;
+
+    ssize_t findBatch(int32_t deviceId, int32_t source) const;
+    status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
+
+    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 09cbb31..ecb3fb5 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -7,325 +7,203 @@
 
 //#define LOG_NDEBUG 0
 
-// Log debug messages about channel signalling (send signal, receive signal)
-#define DEBUG_CHANNEL_SIGNALS 0
+// Log debug messages about channel messages (send message, receive message)
+#define DEBUG_CHANNEL_MESSAGES 0
 
 // Log debug messages whenever InputChannel objects are created/destroyed
 #define DEBUG_CHANNEL_LIFECYCLE 0
 
-// Log debug messages about transport actions (initialize, reset, publish, ...)
+// Log debug messages about transport actions
 #define DEBUG_TRANSPORT_ACTIONS 0
 
 
-#include <cutils/ashmem.h>
 #include <cutils/log.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <sys/mman.h>
 #include <ui/InputTransport.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
 
 namespace android {
 
-#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1))
-#define MIN_HISTORY_DEPTH 20
+// Socket buffer size.  The default is typically about 128KB, which is much larger than
+// we really need.  So we make it smaller.  It just needs to be big enough to hold
+// a few dozen large multi-finger motion events in the case where an application gets
+// behind processing touches.
+static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
 
-// Must be at least sizeof(InputMessage) + sufficient space for pointer data
-static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP(
-        sizeof(InputMessage) + MIN_HISTORY_DEPTH
-                * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)),
-        4096);
 
-// Signal sent by the producer to the consumer to inform it that a new message is
-// available to be consumed in the shared memory buffer.
-static const char INPUT_SIGNAL_DISPATCH = 'D';
+// --- InputMessage ---
 
-// Signal sent by the consumer to the producer to inform it that it has finished
-// consuming the most recent message and it handled it.
-static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f';
+bool InputMessage::isValid(size_t actualSize) const {
+    if (size() == actualSize) {
+        switch (header.type) {
+        case TYPE_KEY:
+            return true;
+        case TYPE_MOTION:
+            return body.motion.pointerCount > 0
+                    && body.motion.pointerCount <= MAX_POINTERS;
+        case TYPE_FINISHED:
+            return true;
+        }
+    }
+    return false;
+}
 
-// Signal sent by the consumer to the producer to inform it that it has finished
-// consuming the most recent message but it did not handle it.
-static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u';
+size_t InputMessage::size() const {
+    switch (header.type) {
+    case TYPE_KEY:
+        return sizeof(Header) + body.key.size();
+    case TYPE_MOTION:
+        return sizeof(Header) + body.motion.size();
+    case TYPE_FINISHED:
+        return sizeof(Header) + body.finished.size();
+    }
+    return sizeof(Header);
+}
 
 
 // --- InputChannel ---
 
-InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
-        int32_t sendPipeFd) :
-        mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
+InputChannel::InputChannel(const String8& name, int fd) :
+        mName(name), mFd(fd) {
 #if DEBUG_CHANNEL_LIFECYCLE
-    ALOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
-            mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
+    ALOGD("Input channel constructed: name='%s', fd=%d",
+            mName.string(), fd);
 #endif
 
-    int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe "
-            "non-blocking.  errno=%d", mName.string(), errno);
-
-    result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe "
+    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
+    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "
             "non-blocking.  errno=%d", mName.string(), errno);
 }
 
 InputChannel::~InputChannel() {
 #if DEBUG_CHANNEL_LIFECYCLE
-    ALOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
-            mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
+    ALOGD("Input channel destroyed: name='%s', fd=%d",
+            mName.string(), mFd);
 #endif
 
-    ::close(mAshmemFd);
-    ::close(mReceivePipeFd);
-    ::close(mSendPipeFd);
+    ::close(mFd);
 }
 
 status_t InputChannel::openInputChannelPair(const String8& name,
         sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
-    status_t result;
-
-    String8 ashmemName("InputChannel ");
-    ashmemName.append(name);
-    int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
-    if (serverAshmemFd < 0) {
-        result = -errno;
-        ALOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
+        status_t result = -errno;
+        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                 name.string(), errno);
-    } else {
-        result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
-        if (result < 0) {
-            ALOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.",
-                    name.string(), result, serverAshmemFd);
-        } else {
-            // Dup the file descriptor because the server and client input channel objects that
-            // are returned may have different lifetimes but they share the same shared memory region.
-            int clientAshmemFd;
-            clientAshmemFd = dup(serverAshmemFd);
-            if (clientAshmemFd < 0) {
-                result = -errno;
-                ALOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
-                        name.string(), errno);
-            } else {
-                int forward[2];
-                if (pipe(forward)) {
-                    result = -errno;
-                    ALOGE("channel '%s' ~ Could not create forward pipe.  errno=%d",
-                            name.string(), errno);
-                } else {
-                    int reverse[2];
-                    if (pipe(reverse)) {
-                        result = -errno;
-                        ALOGE("channel '%s' ~ Could not create reverse pipe.  errno=%d",
-                                name.string(), errno);
-                    } else {
-                        String8 serverChannelName = name;
-                        serverChannelName.append(" (server)");
-                        outServerChannel = new InputChannel(serverChannelName,
-                                serverAshmemFd, reverse[0], forward[1]);
-
-                        String8 clientChannelName = name;
-                        clientChannelName.append(" (client)");
-                        outClientChannel = new InputChannel(clientChannelName,
-                                clientAshmemFd, forward[0], reverse[1]);
-                        return OK;
-                    }
-                    ::close(forward[0]);
-                    ::close(forward[1]);
-                }
-                ::close(clientAshmemFd);
-            }
-        }
-        ::close(serverAshmemFd);
+        outServerChannel.clear();
+        outClientChannel.clear();
+        return result;
     }
 
-    outServerChannel.clear();
-    outClientChannel.clear();
-    return result;
+    int bufferSize = SOCKET_BUFFER_SIZE;
+    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
+    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
+    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
+    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
+
+    String8 serverChannelName = name;
+    serverChannelName.append(" (server)");
+    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
+
+    String8 clientChannelName = name;
+    clientChannelName.append(" (client)");
+    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
+    return OK;
 }
 
-status_t InputChannel::sendSignal(char signal) {
+status_t InputChannel::sendMessage(const InputMessage* msg) {
+    size_t msgLength = msg->size();
     ssize_t nWrite;
     do {
-        nWrite = ::write(mSendPipeFd, & signal, 1);
+        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
     } while (nWrite == -1 && errno == EINTR);
 
-    if (nWrite == 1) {
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
+    if (nWrite < 0) {
+        int error = errno;
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
+                msg->header.type, error);
 #endif
-        return OK;
+        if (error == EAGAIN || error == EWOULDBLOCK) {
+            return WOULD_BLOCK;
+        }
+        if (error == EPIPE || error == ENOTCONN) {
+            return DEAD_OBJECT;
+        }
+        return -error;
     }
 
-#if DEBUG_CHANNEL_SIGNALS
-    ALOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno);
-#endif
-    return -errno;
-}
-
-status_t InputChannel::receiveSignal(char* outSignal) {
-    ssize_t nRead;
-    do {
-        nRead = ::read(mReceivePipeFd, outSignal, 1);
-    } while (nRead == -1 && errno == EINTR);
-
-    if (nRead == 1) {
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
-#endif
-        return OK;
-    }
-
-    if (nRead == 0) { // check for EOF
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
+    if (size_t(nWrite) != msgLength) {
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
+                mName.string(), msg->header.type);
 #endif
         return DEAD_OBJECT;
     }
 
-    if (errno == EAGAIN) {
-#if DEBUG_CHANNEL_SIGNALS
-        ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
+#if DEBUG_CHANNEL_MESSAGES
+    ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
 #endif
-        return WOULD_BLOCK;
+    return OK;
+}
+
+status_t InputChannel::receiveMessage(InputMessage* msg) {
+    ssize_t nRead;
+    do {
+        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
+    } while (nRead == -1 && errno == EINTR);
+
+    if (nRead < 0) {
+        int error = errno;
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
+#endif
+        if (error == EAGAIN || error == EWOULDBLOCK) {
+            return WOULD_BLOCK;
+        }
+        if (error == EPIPE || error == ENOTCONN) {
+            return DEAD_OBJECT;
+        }
+        return -error;
     }
 
-#if DEBUG_CHANNEL_SIGNALS
-    ALOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
+    if (nRead == 0) { // check for EOF
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
 #endif
-    return -errno;
+        return DEAD_OBJECT;
+    }
+
+    if (!msg->isValid(nRead)) {
+#if DEBUG_CHANNEL_MESSAGES
+        ALOGD("channel '%s' ~ received invalid message", mName.string());
+#endif
+        return BAD_VALUE;
+    }
+
+#if DEBUG_CHANNEL_MESSAGES
+    ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
+#endif
+    return OK;
 }
 
 
 // --- InputPublisher ---
 
 InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
-        mChannel(channel), mSharedMessage(NULL),
-        mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
-        mMotionEventSampleDataTail(NULL) {
+        mChannel(channel) {
 }
 
 InputPublisher::~InputPublisher() {
-    reset();
-
-    if (mSharedMessage) {
-        munmap(mSharedMessage, mAshmemSize);
-    }
-}
-
-status_t InputPublisher::initialize() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ initialize",
-            mChannel->getName().string());
-#endif
-
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_get_size_region(ashmemFd);
-    if (result < 0) {
-        ALOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-    mAshmemSize = (size_t) result;
-
-    mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
-    if (! mSharedMessage) {
-        ALOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.",
-                mChannel->getName().string(), ashmemFd);
-        return NO_MEMORY;
-    }
-
-    mPinned = true;
-    mSharedMessage->consumed = false;
-
-    return reset();
-}
-
-status_t InputPublisher::reset() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ reset",
-        mChannel->getName().string());
-#endif
-
-    if (mPinned) {
-        // Destroy the semaphore since we are about to unpin the memory region that contains it.
-        int result;
-        if (mSemaphoreInitialized) {
-            if (mSharedMessage->consumed) {
-                result = sem_post(& mSharedMessage->semaphore);
-                if (result < 0) {
-                    ALOGE("channel '%s' publisher ~ Error %d in sem_post.",
-                            mChannel->getName().string(), errno);
-                    return UNKNOWN_ERROR;
-                }
-            }
-
-            result = sem_destroy(& mSharedMessage->semaphore);
-            if (result < 0) {
-                ALOGE("channel '%s' publisher ~ Error %d in sem_destroy.",
-                        mChannel->getName().string(), errno);
-                return UNKNOWN_ERROR;
-            }
-
-            mSemaphoreInitialized = false;
-        }
-
-        // Unpin the region since we no longer care about its contents.
-        int ashmemFd = mChannel->getAshmemFd();
-        result = ashmem_unpin_region(ashmemFd, 0, 0);
-        if (result < 0) {
-            ALOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.",
-                    mChannel->getName().string(), result, ashmemFd);
-            return UNKNOWN_ERROR;
-        }
-
-        mPinned = false;
-    }
-
-    mMotionEventSampleDataTail = NULL;
-    mWasDispatched = false;
-    return OK;
-}
-
-status_t InputPublisher::publishInputEvent(
-        int32_t type,
-        int32_t deviceId,
-        int32_t source) {
-    if (mPinned) {
-        ALOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has "
-                "not yet been reset.", mChannel->getName().string());
-        return INVALID_OPERATION;
-    }
-
-    // Pin the region.
-    // We do not check for ASHMEM_NOT_PURGED because we don't care about the previous
-    // contents of the buffer so it does not matter whether it was purged in the meantime.
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_pin_region(ashmemFd, 0, 0);
-    if (result < 0) {
-        ALOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-
-    mPinned = true;
-
-    result = sem_init(& mSharedMessage->semaphore, 1, 1);
-    if (result < 0) {
-        ALOGE("channel '%s' publisher ~ Error %d in sem_init.",
-                mChannel->getName().string(), errno);
-        return UNKNOWN_ERROR;
-    }
-
-    mSemaphoreInitialized = true;
-
-    mSharedMessage->consumed = false;
-    mSharedMessage->type = type;
-    mSharedMessage->deviceId = deviceId;
-    mSharedMessage->source = source;
-    return OK;
 }
 
 status_t InputPublisher::publishKeyEvent(
+        uint32_t seq,
         int32_t deviceId,
         int32_t source,
         int32_t action,
@@ -337,31 +215,37 @@
         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
 
-    status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source);
-    if (result < 0) {
-        return result;
+    if (!seq) {
+        ALOGE("Attempted to publish a key event with sequence number 0.");
+        return BAD_VALUE;
     }
 
-    mSharedMessage->key.action = action;
-    mSharedMessage->key.flags = flags;
-    mSharedMessage->key.keyCode = keyCode;
-    mSharedMessage->key.scanCode = scanCode;
-    mSharedMessage->key.metaState = metaState;
-    mSharedMessage->key.repeatCount = repeatCount;
-    mSharedMessage->key.downTime = downTime;
-    mSharedMessage->key.eventTime = eventTime;
-    return OK;
+    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;
+    msg.body.key.flags = flags;
+    msg.body.key.keyCode = keyCode;
+    msg.body.key.scanCode = scanCode;
+    msg.body.key.metaState = metaState;
+    msg.body.key.repeatCount = repeatCount;
+    msg.body.key.downTime = downTime;
+    msg.body.key.eventTime = eventTime;
+    return mChannel->sendMessage(&msg);
 }
 
 status_t InputPublisher::publishMotionEvent(
+        uint32_t seq,
         int32_t deviceId,
         int32_t source,
         int32_t action,
@@ -379,349 +263,349 @@
         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);
         return BAD_VALUE;
     }
 
-    status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source);
-    if (result < 0) {
-        return result;
-    }
-
-    mSharedMessage->motion.action = action;
-    mSharedMessage->motion.flags = flags;
-    mSharedMessage->motion.edgeFlags = edgeFlags;
-    mSharedMessage->motion.metaState = metaState;
-    mSharedMessage->motion.buttonState = buttonState;
-    mSharedMessage->motion.xOffset = xOffset;
-    mSharedMessage->motion.yOffset = yOffset;
-    mSharedMessage->motion.xPrecision = xPrecision;
-    mSharedMessage->motion.yPrecision = yPrecision;
-    mSharedMessage->motion.downTime = downTime;
-    mSharedMessage->motion.pointerCount = pointerCount;
-
-    mSharedMessage->motion.sampleCount = 1;
-    mSharedMessage->motion.sampleData[0].eventTime = eventTime;
-
+    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;
+    msg.body.motion.flags = flags;
+    msg.body.motion.edgeFlags = edgeFlags;
+    msg.body.motion.metaState = metaState;
+    msg.body.motion.buttonState = buttonState;
+    msg.body.motion.xOffset = xOffset;
+    msg.body.motion.yOffset = yOffset;
+    msg.body.motion.xPrecision = xPrecision;
+    msg.body.motion.yPrecision = yPrecision;
+    msg.body.motion.downTime = downTime;
+    msg.body.motion.eventTime = eventTime;
+    msg.body.motion.pointerCount = pointerCount;
     for (size_t i = 0; i < pointerCount; i++) {
-        mSharedMessage->motion.pointerProperties[i].copyFrom(pointerProperties[i]);
-        mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]);
+        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
+        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
     }
-
-    // Cache essential information about the motion event to ensure that a malicious consumer
-    // cannot confuse the publisher by modifying the contents of the shared memory buffer while
-    // it is being updated.
-    if (action == AMOTION_EVENT_ACTION_MOVE
-            || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-        mMotionEventPointerCount = pointerCount;
-        mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount);
-        mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement(
-                mSharedMessage->motion.sampleData, mMotionEventSampleDataStride);
-    } else {
-        mMotionEventSampleDataTail = NULL;
-    }
-    return OK;
+    return mChannel->sendMessage(&msg);
 }
 
-status_t InputPublisher::appendMotionSample(
-        nsecs_t eventTime,
-        const PointerCoords* pointerCoords) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
-            mChannel->getName().string(), eventTime);
-#endif
-
-    if (! mPinned || ! mMotionEventSampleDataTail) {
-        ALOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
-                "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.",
-                mChannel->getName().string());
-        return INVALID_OPERATION;
-    }
-
-    InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement(
-            mMotionEventSampleDataTail, mMotionEventSampleDataStride);
-    size_t newBytesUsed = reinterpret_cast<char*>(newTail) -
-            reinterpret_cast<char*>(mSharedMessage);
-
-    if (newBytesUsed > mAshmemSize) {
-#if DEBUG_TRANSPORT_ACTIONS
-        ALOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
-                "buffer is full.  Buffer size: %d bytes, pointers: %d, samples: %d",
-                mChannel->getName().string(),
-                mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
-#endif
-        return NO_MEMORY;
-    }
-
-    int result;
-    if (mWasDispatched) {
-        result = sem_trywait(& mSharedMessage->semaphore);
-        if (result < 0) {
-            if (errno == EAGAIN) {
-                // Only possible source of contention is the consumer having consumed (or being in the
-                // process of consuming) the message and left the semaphore count at 0.
-#if DEBUG_TRANSPORT_ACTIONS
-                ALOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
-                        "already been consumed.", mChannel->getName().string());
-#endif
-                return FAILED_TRANSACTION;
-            } else {
-                ALOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
-                        mChannel->getName().string(), errno);
-                return UNKNOWN_ERROR;
-            }
-        }
-    }
-
-    mMotionEventSampleDataTail->eventTime = eventTime;
-    for (size_t i = 0; i < mMotionEventPointerCount; i++) {
-        mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]);
-    }
-    mMotionEventSampleDataTail = newTail;
-
-    mSharedMessage->motion.sampleCount += 1;
-
-    if (mWasDispatched) {
-        result = sem_post(& mSharedMessage->semaphore);
-        if (result < 0) {
-            ALOGE("channel '%s' publisher ~ Error %d in sem_post.",
-                    mChannel->getName().string(), errno);
-            return UNKNOWN_ERROR;
-        }
-    }
-    return OK;
-}
-
-status_t InputPublisher::sendDispatchSignal() {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ sendDispatchSignal",
-            mChannel->getName().string());
-#endif
-
-    mWasDispatched = true;
-    return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
-}
-
-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());
 #endif
 
-    char signal;
-    status_t result = mChannel->receiveSignal(& signal);
+    InputMessage msg;
+    status_t result = mChannel->receiveMessage(&msg);
     if (result) {
+        *outSeq = 0;
         *outHandled = false;
         return result;
     }
-    if (signal == INPUT_SIGNAL_FINISHED_HANDLED) {
-        *outHandled = true;
-    } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) {
-        *outHandled = false;
-    } else {
-        ALOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
-                mChannel->getName().string(), signal);
+    if (msg.header.type != InputMessage::TYPE_FINISHED) {
+        ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
+                mChannel->getName().string(), msg.header.type);
         return UNKNOWN_ERROR;
     }
+    *outSeq = msg.body.finished.seq;
+    *outHandled = msg.body.finished.handled;
     return OK;
 }
 
 // --- InputConsumer ---
 
 InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
-        mChannel(channel), mSharedMessage(NULL) {
+        mChannel(channel), mMsgDeferred(false) {
 }
 
 InputConsumer::~InputConsumer() {
-    if (mSharedMessage) {
-        munmap(mSharedMessage, mAshmemSize);
-    }
 }
 
-status_t InputConsumer::initialize() {
+status_t InputConsumer::consume(InputEventFactoryInterface* factory,
+        bool consumeBatches, uint32_t* outSeq, InputEvent** outEvent) {
 #if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ initialize",
-            mChannel->getName().string());
+    ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s",
+            mChannel->getName().string(), consumeBatches ? "true" : "false");
 #endif
 
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_get_size_region(ashmemFd);
-    if (result < 0) {
-        ALOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
-
-    mAshmemSize = (size_t) result;
-
-    mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
-    if (! mSharedMessage) {
-        ALOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.",
-                mChannel->getName().string(), ashmemFd);
-        return NO_MEMORY;
-    }
-
-    return OK;
-}
-
-status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ consume",
-            mChannel->getName().string());
-#endif
-
+    *outSeq = 0;
     *outEvent = NULL;
 
-    int ashmemFd = mChannel->getAshmemFd();
-    int result = ashmem_pin_region(ashmemFd, 0, 0);
-    if (result != ASHMEM_NOT_PURGED) {
-        if (result == ASHMEM_WAS_PURGED) {
-            ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged "
-                    "which probably indicates that the publisher and consumer are out of sync.",
-                    mChannel->getName().string(), result, ashmemFd);
-            return INVALID_OPERATION;
+    // Fetch the next input message.
+    // Loop until an event can be returned or no additional events are received.
+    while (!*outEvent) {
+        if (mMsgDeferred) {
+            // mMsg contains a valid input message from the previous call to consume
+            // that has not yet been processed.
+            mMsgDeferred = false;
+        } else {
+            // Receive a fresh message.
+            status_t result = mChannel->receiveMessage(&mMsg);
+            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;
+            }
         }
 
-        ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
-                mChannel->getName().string(), result, ashmemFd);
-        return UNKNOWN_ERROR;
-    }
+        switch (mMsg.header.type) {
+        case InputMessage::TYPE_KEY: {
+            KeyEvent* keyEvent = factory->createKeyEvent();
+            if (!keyEvent) return NO_MEMORY;
 
-    if (mSharedMessage->consumed) {
-        ALOGE("channel '%s' consumer ~ The current message has already been consumed.",
-                mChannel->getName().string());
-        return INVALID_OPERATION;
-    }
-
-    // Acquire but *never release* the semaphore.  Contention on the semaphore is used to signal
-    // to the publisher that the message has been consumed (or is in the process of being
-    // consumed).  Eventually the publisher will reinitialize the semaphore for the next message.
-    result = sem_wait(& mSharedMessage->semaphore);
-    if (result < 0) {
-        ALOGE("channel '%s' consumer ~ Error %d in sem_wait.",
-                mChannel->getName().string(), errno);
-        return UNKNOWN_ERROR;
-    }
-
-    mSharedMessage->consumed = true;
-
-    switch (mSharedMessage->type) {
-    case AINPUT_EVENT_TYPE_KEY: {
-        KeyEvent* keyEvent = factory->createKeyEvent();
-        if (! keyEvent) return NO_MEMORY;
-
-        populateKeyEvent(keyEvent);
-
-        *outEvent = keyEvent;
-        break;
-    }
-
-    case AINPUT_EVENT_TYPE_MOTION: {
-        MotionEvent* motionEvent = factory->createMotionEvent();
-        if (! motionEvent) return NO_MEMORY;
-
-        populateMotionEvent(motionEvent);
-
-        *outEvent = motionEvent;
-        break;
-    }
-
-    default:
-        ALOGE("channel '%s' consumer ~ Received message of unknown type %d",
-                mChannel->getName().string(), mSharedMessage->type);
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-status_t InputConsumer::sendFinishedSignal(bool handled) {
+            initializeKeyEvent(keyEvent, &mMsg);
+            *outSeq = mMsg.body.key.seq;
+            *outEvent = keyEvent;
 #if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d",
-            mChannel->getName().string(), handled);
+            ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+                    mChannel->getName().string(), *outSeq);
 #endif
+            break;
+        }
 
-    return mChannel->sendSignal(handled
-            ? INPUT_SIGNAL_FINISHED_HANDLED
-            : INPUT_SIGNAL_FINISHED_UNHANDLED);
-}
+        case AINPUT_EVENT_TYPE_MOTION: {
+            ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
+            if (batchIndex >= 0) {
+                Batch& batch = mBatches.editItemAt(batchIndex);
+                if (canAppendSamples(&batch.event, &mMsg)) {
+                    // Append to the batch and save the new sequence number for the tail end.
+                    uint32_t chain = batch.seq;
+                    appendSamples(&batch.event, &mMsg);
+                    batch.seq = mMsg.body.motion.seq;
 
-status_t InputConsumer::receiveDispatchSignal() {
+                    // Update the sequence number chain.
+                    SeqChain seqChain;
+                    seqChain.seq = batch.seq;
+                    seqChain.chain = chain;
+                    mSeqChains.push(seqChain);
 #if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ receiveDispatchSignal",
-            mChannel->getName().string());
+                    ALOGD("channel '%s' consumer ~ appended to batch event",
+                            mChannel->getName().string());
 #endif
+                    break;
+                } else {
+                    MotionEvent* motionEvent = factory->createMotionEvent();
+                    if (! motionEvent) return NO_MEMORY;
 
-    char signal;
-    status_t result = mChannel->receiveSignal(& signal);
-    if (result) {
-        return result;
-    }
-    if (signal != INPUT_SIGNAL_DISPATCH) {
-        ALOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher",
-                mChannel->getName().string(), signal);
-        return UNKNOWN_ERROR;
+                    // We cannot append to the batch in progress, so we need to consume
+                    // the previous batch right now and defer the new message until later.
+                    mMsgDeferred = true;
+
+                    // 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 (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
+                    || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                mBatches.push();
+                Batch& batch = mBatches.editTop();
+                batch.seq = mMsg.body.motion.seq;
+                initializeMotionEvent(&batch.event, &mMsg);
+#if DEBUG_TRANSPORT_ACTIONS
+                ALOGD("channel '%s' consumer ~ started batch event",
+                        mChannel->getName().string());
+#endif
+                break;
+            }
+
+            MotionEvent* motionEvent = factory->createMotionEvent();
+            if (! motionEvent) return NO_MEMORY;
+
+            initializeMotionEvent(motionEvent, &mMsg);
+            *outSeq = mMsg.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(), mMsg.header.type);
+            return UNKNOWN_ERROR;
+        }
     }
     return OK;
 }
 
-void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const {
-    keyEvent->initialize(
-            mSharedMessage->deviceId,
-            mSharedMessage->source,
-            mSharedMessage->key.action,
-            mSharedMessage->key.flags,
-            mSharedMessage->key.keyCode,
-            mSharedMessage->key.scanCode,
-            mSharedMessage->key.metaState,
-            mSharedMessage->key.repeatCount,
-            mSharedMessage->key.downTime,
-            mSharedMessage->key.eventTime);
-}
+status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
+#if DEBUG_TRANSPORT_ACTIONS
+    ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+            mChannel->getName().string(), seq, handled ? "true" : "false");
+#endif
 
-void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const {
-    motionEvent->initialize(
-            mSharedMessage->deviceId,
-            mSharedMessage->source,
-            mSharedMessage->motion.action,
-            mSharedMessage->motion.flags,
-            mSharedMessage->motion.edgeFlags,
-            mSharedMessage->motion.metaState,
-            mSharedMessage->motion.buttonState,
-            mSharedMessage->motion.xOffset,
-            mSharedMessage->motion.yOffset,
-            mSharedMessage->motion.xPrecision,
-            mSharedMessage->motion.yPrecision,
-            mSharedMessage->motion.downTime,
-            mSharedMessage->motion.sampleData[0].eventTime,
-            mSharedMessage->motion.pointerCount,
-            mSharedMessage->motion.pointerProperties,
-            mSharedMessage->motion.sampleData[0].coords);
+    if (!seq) {
+        ALOGE("Attempted to send a finished signal with sequence number 0.");
+        return BAD_VALUE;
+    }
 
-    size_t sampleCount = mSharedMessage->motion.sampleCount;
-    if (sampleCount > 1) {
-        InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData;
-        size_t sampleDataStride = InputMessage::sampleDataStride(
-                mSharedMessage->motion.pointerCount);
-
-        while (--sampleCount > 0) {
-            sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride);
-            motionEvent->addSample(sampleData->eventTime, sampleData->coords);
+    // Send finished signals for the batch sequence chain first.
+    size_t seqChainCount = mSeqChains.size();
+    if (seqChainCount) {
+        uint32_t currentSeq = seq;
+        uint32_t chainSeqs[seqChainCount];
+        size_t chainIndex = 0;
+        for (size_t i = seqChainCount; i-- > 0; ) {
+             const SeqChain& seqChain = mSeqChains.itemAt(i);
+             if (seqChain.seq == currentSeq) {
+                 currentSeq = seqChain.chain;
+                 chainSeqs[chainIndex++] = currentSeq;
+                 mSeqChains.removeAt(i);
+             }
+        }
+        status_t status = OK;
+        while (!status && chainIndex-- > 0) {
+            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
+        }
+        if (status) {
+            // An error occurred so at least one signal was not sent, reconstruct the chain.
+            do {
+                SeqChain seqChain;
+                seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
+                seqChain.chain = chainSeqs[chainIndex];
+                mSeqChains.push(seqChain);
+            } while (chainIndex-- > 0);
+            return status;
         }
     }
+
+    // Send finished signal for the last message in the batch.
+    return sendUnchainedFinishedSignal(seq, handled);
+}
+
+status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
+    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 eff22ee..ee422fe 100644
--- a/libs/ui/tests/InputChannel_test.cpp
+++ b/libs/ui/tests/InputChannel_test.cpp
@@ -20,8 +20,7 @@
 #include <gtest/gtest.h>
 #include <unistd.h>
 #include <time.h>
-#include <sys/mman.h>
-#include <cutils/ashmem.h>
+#include <errno.h>
 
 #include "../../utils/tests/TestHelpers.h"
 
@@ -36,35 +35,24 @@
 
 TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
     // Our purpose here is to verify that the input channel destructor closes the
-    // file descriptors provided to it.  One easy way is to provide it with one end
+    // file descriptor provided to it.  One easy way is to provide it with one end
     // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
-    Pipe fakeAshmem, sendPipe, receivePipe;
+    Pipe pipe;
 
-    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"),
-            fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd);
+    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"), pipe.sendFd);
 
     EXPECT_STREQ("channel name", inputChannel->getName().string())
             << "channel should have provided name";
-    EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd())
-            << "channel should have provided ashmem fd";
-    EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd())
-            << "channel should have provided receive pipe fd";
-    EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd())
-            << "channel should have provided send pipe fd";
+    EXPECT_EQ(pipe.sendFd, inputChannel->getFd())
+            << "channel should have provided fd";
 
     inputChannel.clear(); // destroys input channel
 
-    EXPECT_EQ(-EPIPE, fakeAshmem.readSignal())
-            << "channel should have closed ashmem fd when destroyed";
-    EXPECT_EQ(-EPIPE, receivePipe.writeSignal())
-            << "channel should have closed receive pipe fd when destroyed";
-    EXPECT_EQ(-EPIPE, sendPipe.readSignal())
-            << "channel should have closed send pipe fd when destroyed";
+    EXPECT_EQ(-EPIPE, pipe.readSignal())
+            << "channel should have closed fd when destroyed";
 
     // clean up fds of Pipe endpoints that were closed so we don't try to close them again
-    fakeAshmem.sendFd = -1;
-    receivePipe.receiveFd = -1;
-    sendPipe.sendFd = -1;
+    pipe.sendFd = -1;
 }
 
 TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
@@ -82,43 +70,40 @@
     EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
             << "client channel should have suffixed name";
 
-    // Ashmem uniqueness
-    EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd())
-            << "server and client channel should have different ashmem fds because it was dup'd";
-
-    // Ashmem usability
-    ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd());
-    ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd());
-    uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0));
-    uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize,
-            PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0));
-    ASSERT_TRUE(serverAshmem != NULL)
-            << "server channel ashmem should be mappable";
-    ASSERT_TRUE(clientAshmem != NULL)
-            << "client channel ashmem should be mappable";
-    *serverAshmem = 0xf00dd00d;
-    EXPECT_EQ(0xf00dd00d, *clientAshmem)
-            << "ashmem buffer should be shared by client and server";
-    munmap(serverAshmem, serverAshmemSize);
-    munmap(clientAshmem, clientAshmemSize);
-
     // Server->Client communication
-    EXPECT_EQ(OK, serverChannel->sendSignal('S'))
-            << "server channel should be able to send signal to client channel";
-    char signal;
-    EXPECT_EQ(OK, clientChannel->receiveSignal(& signal))
-            << "client channel should be able to receive signal from server channel";
-    EXPECT_EQ('S', signal)
-            << "client channel should receive the correct signal from server channel";
+    InputMessage serverMsg;
+    memset(&serverMsg, 0, sizeof(InputMessage));
+    serverMsg.header.type = InputMessage::TYPE_KEY;
+    serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN;
+    EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
+            << "server channel should be able to send message to client channel";
+
+    InputMessage clientMsg;
+    EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
+            << "client channel should be able to receive message from server channel";
+    EXPECT_EQ(serverMsg.header.type, clientMsg.header.type)
+            << "client channel should receive the correct message from server channel";
+    EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action)
+            << "client channel should receive the correct message from server channel";
 
     // Client->Server communication
-    EXPECT_EQ(OK, clientChannel->sendSignal('c'))
-            << "client channel should be able to send signal to server channel";
-    EXPECT_EQ(OK, serverChannel->receiveSignal(& signal))
-            << "server channel should be able to receive signal from client channel";
-    EXPECT_EQ('c', signal)
-            << "server channel should receive the correct signal from client channel";
+    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";
+
+    InputMessage serverReply;
+    EXPECT_EQ(OK, serverChannel->receiveMessage(&serverReply))
+            << "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";
 }
 
 TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
@@ -130,9 +115,9 @@
     ASSERT_EQ(OK, result)
             << "should have successfully opened a channel pair";
 
-    char signal;
-    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal))
-            << "receiveSignal should have returned WOULD_BLOCK";
+    InputMessage msg;
+    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveMessage(&msg))
+            << "receiveMessage should have returned WOULD_BLOCK";
 }
 
 TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
@@ -146,9 +131,9 @@
 
     serverChannel.clear(); // close server channel
 
-    char signal;
-    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal))
-            << "receiveSignal should have returned DEAD_OBJECT";
+    InputMessage msg;
+    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg))
+            << "receiveMessage should have returned DEAD_OBJECT";
 }
 
 TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
@@ -162,8 +147,10 @@
 
     serverChannel.clear(); // close server channel
 
-    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S'))
-            << "sendSignal should have returned DEAD_OBJECT";
+    InputMessage msg;
+    msg.header.type = InputMessage::TYPE_KEY;
+    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg))
+            << "sendMessage should have returned DEAD_OBJECT";
 }
 
 
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
index fcc4cad..3303053 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -57,11 +57,8 @@
         clientChannel.clear();
     }
 
-    void Initialize();
     void PublishAndConsumeKeyEvent();
-    void PublishAndConsumeMotionEvent(
-            size_t samplesToAppendBeforeDispatch = 0,
-            size_t samplesToAppendAfterDispatch = 0);
+    void PublishAndConsumeMotionEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -69,21 +66,10 @@
     EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
 }
 
-void InputPublisherAndConsumerTest::Initialize() {
-    status_t status;
-
-    status = mPublisher->initialize();
-    ASSERT_EQ(OK, status)
-            << "publisher initialize should return OK";
-
-    status = mConsumer->initialize();
-    ASSERT_EQ(OK, status)
-            << "consumer initialize should return OK";
-}
-
 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;
@@ -95,21 +81,14 @@
     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";
 
-    status = mPublisher->sendDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "publisher sendDispatchSignal should return OK";
-
-    status = mConsumer->receiveDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "consumer receiveDispatchSignal 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";
 
@@ -119,6 +98,7 @@
             << "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());
@@ -130,26 +110,25 @@
     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";
-
-    status = mPublisher->reset();
-    ASSERT_EQ(OK, status)
-            << "publisher reset should return OK";
 }
 
-void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
-        size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) {
+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;
@@ -163,67 +142,36 @@
     const float yPrecision = 0.5;
     const nsecs_t downTime = 3;
     const size_t pointerCount = 3;
+    const nsecs_t eventTime = 4;
     PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
     for (size_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].clear();
         pointerProperties[i].id = (i + 2) % pointerCount;
         pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+        pointerCoords[i].clear();
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
     }
 
-    Vector<nsecs_t> sampleEventTimes;
-    Vector<PointerCoords> samplePointerCoords;
-
-    for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) {
-        sampleEventTimes.push(i + 10);
-        for (size_t j = 0; j < pointerCount; j++) {
-            samplePointerCoords.push();
-            PointerCoords& pc = samplePointerCoords.editTop();
-            pc.clear();
-            pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j);
-            pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j);
-        }
-    }
-
-    status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags,
+    status = mPublisher->publishMotionEvent(seq, deviceId, source, action, flags, edgeFlags,
             metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
-            downTime, sampleEventTimes[0], pointerCount,
-            pointerProperties, samplePointerCoords.array());
+            downTime, eventTime, pointerCount,
+            pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
-    for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) {
-        size_t sampleIndex = i + 1;
-        status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
-                samplePointerCoords.array() + sampleIndex * pointerCount);
-        ASSERT_EQ(OK, status)
-                << "publisher appendMotionEvent should return OK";
-    }
-
-    status = mPublisher->sendDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "publisher sendDispatchSignal should return OK";
-
-    for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) {
-        size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch;
-        status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
-                samplePointerCoords.array() + sampleIndex * pointerCount);
-        ASSERT_EQ(OK, status)
-                << "publisher appendMotionEvent should return OK";
-    }
-
-    status = mConsumer->receiveDispatchSignal();
-    ASSERT_EQ(OK, status)
-            << "consumer receiveDispatchSignal 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";
 
@@ -232,9 +180,8 @@
     ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
             << "consumer should have returned a motion event";
 
-    size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch;
-
     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());
@@ -245,150 +192,69 @@
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
     EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
-    EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
+    EXPECT_EQ(eventTime, motionEvent->getEventTime());
     EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
-    EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize());
+    EXPECT_EQ(0U, motionEvent->getHistorySize());
 
     for (size_t i = 0; i < pointerCount; i++) {
         SCOPED_TRACE(i);
         EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
         EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
-    }
 
-    for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) {
-        SCOPED_TRACE(sampleIndex);
-        EXPECT_EQ(sampleEventTimes[sampleIndex],
-                motionEvent->getHistoricalEventTime(sampleIndex));
-        for (size_t i = 0; i < pointerCount; i++) {
-            SCOPED_TRACE(i);
-            size_t offset = sampleIndex * pointerCount + i;
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X),
-                    motionEvent->getHistoricalRawX(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                    motionEvent->getHistoricalRawY(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
-                    motionEvent->getHistoricalX(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
-                    motionEvent->getHistoricalY(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                    motionEvent->getHistoricalPressure(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                    motionEvent->getHistoricalSize(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                    motionEvent->getHistoricalTouchMajor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                    motionEvent->getHistoricalTouchMinor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                    motionEvent->getHistoricalToolMajor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                    motionEvent->getHistoricalToolMinor(i, sampleIndex));
-            EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
-                    motionEvent->getHistoricalOrientation(i, sampleIndex));
-        }
-    }
-
-    SCOPED_TRACE(lastSampleIndex);
-    EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
-    for (size_t i = 0; i < pointerCount; i++) {
-        SCOPED_TRACE(i);
-        size_t offset = lastSampleIndex * pointerCount + i;
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
                 motionEvent->getRawX(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
                 motionEvent->getRawY(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
                 motionEvent->getX(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
                 motionEvent->getY(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
                 motionEvent->getPressure(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
                 motionEvent->getSize(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
                 motionEvent->getTouchMajor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
                 motionEvent->getTouchMinor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
                 motionEvent->getToolMajor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
                 motionEvent->getToolMinor(i));
-        EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
                 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";
-
-    status = mPublisher->reset();
-    ASSERT_EQ(OK, status)
-            << "publisher reset should return OK";
 }
 
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
-    ASSERT_NO_FATAL_FAILURE(Initialize());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
 
-TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-    ASSERT_EQ(OK, status)
-            << "publisher publishKeyEvent should return OK first time";
-
-    status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher publishKeyEvent should return INVALID_OPERATION because "
-                    "the publisher was not reset";
-}
-
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
-    ASSERT_NO_FATAL_FAILURE(Initialize());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
 }
 
-TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = 1;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status)
-            << "publisher publishMotionEvent should return OK";
-
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher publishMotionEvent should return INVALID_OPERATION because ";
-                    "the publisher was not reset";
-}
-
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
     status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
     const size_t pointerCount = 0;
     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";
@@ -396,8 +262,6 @@
 
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
     status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
     const size_t pointerCount = MAX_POINTERS + 1;
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
@@ -406,14 +270,13 @@
         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";
 }
 
 TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
-    ASSERT_NO_FATAL_FAILURE(Initialize());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
@@ -421,111 +284,4 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
 
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0));
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4));
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    PointerCoords pointerCoords[1];
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher appendMotionSample should return INVALID_OPERATION";
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status);
-
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(INVALID_OPERATION, status)
-            << "publisher appendMotionSample should return INVALID_OPERATION";
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status);
-
-    status = mPublisher->sendDispatchSignal();
-    ASSERT_EQ(OK, status);
-
-    status = mConsumer->receiveDispatchSignal();
-    ASSERT_EQ(OK, status);
-
-    InputEvent* event;
-    status = mConsumer->consume(& mEventFactory, & event);
-    ASSERT_EQ(OK, status);
-
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(status_t(FAILED_TRANSACTION), status)
-            << "publisher appendMotionSample should return FAILED_TRANSACTION";
-}
-
-TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) {
-    status_t status;
-    ASSERT_NO_FATAL_FAILURE(Initialize());
-
-    const size_t pointerCount = MAX_POINTERS;
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords pointerCoords[pointerCount];
-    for (size_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].clear();
-        pointerCoords[i].clear();
-    }
-
-    status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
-            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords);
-    ASSERT_EQ(OK, status);
-
-    for (int count = 1;; count++) {
-        ASSERT_LT(count, 100000) << "should eventually reach OOM";
-
-        status = mPublisher->appendMotionSample(0, pointerCoords);
-        if (status != OK) {
-            ASSERT_GT(count, 12) << "should be able to add at least a dozen samples";
-            ASSERT_EQ(NO_MEMORY, status)
-                    << "publisher appendMotionSample should return NO_MEMORY when buffer is full";
-            break;
-        }
-    }
-
-    status = mPublisher->appendMotionSample(0, pointerCoords);
-    ASSERT_EQ(NO_MEMORY, status)
-            << "publisher appendMotionSample should return NO_MEMORY persistently until reset";
-}
-
 } // namespace android