| // | 
 | // 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 | 
 |     ALOGD("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 | 
 |     ALOGD("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; | 
 |  | 
 |     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", | 
 |                 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; | 
 | } | 
 |  | 
 | 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 | 
 |         ALOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal); | 
 | #endif | 
 |         return OK; | 
 |     } | 
 |  | 
 | #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()); | 
 | #endif | 
 |         return DEAD_OBJECT; | 
 |     } | 
 |  | 
 |     if (errno == EAGAIN) { | 
 | #if DEBUG_CHANNEL_SIGNALS | 
 |         ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string()); | 
 | #endif | 
 |         return WOULD_BLOCK; | 
 |     } | 
 |  | 
 | #if DEBUG_CHANNEL_SIGNALS | 
 |     ALOGD("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 | 
 |     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( | 
 |         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 | 
 |     ALOGD("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 | 
 |     ALOGD("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) { | 
 |         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; | 
 |  | 
 |     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; | 
 |     } | 
 |  | 
 |     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) { | 
 | #if DEBUG_TRANSPORT_ACTIONS | 
 |     ALOGD("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 { | 
 |         ALOGE("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 | 
 |     ALOGD("channel '%s' consumer ~ initialize", | 
 |             mChannel->getName().string()); | 
 | #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 | 
 |  | 
 |     *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; | 
 |         } | 
 |  | 
 |         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; | 
 |     } | 
 |  | 
 |     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) { | 
 | #if DEBUG_TRANSPORT_ACTIONS | 
 |     ALOGD("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 | 
 |     ALOGD("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) { | 
 |         ALOGE("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 |