michael@0: // michael@0: // Copyright 2010 The Android Open Source Project michael@0: // michael@0: // Provides a shared memory transport for input events. michael@0: // michael@0: #define LOG_TAG "InputTransport" michael@0: michael@0: //#define LOG_NDEBUG 0 michael@0: michael@0: // Log debug messages about channel messages (send message, receive message) michael@0: #define DEBUG_CHANNEL_MESSAGES 0 michael@0: michael@0: // Log debug messages whenever InputChannel objects are created/destroyed michael@0: #define DEBUG_CHANNEL_LIFECYCLE 0 michael@0: michael@0: // Log debug messages about transport actions michael@0: #define DEBUG_TRANSPORT_ACTIONS 0 michael@0: michael@0: // Log debug messages about touch event resampling michael@0: #define DEBUG_RESAMPLING 0 michael@0: michael@0: michael@0: #include "cutils_log.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include "InputTransport.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: michael@0: namespace android { michael@0: michael@0: // Socket buffer size. The default is typically about 128KB, which is much larger than michael@0: // we really need. So we make it smaller. It just needs to be big enough to hold michael@0: // a few dozen large multi-finger motion events in the case where an application gets michael@0: // behind processing touches. michael@0: static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; michael@0: michael@0: // Nanoseconds per milliseconds. michael@0: static const nsecs_t NANOS_PER_MS = 1000000; michael@0: michael@0: // Latency added during resampling. A few milliseconds doesn't hurt much but michael@0: // reduces the impact of mispredicted touch positions. michael@0: static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS; michael@0: michael@0: // Minimum time difference between consecutive samples before attempting to resample. michael@0: static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; michael@0: michael@0: // Maximum time to predict forward from the last known state, to avoid predicting too michael@0: // far into the future. This time is further bounded by 50% of the last time delta. michael@0: static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; michael@0: michael@0: template michael@0: inline static T min(const T& a, const T& b) { michael@0: return a < b ? a : b; michael@0: } michael@0: michael@0: inline static float lerp(float a, float b, float alpha) { michael@0: return a + alpha * (b - a); michael@0: } michael@0: michael@0: // --- InputMessage --- michael@0: michael@0: bool InputMessage::isValid(size_t actualSize) const { michael@0: if (size() == actualSize) { michael@0: switch (header.type) { michael@0: case TYPE_KEY: michael@0: return true; michael@0: case TYPE_MOTION: michael@0: return body.motion.pointerCount > 0 michael@0: && body.motion.pointerCount <= MAX_POINTERS; michael@0: case TYPE_FINISHED: michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: size_t InputMessage::size() const { michael@0: switch (header.type) { michael@0: case TYPE_KEY: michael@0: return sizeof(Header) + body.key.size(); michael@0: case TYPE_MOTION: michael@0: return sizeof(Header) + body.motion.size(); michael@0: case TYPE_FINISHED: michael@0: return sizeof(Header) + body.finished.size(); michael@0: } michael@0: return sizeof(Header); michael@0: } michael@0: michael@0: michael@0: // --- InputChannel --- michael@0: michael@0: InputChannel::InputChannel(const String8& name, int fd) : michael@0: mName(name), mFd(fd) { michael@0: #if DEBUG_CHANNEL_LIFECYCLE michael@0: ALOGD("Input channel constructed: name='%s', fd=%d", michael@0: mName.string(), fd); michael@0: #endif michael@0: michael@0: int result = fcntl(mFd, F_SETFL, O_NONBLOCK); michael@0: LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " michael@0: "non-blocking. errno=%d", mName.string(), errno); michael@0: } michael@0: michael@0: InputChannel::~InputChannel() { michael@0: #if DEBUG_CHANNEL_LIFECYCLE michael@0: ALOGD("Input channel destroyed: name='%s', fd=%d", michael@0: mName.string(), mFd); michael@0: #endif michael@0: michael@0: ::close(mFd); michael@0: } michael@0: michael@0: status_t InputChannel::openInputChannelPair(const String8& name, michael@0: sp& outServerChannel, sp& outClientChannel) { michael@0: int sockets[2]; michael@0: if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { michael@0: status_t result = -errno; michael@0: ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", michael@0: name.string(), errno); michael@0: outServerChannel.clear(); michael@0: outClientChannel.clear(); michael@0: return result; michael@0: } michael@0: michael@0: int bufferSize = SOCKET_BUFFER_SIZE; michael@0: setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); michael@0: setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); michael@0: setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); michael@0: setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); michael@0: michael@0: String8 serverChannelName = name; michael@0: serverChannelName.append(" (server)"); michael@0: outServerChannel = new InputChannel(serverChannelName, sockets[0]); michael@0: michael@0: String8 clientChannelName = name; michael@0: clientChannelName.append(" (client)"); michael@0: outClientChannel = new InputChannel(clientChannelName, sockets[1]); michael@0: return OK; michael@0: } michael@0: michael@0: status_t InputChannel::sendMessage(const InputMessage* msg) { michael@0: size_t msgLength = msg->size(); michael@0: ssize_t nWrite; michael@0: do { michael@0: nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); michael@0: } while (nWrite == -1 && errno == EINTR); michael@0: michael@0: if (nWrite < 0) { michael@0: int error = errno; michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(), michael@0: msg->header.type, error); michael@0: #endif michael@0: if (error == EAGAIN || error == EWOULDBLOCK) { michael@0: return WOULD_BLOCK; michael@0: } michael@0: if (error == EPIPE || error == ENOTCONN) { michael@0: return DEAD_OBJECT; michael@0: } michael@0: return -error; michael@0: } michael@0: michael@0: if (size_t(nWrite) != msgLength) { michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", michael@0: mName.string(), msg->header.type); michael@0: #endif michael@0: return DEAD_OBJECT; michael@0: } michael@0: michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type); michael@0: #endif michael@0: return OK; michael@0: } michael@0: michael@0: status_t InputChannel::receiveMessage(InputMessage* msg) { michael@0: ssize_t nRead; michael@0: do { michael@0: nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); michael@0: } while (nRead == -1 && errno == EINTR); michael@0: michael@0: if (nRead < 0) { michael@0: int error = errno; michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno); michael@0: #endif michael@0: if (error == EAGAIN || error == EWOULDBLOCK) { michael@0: return WOULD_BLOCK; michael@0: } michael@0: if (error == EPIPE || error == ENOTCONN) { michael@0: return DEAD_OBJECT; michael@0: } michael@0: return -error; michael@0: } michael@0: michael@0: if (nRead == 0) { // check for EOF michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string()); michael@0: #endif michael@0: return DEAD_OBJECT; michael@0: } michael@0: michael@0: if (!msg->isValid(nRead)) { michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ received invalid message", mName.string()); michael@0: #endif michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: #if DEBUG_CHANNEL_MESSAGES michael@0: ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type); michael@0: #endif michael@0: return OK; michael@0: } michael@0: michael@0: sp InputChannel::dup() const { michael@0: int fd = ::dup(getFd()); michael@0: return fd >= 0 ? new InputChannel(getName(), fd) : NULL; michael@0: } michael@0: michael@0: michael@0: // --- InputPublisher --- michael@0: michael@0: InputPublisher::InputPublisher(const sp& channel) : michael@0: mChannel(channel) { michael@0: } michael@0: michael@0: InputPublisher::~InputPublisher() { michael@0: } michael@0: michael@0: status_t InputPublisher::publishKeyEvent( michael@0: uint32_t seq, michael@0: int32_t deviceId, michael@0: int32_t source, michael@0: int32_t action, michael@0: int32_t flags, michael@0: int32_t keyCode, michael@0: int32_t scanCode, michael@0: int32_t metaState, michael@0: int32_t repeatCount, michael@0: nsecs_t downTime, michael@0: nsecs_t eventTime) { michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " michael@0: "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," michael@0: "downTime=%lld, eventTime=%lld", michael@0: mChannel->getName().string(), seq, michael@0: deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, michael@0: downTime, eventTime); michael@0: #endif michael@0: michael@0: if (!seq) { michael@0: ALOGE("Attempted to publish a key event with sequence number 0."); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: InputMessage msg; michael@0: msg.header.type = InputMessage::TYPE_KEY; michael@0: msg.body.key.seq = seq; michael@0: msg.body.key.deviceId = deviceId; michael@0: msg.body.key.source = source; michael@0: msg.body.key.action = action; michael@0: msg.body.key.flags = flags; michael@0: msg.body.key.keyCode = keyCode; michael@0: msg.body.key.scanCode = scanCode; michael@0: msg.body.key.metaState = metaState; michael@0: msg.body.key.repeatCount = repeatCount; michael@0: msg.body.key.downTime = downTime; michael@0: msg.body.key.eventTime = eventTime; michael@0: return mChannel->sendMessage(&msg); michael@0: } michael@0: michael@0: status_t InputPublisher::publishMotionEvent( michael@0: uint32_t seq, michael@0: int32_t deviceId, michael@0: int32_t source, michael@0: int32_t action, michael@0: int32_t flags, michael@0: int32_t edgeFlags, michael@0: int32_t metaState, michael@0: int32_t buttonState, michael@0: float xOffset, michael@0: float yOffset, michael@0: float xPrecision, michael@0: float yPrecision, michael@0: nsecs_t downTime, michael@0: nsecs_t eventTime, michael@0: size_t pointerCount, michael@0: const PointerProperties* pointerProperties, michael@0: const PointerCoords* pointerCoords) { michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " michael@0: "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " michael@0: "xOffset=%f, yOffset=%f, " michael@0: "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " michael@0: "pointerCount=%d", michael@0: mChannel->getName().string(), seq, michael@0: deviceId, source, action, flags, edgeFlags, metaState, buttonState, michael@0: xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); michael@0: #endif michael@0: michael@0: if (!seq) { michael@0: ALOGE("Attempted to publish a motion event with sequence number 0."); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: if (pointerCount > MAX_POINTERS || pointerCount < 1) { michael@0: ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", michael@0: mChannel->getName().string(), pointerCount); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: InputMessage msg; michael@0: msg.header.type = InputMessage::TYPE_MOTION; michael@0: msg.body.motion.seq = seq; michael@0: msg.body.motion.deviceId = deviceId; michael@0: msg.body.motion.source = source; michael@0: msg.body.motion.action = action; michael@0: msg.body.motion.flags = flags; michael@0: msg.body.motion.edgeFlags = edgeFlags; michael@0: msg.body.motion.metaState = metaState; michael@0: msg.body.motion.buttonState = buttonState; michael@0: msg.body.motion.xOffset = xOffset; michael@0: msg.body.motion.yOffset = yOffset; michael@0: msg.body.motion.xPrecision = xPrecision; michael@0: msg.body.motion.yPrecision = yPrecision; michael@0: msg.body.motion.downTime = downTime; michael@0: msg.body.motion.eventTime = eventTime; michael@0: msg.body.motion.pointerCount = pointerCount; michael@0: for (size_t i = 0; i < pointerCount; i++) { michael@0: msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); michael@0: msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); michael@0: } michael@0: return mChannel->sendMessage(&msg); michael@0: } michael@0: michael@0: status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' publisher ~ receiveFinishedSignal", michael@0: mChannel->getName().string()); michael@0: #endif michael@0: michael@0: InputMessage msg; michael@0: status_t result = mChannel->receiveMessage(&msg); michael@0: if (result) { michael@0: *outSeq = 0; michael@0: *outHandled = false; michael@0: return result; michael@0: } michael@0: if (msg.header.type != InputMessage::TYPE_FINISHED) { michael@0: ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", michael@0: mChannel->getName().string(), msg.header.type); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: *outSeq = msg.body.finished.seq; michael@0: *outHandled = msg.body.finished.handled; michael@0: return OK; michael@0: } michael@0: michael@0: // --- InputConsumer --- michael@0: michael@0: InputConsumer::InputConsumer(const sp& channel) : michael@0: mResampleTouch(isTouchResamplingEnabled()), michael@0: mChannel(channel), mMsgDeferred(false) { michael@0: } michael@0: michael@0: InputConsumer::~InputConsumer() { michael@0: } michael@0: michael@0: bool InputConsumer::isTouchResamplingEnabled() { michael@0: char value[PROPERTY_VALUE_MAX]; michael@0: int length = property_get("debug.inputconsumer.resample", value, NULL); michael@0: if (length > 0) { michael@0: if (!strcmp("0", value)) { michael@0: return false; michael@0: } michael@0: if (strcmp("1", value)) { michael@0: ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. " michael@0: "Use '1' or '0'."); michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: status_t InputConsumer::consume(InputEventFactoryInterface* factory, michael@0: bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld", michael@0: mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime); michael@0: #endif michael@0: michael@0: *outSeq = 0; michael@0: *outEvent = NULL; michael@0: michael@0: // Fetch the next input message. michael@0: // Loop until an event can be returned or no additional events are received. michael@0: while (!*outEvent) { michael@0: if (mMsgDeferred) { michael@0: // mMsg contains a valid input message from the previous call to consume michael@0: // that has not yet been processed. michael@0: mMsgDeferred = false; michael@0: } else { michael@0: // Receive a fresh message. michael@0: status_t result = mChannel->receiveMessage(&mMsg); michael@0: if (result) { michael@0: // Consume the next batched event unless batches are being held for later. michael@0: if (consumeBatches || result != WOULD_BLOCK) { michael@0: result = consumeBatch(factory, frameTime, outSeq, outEvent); michael@0: if (*outEvent) { michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", michael@0: mChannel->getName().string(), *outSeq); michael@0: #endif michael@0: break; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: switch (mMsg.header.type) { michael@0: case InputMessage::TYPE_KEY: { michael@0: KeyEvent* keyEvent = factory->createKeyEvent(); michael@0: if (!keyEvent) return NO_MEMORY; michael@0: michael@0: initializeKeyEvent(keyEvent, &mMsg); michael@0: *outSeq = mMsg.body.key.seq; michael@0: *outEvent = keyEvent; michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", michael@0: mChannel->getName().string(), *outSeq); michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: case AINPUT_EVENT_TYPE_MOTION: { michael@0: ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); michael@0: if (batchIndex >= 0) { michael@0: Batch& batch = mBatches.editItemAt(batchIndex); michael@0: if (canAddSample(batch, &mMsg)) { michael@0: batch.samples.push(mMsg); michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ appended to batch event", michael@0: mChannel->getName().string()); michael@0: #endif michael@0: break; michael@0: } else { michael@0: // We cannot append to the batch in progress, so we need to consume michael@0: // the previous batch right now and defer the new message until later. michael@0: mMsgDeferred = true; michael@0: status_t result = consumeSamples(factory, michael@0: batch, batch.samples.size(), outSeq, outEvent); michael@0: mBatches.removeAt(batchIndex); michael@0: if (result) { michael@0: return result; michael@0: } michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ consumed batch event and " michael@0: "deferred current event, seq=%u", michael@0: mChannel->getName().string(), *outSeq); michael@0: #endif michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Start a new batch if needed. michael@0: if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE michael@0: || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { michael@0: mBatches.push(); michael@0: Batch& batch = mBatches.editTop(); michael@0: batch.samples.push(mMsg); michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ started batch event", michael@0: mChannel->getName().string()); michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: MotionEvent* motionEvent = factory->createMotionEvent(); michael@0: if (! motionEvent) return NO_MEMORY; michael@0: michael@0: updateTouchState(&mMsg); michael@0: initializeMotionEvent(motionEvent, &mMsg); michael@0: *outSeq = mMsg.body.motion.seq; michael@0: *outEvent = motionEvent; michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", michael@0: mChannel->getName().string(), *outSeq); michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", michael@0: mChannel->getName().string(), mMsg.header.type); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: } michael@0: return OK; michael@0: } michael@0: michael@0: status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, michael@0: nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { michael@0: status_t result; michael@0: for (size_t i = mBatches.size(); i-- > 0; ) { michael@0: Batch& batch = mBatches.editItemAt(i); michael@0: if (frameTime < 0) { michael@0: result = consumeSamples(factory, batch, batch.samples.size(), michael@0: outSeq, outEvent); michael@0: mBatches.removeAt(i); michael@0: return result; michael@0: } michael@0: michael@0: nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY; michael@0: ssize_t split = findSampleNoLaterThan(batch, sampleTime); michael@0: if (split < 0) { michael@0: continue; michael@0: } michael@0: michael@0: result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); michael@0: const InputMessage* next; michael@0: if (batch.samples.isEmpty()) { michael@0: mBatches.removeAt(i); michael@0: next = NULL; michael@0: } else { michael@0: next = &batch.samples.itemAt(0); michael@0: } michael@0: if (!result) { michael@0: resampleTouchState(sampleTime, static_cast(*outEvent), next); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: return WOULD_BLOCK; michael@0: } michael@0: michael@0: status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, michael@0: Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { michael@0: MotionEvent* motionEvent = factory->createMotionEvent(); michael@0: if (! motionEvent) return NO_MEMORY; michael@0: michael@0: uint32_t chain = 0; michael@0: for (size_t i = 0; i < count; i++) { michael@0: InputMessage& msg = batch.samples.editItemAt(i); michael@0: updateTouchState(&msg); michael@0: if (i) { michael@0: SeqChain seqChain; michael@0: seqChain.seq = msg.body.motion.seq; michael@0: seqChain.chain = chain; michael@0: mSeqChains.push(seqChain); michael@0: addSample(motionEvent, &msg); michael@0: } else { michael@0: initializeMotionEvent(motionEvent, &msg); michael@0: } michael@0: chain = msg.body.motion.seq; michael@0: } michael@0: batch.samples.removeItemsAt(0, count); michael@0: michael@0: *outSeq = chain; michael@0: *outEvent = motionEvent; michael@0: return OK; michael@0: } michael@0: michael@0: void InputConsumer::updateTouchState(InputMessage* msg) { michael@0: if (!mResampleTouch || michael@0: !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { michael@0: return; michael@0: } michael@0: michael@0: int32_t deviceId = msg->body.motion.deviceId; michael@0: int32_t source = msg->body.motion.source; michael@0: nsecs_t eventTime = msg->body.motion.eventTime; michael@0: michael@0: // Update the touch state history to incorporate the new input message. michael@0: // If the message is in the past relative to the most recently produced resampled michael@0: // touch, then use the resampled time and coordinates instead. michael@0: switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) { michael@0: case AMOTION_EVENT_ACTION_DOWN: { michael@0: ssize_t index = findTouchState(deviceId, source); michael@0: if (index < 0) { michael@0: mTouchStates.push(); michael@0: index = mTouchStates.size() - 1; michael@0: } michael@0: TouchState& touchState = mTouchStates.editItemAt(index); michael@0: touchState.initialize(deviceId, source); michael@0: touchState.addHistory(msg); michael@0: break; michael@0: } michael@0: michael@0: case AMOTION_EVENT_ACTION_MOVE: { michael@0: ssize_t index = findTouchState(deviceId, source); michael@0: if (index >= 0) { michael@0: TouchState& touchState = mTouchStates.editItemAt(index); michael@0: touchState.addHistory(msg); michael@0: if (eventTime < touchState.lastResample.eventTime) { michael@0: rewriteMessage(touchState, msg); michael@0: } else { michael@0: touchState.lastResample.idBits.clear(); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AMOTION_EVENT_ACTION_POINTER_DOWN: { michael@0: ssize_t index = findTouchState(deviceId, source); michael@0: if (index >= 0) { michael@0: TouchState& touchState = mTouchStates.editItemAt(index); michael@0: touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); michael@0: rewriteMessage(touchState, msg); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AMOTION_EVENT_ACTION_POINTER_UP: { michael@0: ssize_t index = findTouchState(deviceId, source); michael@0: if (index >= 0) { michael@0: TouchState& touchState = mTouchStates.editItemAt(index); michael@0: rewriteMessage(touchState, msg); michael@0: touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AMOTION_EVENT_ACTION_SCROLL: { michael@0: ssize_t index = findTouchState(deviceId, source); michael@0: if (index >= 0) { michael@0: const TouchState& touchState = mTouchStates.itemAt(index); michael@0: rewriteMessage(touchState, msg); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case AMOTION_EVENT_ACTION_UP: michael@0: case AMOTION_EVENT_ACTION_CANCEL: { michael@0: ssize_t index = findTouchState(deviceId, source); michael@0: if (index >= 0) { michael@0: const TouchState& touchState = mTouchStates.itemAt(index); michael@0: rewriteMessage(touchState, msg); michael@0: mTouchStates.removeAt(index); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) { michael@0: for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { michael@0: uint32_t id = msg->body.motion.pointers[i].properties.id; michael@0: if (state.lastResample.idBits.hasBit(id)) { michael@0: PointerCoords& msgCoords = msg->body.motion.pointers[i].coords; michael@0: const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, michael@0: resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X), michael@0: resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y), michael@0: msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X), michael@0: msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); michael@0: #endif michael@0: msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); michael@0: msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, michael@0: const InputMessage* next) { michael@0: if (!mResampleTouch michael@0: || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER) michael@0: || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { michael@0: return; michael@0: } michael@0: michael@0: ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); michael@0: if (index < 0) { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Not resampled, no touch state for device."); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: TouchState& touchState = mTouchStates.editItemAt(index); michael@0: if (touchState.historySize < 1) { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Not resampled, no history for device."); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: // Ensure that the current sample has all of the pointers that need to be reported. michael@0: const History* current = touchState.getHistory(0); michael@0: size_t pointerCount = event->getPointerCount(); michael@0: for (size_t i = 0; i < pointerCount; i++) { michael@0: uint32_t id = event->getPointerId(i); michael@0: if (!current->idBits.hasBit(id)) { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Not resampled, missing id %d", id); michael@0: #endif michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Find the data to use for resampling. michael@0: const History* other; michael@0: History future; michael@0: float alpha; michael@0: if (next) { michael@0: // Interpolate between current sample and future sample. michael@0: // So current->eventTime <= sampleTime <= future.eventTime. michael@0: future.initializeFrom(next); michael@0: other = &future; michael@0: nsecs_t delta = future.eventTime - current->eventTime; michael@0: if (delta < RESAMPLE_MIN_DELTA) { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Not resampled, delta time is %lld ns.", delta); michael@0: #endif michael@0: return; michael@0: } michael@0: alpha = float(sampleTime - current->eventTime) / delta; michael@0: } else if (touchState.historySize >= 2) { michael@0: // Extrapolate future sample using current sample and past sample. michael@0: // So other->eventTime <= current->eventTime <= sampleTime. michael@0: other = touchState.getHistory(1); michael@0: nsecs_t delta = current->eventTime - other->eventTime; michael@0: if (delta < RESAMPLE_MIN_DELTA) { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Not resampled, delta time is %lld ns.", delta); michael@0: #endif michael@0: return; michael@0: } michael@0: nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); michael@0: if (sampleTime > maxPredict) { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Sample time is too far in the future, adjusting prediction " michael@0: "from %lld to %lld ns.", michael@0: sampleTime - current->eventTime, maxPredict - current->eventTime); michael@0: #endif michael@0: sampleTime = maxPredict; michael@0: } michael@0: alpha = float(current->eventTime - sampleTime) / delta; michael@0: } else { michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("Not resampled, insufficient data."); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: // Resample touch coordinates. michael@0: touchState.lastResample.eventTime = sampleTime; michael@0: touchState.lastResample.idBits.clear(); michael@0: for (size_t i = 0; i < pointerCount; i++) { michael@0: uint32_t id = event->getPointerId(i); michael@0: touchState.lastResample.idToIndex[id] = i; michael@0: touchState.lastResample.idBits.markBit(id); michael@0: PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; michael@0: const PointerCoords& currentCoords = current->getPointerById(id); michael@0: if (other->idBits.hasBit(id) michael@0: && shouldResampleTool(event->getToolType(i))) { michael@0: const PointerCoords& otherCoords = other->getPointerById(id); michael@0: resampledCoords.copyFrom(currentCoords); michael@0: resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, michael@0: lerp(currentCoords.getX(), otherCoords.getX(), alpha)); michael@0: resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, michael@0: lerp(currentCoords.getY(), otherCoords.getY(), alpha)); michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " michael@0: "other (%0.3f, %0.3f), alpha %0.3f", michael@0: id, resampledCoords.getX(), resampledCoords.getY(), michael@0: currentCoords.getX(), currentCoords.getY(), michael@0: otherCoords.getX(), otherCoords.getY(), michael@0: alpha); michael@0: #endif michael@0: } else { michael@0: resampledCoords.copyFrom(currentCoords); michael@0: #if DEBUG_RESAMPLING michael@0: ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", michael@0: id, resampledCoords.getX(), resampledCoords.getY(), michael@0: currentCoords.getX(), currentCoords.getY()); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: event->addSample(sampleTime, touchState.lastResample.pointers); michael@0: } michael@0: michael@0: bool InputConsumer::shouldResampleTool(int32_t toolType) { michael@0: return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER michael@0: || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN; michael@0: } michael@0: michael@0: status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { michael@0: #if DEBUG_TRANSPORT_ACTIONS michael@0: ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", michael@0: mChannel->getName().string(), seq, handled ? "true" : "false"); michael@0: #endif michael@0: michael@0: if (!seq) { michael@0: ALOGE("Attempted to send a finished signal with sequence number 0."); michael@0: return BAD_VALUE; michael@0: } michael@0: michael@0: // Send finished signals for the batch sequence chain first. michael@0: size_t seqChainCount = mSeqChains.size(); michael@0: if (seqChainCount) { michael@0: uint32_t currentSeq = seq; michael@0: uint32_t chainSeqs[seqChainCount]; michael@0: size_t chainIndex = 0; michael@0: for (size_t i = seqChainCount; i-- > 0; ) { michael@0: const SeqChain& seqChain = mSeqChains.itemAt(i); michael@0: if (seqChain.seq == currentSeq) { michael@0: currentSeq = seqChain.chain; michael@0: chainSeqs[chainIndex++] = currentSeq; michael@0: mSeqChains.removeAt(i); michael@0: } michael@0: } michael@0: status_t status = OK; michael@0: while (!status && chainIndex-- > 0) { michael@0: status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); michael@0: } michael@0: if (status) { michael@0: // An error occurred so at least one signal was not sent, reconstruct the chain. michael@0: do { michael@0: SeqChain seqChain; michael@0: seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; michael@0: seqChain.chain = chainSeqs[chainIndex]; michael@0: mSeqChains.push(seqChain); michael@0: } while (chainIndex-- > 0); michael@0: return status; michael@0: } michael@0: } michael@0: michael@0: // Send finished signal for the last message in the batch. michael@0: return sendUnchainedFinishedSignal(seq, handled); michael@0: } michael@0: michael@0: status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { michael@0: InputMessage msg; michael@0: msg.header.type = InputMessage::TYPE_FINISHED; michael@0: msg.body.finished.seq = seq; michael@0: msg.body.finished.handled = handled; michael@0: return mChannel->sendMessage(&msg); michael@0: } michael@0: michael@0: bool InputConsumer::hasDeferredEvent() const { michael@0: return mMsgDeferred; michael@0: } michael@0: michael@0: bool InputConsumer::hasPendingBatch() const { michael@0: return !mBatches.isEmpty(); michael@0: } michael@0: michael@0: ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { michael@0: for (size_t i = 0; i < mBatches.size(); i++) { michael@0: const Batch& batch = mBatches.itemAt(i); michael@0: const InputMessage& head = batch.samples.itemAt(0); michael@0: if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { michael@0: for (size_t i = 0; i < mTouchStates.size(); i++) { michael@0: const TouchState& touchState = mTouchStates.itemAt(i); michael@0: if (touchState.deviceId == deviceId && touchState.source == source) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { michael@0: event->initialize( michael@0: msg->body.key.deviceId, michael@0: msg->body.key.source, michael@0: msg->body.key.action, michael@0: msg->body.key.flags, michael@0: msg->body.key.keyCode, michael@0: msg->body.key.scanCode, michael@0: msg->body.key.metaState, michael@0: msg->body.key.repeatCount, michael@0: msg->body.key.downTime, michael@0: msg->body.key.eventTime); michael@0: } michael@0: michael@0: void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { michael@0: size_t pointerCount = msg->body.motion.pointerCount; michael@0: PointerProperties pointerProperties[pointerCount]; michael@0: PointerCoords pointerCoords[pointerCount]; michael@0: for (size_t i = 0; i < pointerCount; i++) { michael@0: pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); michael@0: pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); michael@0: } michael@0: michael@0: event->initialize( michael@0: msg->body.motion.deviceId, michael@0: msg->body.motion.source, michael@0: msg->body.motion.action, michael@0: msg->body.motion.flags, michael@0: msg->body.motion.edgeFlags, michael@0: msg->body.motion.metaState, michael@0: msg->body.motion.buttonState, michael@0: msg->body.motion.xOffset, michael@0: msg->body.motion.yOffset, michael@0: msg->body.motion.xPrecision, michael@0: msg->body.motion.yPrecision, michael@0: msg->body.motion.downTime, michael@0: msg->body.motion.eventTime, michael@0: pointerCount, michael@0: pointerProperties, michael@0: pointerCoords); michael@0: } michael@0: michael@0: void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { michael@0: size_t pointerCount = msg->body.motion.pointerCount; michael@0: PointerCoords pointerCoords[pointerCount]; michael@0: for (size_t i = 0; i < pointerCount; i++) { michael@0: pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); michael@0: } michael@0: michael@0: event->setMetaState(event->getMetaState() | msg->body.motion.metaState); michael@0: event->addSample(msg->body.motion.eventTime, pointerCoords); michael@0: } michael@0: michael@0: bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { michael@0: const InputMessage& head = batch.samples.itemAt(0); michael@0: size_t pointerCount = msg->body.motion.pointerCount; michael@0: if (head.body.motion.pointerCount != pointerCount michael@0: || head.body.motion.action != msg->body.motion.action) { michael@0: return false; michael@0: } michael@0: for (size_t i = 0; i < pointerCount; i++) { michael@0: if (head.body.motion.pointers[i].properties michael@0: != msg->body.motion.pointers[i].properties) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { michael@0: size_t numSamples = batch.samples.size(); michael@0: size_t index = 0; michael@0: while (index < numSamples michael@0: && batch.samples.itemAt(index).body.motion.eventTime <= time) { michael@0: index += 1; michael@0: } michael@0: return ssize_t(index) - 1; michael@0: } michael@0: michael@0: } // namespace android