| // | |
| // 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 { | |
| // Must be at least sizeof(InputMessage) + sufficient space for pointer data | |
| static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384; | |
| // 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, | |
| float xOffset, | |
| float yOffset, | |
| float xPrecision, | |
| float yPrecision, | |
| nsecs_t downTime, | |
| nsecs_t eventTime, | |
| size_t pointerCount, | |
| const int32_t* pointerIds, | |
| 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, xOffset=%f, yOffset=%f, " | |
| "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " | |
| "pointerCount=%d", | |
| mChannel->getName().string(), | |
| deviceId, source, action, flags, edgeFlags, metaState, 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.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.pointerIds[i] = pointerIds[i]; | |
| mSharedMessage->motion.sampleData[0].coords[i] = 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 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] = 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.xOffset, | |
| mSharedMessage->motion.yOffset, | |
| mSharedMessage->motion.xPrecision, | |
| mSharedMessage->motion.yPrecision, | |
| mSharedMessage->motion.downTime, | |
| mSharedMessage->motion.sampleData[0].eventTime, | |
| mSharedMessage->motion.pointerCount, | |
| mSharedMessage->motion.pointerIds, | |
| 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 |