7832080b5a
Fixed some issues where inconsistent streams of events could be generated by the dispatcher, particularly when switching from hovering with one device to hovering with another. Fixed a bug where the touch pad would fail to generate a new HOVER_MOVE following a tap event. As a result, the hover event stream would not resume until the user touched the touch pad again. Change-Id: I444dce84641fb12e56a0af84c931520771d6c493
726 lines
25 KiB
C++
726 lines
25 KiB
C++
//
|
|
// Copyright 2010 The Android Open Source Project
|
|
//
|
|
// Provides a shared memory transport for input events.
|
|
//
|
|
#define LOG_TAG "InputTransport"
|
|
|
|
//#define LOG_NDEBUG 0
|
|
|
|
// Log debug messages about channel signalling (send signal, receive signal)
|
|
#define DEBUG_CHANNEL_SIGNALS 0
|
|
|
|
// Log debug messages whenever InputChannel objects are created/destroyed
|
|
#define DEBUG_CHANNEL_LIFECYCLE 0
|
|
|
|
// Log debug messages about transport actions (initialize, reset, publish, ...)
|
|
#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>
|
|
|
|
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';
|
|
|
|
// 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';
|
|
|
|
// 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';
|
|
|
|
|
|
// --- InputChannel ---
|
|
|
|
InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
|
|
int32_t sendPipeFd) :
|
|
mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
|
|
#if DEBUG_CHANNEL_LIFECYCLE
|
|
LOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
|
|
mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
|
|
#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 "
|
|
"non-blocking. errno=%d", mName.string(), errno);
|
|
}
|
|
|
|
InputChannel::~InputChannel() {
|
|
#if DEBUG_CHANNEL_LIFECYCLE
|
|
LOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
|
|
mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
|
|
#endif
|
|
|
|
::close(mAshmemFd);
|
|
::close(mReceivePipeFd);
|
|
::close(mSendPipeFd);
|
|
}
|
|
|
|
status_t InputChannel::openInputChannelPair(const String8& name,
|
|
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
|
|
status_t result;
|
|
|
|
int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
|
|
if (serverAshmemFd < 0) {
|
|
result = -errno;
|
|
LOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
|
|
name.string(), errno);
|
|
} else {
|
|
result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
|
|
if (result < 0) {
|
|
LOGE("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;
|
|
LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
|
|
name.string(), errno);
|
|
} else {
|
|
int forward[2];
|
|
if (pipe(forward)) {
|
|
result = -errno;
|
|
LOGE("channel '%s' ~ Could not create forward pipe. errno=%d",
|
|
name.string(), errno);
|
|
} else {
|
|
int reverse[2];
|
|
if (pipe(reverse)) {
|
|
result = -errno;
|
|
LOGE("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;
|
|
}
|
|
|
|
status_t InputChannel::sendSignal(char signal) {
|
|
ssize_t nWrite;
|
|
do {
|
|
nWrite = ::write(mSendPipeFd, & signal, 1);
|
|
} while (nWrite == -1 && errno == EINTR);
|
|
|
|
if (nWrite == 1) {
|
|
#if DEBUG_CHANNEL_SIGNALS
|
|
LOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
#if DEBUG_CHANNEL_SIGNALS
|
|
LOGD("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
|
|
LOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
if (nRead == 0) { // check for EOF
|
|
#if DEBUG_CHANNEL_SIGNALS
|
|
LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
|
|
#endif
|
|
return DEAD_OBJECT;
|
|
}
|
|
|
|
if (errno == EAGAIN) {
|
|
#if DEBUG_CHANNEL_SIGNALS
|
|
LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
|
|
#endif
|
|
return WOULD_BLOCK;
|
|
}
|
|
|
|
#if DEBUG_CHANNEL_SIGNALS
|
|
LOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
|
|
#endif
|
|
return -errno;
|
|
}
|
|
|
|
|
|
// --- InputPublisher ---
|
|
|
|
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
|
|
mChannel(channel), mSharedMessage(NULL),
|
|
mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
|
|
mMotionEventSampleDataTail(NULL) {
|
|
}
|
|
|
|
InputPublisher::~InputPublisher() {
|
|
reset();
|
|
|
|
if (mSharedMessage) {
|
|
munmap(mSharedMessage, mAshmemSize);
|
|
}
|
|
}
|
|
|
|
status_t InputPublisher::initialize() {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' publisher ~ initialize",
|
|
mChannel->getName().string());
|
|
#endif
|
|
|
|
int ashmemFd = mChannel->getAshmemFd();
|
|
int result = ashmem_get_size_region(ashmemFd);
|
|
if (result < 0) {
|
|
LOGE("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) {
|
|
LOGE("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
|
|
LOGD("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) {
|
|
LOGE("channel '%s' publisher ~ Error %d in sem_post.",
|
|
mChannel->getName().string(), errno);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
result = sem_destroy(& mSharedMessage->semaphore);
|
|
if (result < 0) {
|
|
LOGE("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) {
|
|
LOGE("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) {
|
|
LOGE("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) {
|
|
LOGE("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) {
|
|
LOGE("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(
|
|
int32_t deviceId,
|
|
int32_t source,
|
|
int32_t action,
|
|
int32_t flags,
|
|
int32_t keyCode,
|
|
int32_t scanCode,
|
|
int32_t metaState,
|
|
int32_t repeatCount,
|
|
nsecs_t downTime,
|
|
nsecs_t eventTime) {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' publisher ~ publishKeyEvent: 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(),
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
status_t InputPublisher::publishMotionEvent(
|
|
int32_t deviceId,
|
|
int32_t source,
|
|
int32_t action,
|
|
int32_t flags,
|
|
int32_t edgeFlags,
|
|
int32_t metaState,
|
|
int32_t buttonState,
|
|
float xOffset,
|
|
float yOffset,
|
|
float xPrecision,
|
|
float yPrecision,
|
|
nsecs_t downTime,
|
|
nsecs_t eventTime,
|
|
size_t pointerCount,
|
|
const PointerProperties* pointerProperties,
|
|
const PointerCoords* pointerCoords) {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' publisher ~ publishMotionEvent: 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(),
|
|
deviceId, source, action, flags, edgeFlags, metaState, buttonState,
|
|
xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
|
|
#endif
|
|
|
|
if (pointerCount > MAX_POINTERS || pointerCount < 1) {
|
|
LOGE("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;
|
|
|
|
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
|
|
LOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
|
|
mChannel->getName().string(), eventTime);
|
|
#endif
|
|
|
|
if (! mPinned || ! mMotionEventSampleDataTail) {
|
|
LOGE("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
|
|
LOGD("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
|
|
LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
|
|
"already been consumed.", mChannel->getName().string());
|
|
#endif
|
|
return FAILED_TRANSACTION;
|
|
} else {
|
|
LOGE("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) {
|
|
LOGE("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
|
|
LOGD("channel '%s' publisher ~ sendDispatchSignal",
|
|
mChannel->getName().string());
|
|
#endif
|
|
|
|
mWasDispatched = true;
|
|
return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
|
|
}
|
|
|
|
status_t InputPublisher::receiveFinishedSignal(bool* outHandled) {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' publisher ~ receiveFinishedSignal",
|
|
mChannel->getName().string());
|
|
#endif
|
|
|
|
char signal;
|
|
status_t result = mChannel->receiveSignal(& signal);
|
|
if (result) {
|
|
*outHandled = false;
|
|
return result;
|
|
}
|
|
if (signal == INPUT_SIGNAL_FINISHED_HANDLED) {
|
|
*outHandled = true;
|
|
} else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) {
|
|
*outHandled = false;
|
|
} else {
|
|
LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
|
|
mChannel->getName().string(), signal);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
// --- InputConsumer ---
|
|
|
|
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
|
|
mChannel(channel), mSharedMessage(NULL) {
|
|
}
|
|
|
|
InputConsumer::~InputConsumer() {
|
|
if (mSharedMessage) {
|
|
munmap(mSharedMessage, mAshmemSize);
|
|
}
|
|
}
|
|
|
|
status_t InputConsumer::initialize() {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' consumer ~ initialize",
|
|
mChannel->getName().string());
|
|
#endif
|
|
|
|
int ashmemFd = mChannel->getAshmemFd();
|
|
int result = ashmem_get_size_region(ashmemFd);
|
|
if (result < 0) {
|
|
LOGE("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) {
|
|
LOGE("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
|
|
LOGD("channel '%s' consumer ~ consume",
|
|
mChannel->getName().string());
|
|
#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) {
|
|
LOGE("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;
|
|
}
|
|
|
|
LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
|
|
mChannel->getName().string(), result, ashmemFd);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (mSharedMessage->consumed) {
|
|
LOGE("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) {
|
|
LOGE("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:
|
|
LOGE("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) {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d",
|
|
mChannel->getName().string(), handled);
|
|
#endif
|
|
|
|
return mChannel->sendSignal(handled
|
|
? INPUT_SIGNAL_FINISHED_HANDLED
|
|
: INPUT_SIGNAL_FINISHED_UNHANDLED);
|
|
}
|
|
|
|
status_t InputConsumer::receiveDispatchSignal() {
|
|
#if DEBUG_TRANSPORT_ACTIONS
|
|
LOGD("channel '%s' consumer ~ receiveDispatchSignal",
|
|
mChannel->getName().string());
|
|
#endif
|
|
|
|
char signal;
|
|
status_t result = mChannel->receiveSignal(& signal);
|
|
if (result) {
|
|
return result;
|
|
}
|
|
if (signal != INPUT_SIGNAL_DISPATCH) {
|
|
LOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher",
|
|
mChannel->getName().string(), signal);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace android
|