summaryrefslogtreecommitdiffstats
path: root/libs/ui/InputTransport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ui/InputTransport.cpp')
-rw-r--r--libs/ui/InputTransport.cpp904
1 files changed, 377 insertions, 527 deletions
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 09cbb31..44cb6db 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
-// 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';
+namespace android {
-// 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';
+// 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;
+
+
+// --- InputMessage ---
+
+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);
+ 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 -errno;
+ return DEAD_OBJECT;
+ }
+
+#if DEBUG_CHANNEL_MESSAGES
+ ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
+#endif
+ return OK;
}
-status_t InputChannel::receiveSignal(char* outSignal) {
+status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::read(mReceivePipeFd, outSignal, 1);
+ nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
- if (nRead == 1) {
-#if DEBUG_CHANNEL_SIGNALS
- ALOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
+ if (nRead < 0) {
+ int error = errno;
+#if DEBUG_CHANNEL_MESSAGES
+ ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno);
#endif
- return OK;
+ if (error == EAGAIN || error == EWOULDBLOCK) {
+ return WOULD_BLOCK;
+ }
+ if (error == EPIPE || error == ENOTCONN) {
+ return DEAD_OBJECT;
+ }
+ return -error;
}
if (nRead == 0) { // check for EOF
-#if DEBUG_CHANNEL_SIGNALS
- ALOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
+#if DEBUG_CHANNEL_MESSAGES
+ ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string());
#endif
return DEAD_OBJECT;
}
- if (errno == EAGAIN) {
-#if DEBUG_CHANNEL_SIGNALS
- ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
+ if (!msg->isValid(nRead)) {
+#if DEBUG_CHANNEL_MESSAGES
+ ALOGD("channel '%s' ~ received invalid message", mName.string());
#endif
- return WOULD_BLOCK;
+ return BAD_VALUE;
}
-#if DEBUG_CHANNEL_SIGNALS
- ALOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
+#if DEBUG_CHANNEL_MESSAGES
+ ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type);
#endif
- return -errno;
+ 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 @@ status_t InputPublisher::publishKeyEvent(
nsecs_t downTime,
nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, "
+ ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
- mChannel->getName().string(),
+ mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
- 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,315 @@ status_t InputPublisher::publishMotionEvent(
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, "
+ ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, "
"xOffset=%f, yOffset=%f, "
"xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
"pointerCount=%d",
- mChannel->getName().string(),
+ mChannel->getName().string(), seq,
deviceId, source, action, flags, edgeFlags, metaState, buttonState,
xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
#endif
+ if (!seq) {
+ ALOGE("Attempted to publish a motion event with sequence number 0.");
+ return BAD_VALUE;
+ }
+
if (pointerCount > MAX_POINTERS || pointerCount < 1) {
ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
mChannel->getName().string(), pointerCount);
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]);
- }
-
- // 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;
-}
-
-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;
+ msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
+ msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
-
- 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);
+ return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::receiveFinishedSignal(bool* outHandled) {
+status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
mChannel->getName().string());
#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;
-}
+ *outSeq = 0;
+ *outEvent = NULL;
-status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
+ // 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 ~ consume",
- mChannel->getName().string());
+ ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().string(), *outSeq);
#endif
-
- *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;
+ break;
+ }
+ return result;
+ }
}
- ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
- mChannel->getName().string(), result, ashmemFd);
- return UNKNOWN_ERROR;
- }
-
- 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;
- }
+ switch (mMsg.header.type) {
+ case InputMessage::TYPE_KEY: {
+ KeyEvent* keyEvent = factory->createKeyEvent();
+ if (!keyEvent) return NO_MEMORY;
- mSharedMessage->consumed = true;
-
- switch (mSharedMessage->type) {
- case AINPUT_EVENT_TYPE_KEY: {
- KeyEvent* keyEvent = factory->createKeyEvent();
- if (! keyEvent) return NO_MEMORY;
+ initializeKeyEvent(keyEvent, &mMsg);
+ *outSeq = mMsg.body.key.seq;
+ *outEvent = keyEvent;
+#if DEBUG_TRANSPORT_ACTIONS
+ ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().string(), *outSeq);
+#endif
+ break;
+ }
- populateKeyEvent(keyEvent);
+ 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)) {
+ // Send finished message for the earlier part of the batch.
+ // Claim that we handled the event. (The dispatcher doesn't care either
+ // way at the moment.)
+ status_t status = sendFinishedSignal(batch.seq, true);
+ if (status) {
+ return status;
+ }
- *outEvent = keyEvent;
- break;
- }
+ // Append to the batch and save the new sequence number for the tail end.
+ appendSamples(&batch.event, &mMsg);
+ batch.seq = mMsg.body.motion.seq;
+#if DEBUG_TRANSPORT_ACTIONS
+ ALOGD("channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().string());
+#endif
+ break;
+ } else {
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (! motionEvent) return NO_MEMORY;
+
+ // We cannot append to the batch in progress, so we need to consume
+ // the previous batch right now and defer the new 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;
+ }
+ }
- case AINPUT_EVENT_TYPE_MOTION: {
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
+ // 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;
+ }
- populateMotionEvent(motionEvent);
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (! motionEvent) return NO_MEMORY;
- *outEvent = motionEvent;
- break;
- }
+ 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 message of unknown type %d",
- mChannel->getName().string(), mSharedMessage->type);
- return UNKNOWN_ERROR;
+ default:
+ ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
+ mChannel->getName().string(), mMsg.header.type);
+ return UNKNOWN_ERROR;
+ }
}
-
return OK;
}
-status_t InputConsumer::sendFinishedSignal(bool handled) {
+status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d",
- mChannel->getName().string(), handled);
+ ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().string(), seq, handled ? "true" : "false");
#endif
- return mChannel->sendSignal(handled
- ? INPUT_SIGNAL_FINISHED_HANDLED
- : INPUT_SIGNAL_FINISHED_UNHANDLED);
+ if (!seq) {
+ ALOGE("Attempted to send a finished signal with sequence number 0.");
+ return BAD_VALUE;
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::TYPE_FINISHED;
+ msg.body.finished.seq = seq;
+ msg.body.finished.handled = handled;
+ return mChannel->sendMessage(&msg);
}
-status_t InputConsumer::receiveDispatchSignal() {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ receiveDispatchSignal",
- mChannel->getName().string());
-#endif
+bool InputConsumer::hasPendingBatch() const {
+ return !mBatches.isEmpty();
+}
- 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;
+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 OK;
+ 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::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);
+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);
}
-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);
-
- 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);
+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