diff options
-rw-r--r-- | core/java/android/view/InputEventReceiver.java | 12 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 6 | ||||
-rw-r--r-- | core/jni/android_app_NativeActivity.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_view_InputEventReceiver.cpp | 20 | ||||
-rw-r--r-- | include/androidfw/Input.h | 1 | ||||
-rw-r--r-- | include/androidfw/InputTransport.h | 73 | ||||
-rw-r--r-- | libs/androidfw/Input.cpp | 20 | ||||
-rw-r--r-- | libs/androidfw/InputTransport.cpp | 329 | ||||
-rw-r--r-- | libs/androidfw/tests/InputPublisherAndConsumer_test.cpp | 4 |
9 files changed, 393 insertions, 74 deletions
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 6a457ec..9c56782 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -46,7 +46,8 @@ public abstract class InputEventReceiver { InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int receiverPtr); private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled); - private static native void nativeConsumeBatchedInputEvents(int receiverPtr); + private static native void nativeConsumeBatchedInputEvents(int receiverPtr, + long frameTimeNanos); /** * Creates an input event receiver bound to the specified input channel. @@ -114,7 +115,7 @@ public abstract class InputEventReceiver { * immediately (such as a pointer up event). */ public void onBatchedInputEventPending() { - consumeBatchedInputEvents(); + consumeBatchedInputEvents(-1); } /** @@ -150,13 +151,16 @@ public abstract class InputEventReceiver { * * This method forces all batched input events to be delivered immediately. * Should be called just before animating or drawing a new frame in the UI. + * + * @param frameTimeNanos The time in the {@link System#nanoTime()} time base + * when the current display frame started rendering, or -1 if unknown. */ - public final void consumeBatchedInputEvents() { + public final void consumeBatchedInputEvents(long frameTimeNanos) { if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to consume batched input events but the input event " + "receiver has already been disposed."); } else { - nativeConsumeBatchedInputEvents(mReceiverPtr); + nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index afa9b12..1ee7934 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4204,11 +4204,11 @@ public final class ViewRootImpl implements ViewParent, } } - void doConsumeBatchedInput() { + void doConsumeBatchedInput(long frameTimeNanos) { if (mConsumeBatchedInputScheduled) { mConsumeBatchedInputScheduled = false; if (mInputEventReceiver != null) { - mInputEventReceiver.consumeBatchedInputEvents(); + mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos); } doProcessInputEvents(); } @@ -4248,7 +4248,7 @@ public final class ViewRootImpl implements ViewParent, final class ConsumeBatchedInputRunnable implements Runnable { @Override public void run() { - doConsumeBatchedInput(); + doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); } } final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable = diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 19bc154..074afa3 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -207,7 +207,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) { uint32_t consumerSeq; InputEvent* myEvent = NULL; - status_t res = mConsumer.consume(&mPooledInputEventFactory, true /*consumeBatches*/, + status_t res = mConsumer.consume(&mPooledInputEventFactory, true /*consumeBatches*/, -1, &consumerSeq, &myEvent); if (res != android::OK) { if (res != android::WOULD_BLOCK) { diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 8f6f5f4..08e08b9 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -52,7 +52,7 @@ public: status_t initialize(); status_t finishInputEvent(uint32_t seq, bool handled); - status_t consumeEvents(JNIEnv* env, bool consumeBatches); + status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime); protected: virtual ~NativeInputEventReceiver(); @@ -130,15 +130,16 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v } JNIEnv* env = AndroidRuntime::getJNIEnv(); - status_t status = r->consumeEvents(env, false /*consumeBatches*/); + status_t status = r->consumeEvents(env, false /*consumeBatches*/, -1); r->mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; } -status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches) { +status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, + bool consumeBatches, nsecs_t frameTime) { #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(), - consumeBatches ? "true" : "false"); + ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%lld.", + getInputChannelName(), consumeBatches ? "true" : "false", frameTime); #endif if (consumeBatches) { @@ -150,7 +151,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatche uint32_t seq; InputEvent* inputEvent; status_t status = mInputConsumer.consume(&mInputEventFactory, - consumeBatches, &seq, &inputEvent); + consumeBatches, frameTime, &seq, &inputEvent); if (status) { if (status == WOULD_BLOCK) { if (!skipCallbacks && !mBatchedInputEventPending @@ -270,10 +271,11 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, } } -static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr) { +static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr, + jlong frameTimeNanos) { sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); - status_t status = receiver->consumeEvents(env, true /*consumeBatches*/); + status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos); if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) { String8 message; message.appendFormat("Failed to consume batched input event. status=%d", status); @@ -291,7 +293,7 @@ static JNINativeMethod gMethods[] = { (void*)nativeDispose }, { "nativeFinishInputEvent", "(IIZ)V", (void*)nativeFinishInputEvent }, - { "nativeConsumeBatchedInputEvents", "(I)V", + { "nativeConsumeBatchedInputEvents", "(IJ)V", (void*)nativeConsumeBatchedInputEvents }, }; diff --git a/include/androidfw/Input.h b/include/androidfw/Input.h index a98e1a2..044f2bf 100644 --- a/include/androidfw/Input.h +++ b/include/androidfw/Input.h @@ -208,6 +208,7 @@ struct PointerCoords { status_t setAxisValue(int32_t axis, float value); void scale(float scale); + void lerp(const PointerCoords& a, const PointerCoords& b, float alpha); inline float getX() const { return getAxisValue(AMOTION_EVENT_AXIS_X); diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h index 29c296e..2924505 100644 --- a/include/androidfw/InputTransport.h +++ b/include/androidfw/InputTransport.h @@ -33,6 +33,7 @@ #include <utils/RefBase.h> #include <utils/String8.h> #include <utils/Vector.h> +#include <utils/BitSet.h> namespace android { @@ -271,6 +272,9 @@ public: * If consumeBatches is true, then events are still batched but they are consumed * immediately as soon as the input channel is exhausted. * + * The frameTime parameter specifies the time when the current display frame started + * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. + * * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. @@ -280,7 +284,7 @@ public: * Other errors probably indicate that the channel is broken. */ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, - uint32_t* outSeq, InputEvent** outEvent); + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether @@ -298,7 +302,7 @@ public: * has a deferred event to be processed. Deferred events are somewhat special in * that they have already been removed from the input channel. If the input channel * becomes empty, the client may need to do extra work to ensure that it processes - * the deferred event despite the fact that the inptu channel's file descriptor + * the deferred event despite the fact that the input channel's file descriptor * is not readable. * * One option is simply to call consume() in a loop until it returns WOULD_BLOCK. @@ -329,11 +333,55 @@ private: // Batched motion events per device and source. struct Batch { - uint32_t seq; // sequence number of last input message batched in the event - MotionEvent event; + Vector<InputMessage> samples; }; Vector<Batch> mBatches; + // Touch state per device and source, only for sources of class pointer. + struct History { + nsecs_t eventTime; + BitSet32 idBits; + PointerCoords pointers[MAX_POINTERS]; + + void initializeFrom(const InputMessage* msg) { + eventTime = msg->body.motion.eventTime; + idBits.clear(); + for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { + uint32_t id = msg->body.motion.pointers[i].properties.id; + idBits.markBit(id); + size_t index = idBits.getIndexOfBit(id); + pointers[index].copyFrom(msg->body.motion.pointers[i].coords); + } + } + }; + struct TouchState { + int32_t deviceId; + int32_t source; + size_t historyCurrent; + size_t historySize; + History history[2]; + + void initialize(int32_t deviceId, int32_t source) { + this->deviceId = deviceId; + this->source = source; + historyCurrent = 0; + historySize = 0; + } + + void addHistory(const InputMessage* msg) { + historyCurrent ^= 1; + if (historySize < 2) { + historySize += 1; + } + history[historyCurrent].initializeFrom(msg); + } + + const History* getHistory(size_t index) const { + return &history[(historyCurrent + index) & 1]; + } + }; + Vector<TouchState> mTouchStates; + // Chain of batched sequence numbers. When multiple input messages are combined into // a batch, we append a record here that associates the last sequence number in the // batch with the previous one. When the finished signal is sent, we traverse the @@ -344,13 +392,26 @@ private: }; Vector<SeqChain> mSeqChains; + status_t consumeBatch(InputEventFactoryInterface* factory, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + status_t consumeSamples(InputEventFactoryInterface* factory, + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); + + void updateTouchState(InputMessage* msg); + void resampleTouchState(nsecs_t frameTime, MotionEvent* event, + const InputMessage *next); + ssize_t findBatch(int32_t deviceId, int32_t source) const; + ssize_t findTouchState(int32_t deviceId, int32_t source) const; + status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); - static bool canAppendSamples(const MotionEvent* event, const InputMessage* msg); - static void appendSamples(MotionEvent* event, const InputMessage* msg); + static void addSample(MotionEvent* event, const InputMessage* msg); + static bool canAddSample(const Batch& batch, const InputMessage* msg); + static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); + static bool shouldResampleTool(int32_t toolType); }; } // namespace android diff --git a/libs/androidfw/Input.cpp b/libs/androidfw/Input.cpp index 1617a3f..fbe1926 100644 --- a/libs/androidfw/Input.cpp +++ b/libs/androidfw/Input.cpp @@ -229,6 +229,26 @@ void PointerCoords::scale(float scaleFactor) { scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); } +void PointerCoords::lerp(const PointerCoords& a, const PointerCoords& b, float alpha) { + bits = 0; + for (uint64_t bitsRemaining = a.bits | b.bits; bitsRemaining; ) { + int32_t axis = __builtin_ctz(bitsRemaining); + uint64_t axisBit = 1LL << axis; + bitsRemaining &= ~axisBit; + if (a.bits & axisBit) { + if (b.bits & axisBit) { + float aval = a.getAxisValue(axis); + float bval = b.getAxisValue(axis); + setAxisValue(axis, aval + alpha * (bval - aval)); + } else { + setAxisValue(axis, a.getAxisValue(axis)); + } + } else { + setAxisValue(axis, b.getAxisValue(axis)); + } + } +} + #ifdef HAVE_ANDROID_OS status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp index 294236f..9a4182c 100644 --- a/libs/androidfw/InputTransport.cpp +++ b/libs/androidfw/InputTransport.cpp @@ -16,6 +16,9 @@ // Log debug messages about transport actions #define DEBUG_TRANSPORT_ACTIONS 0 +// Log debug messages about touch event resampling +#define DEBUG_RESAMPLING 0 + #include <cutils/log.h> #include <errno.h> @@ -24,6 +27,7 @@ #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> +#include <math.h> namespace android { @@ -34,6 +38,20 @@ namespace android { // behind processing touches. static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; +// Nanoseconds per milliseconds. +static const nsecs_t NANOS_PER_MS = 1000000; + +// Latency added during resampling. A few milliseconds doesn't hurt much but +// reduces the impact of mispredicted touch positions. +static const nsecs_t RESAMPLE_LATENCY = 4 * NANOS_PER_MS; + +// Minimum time difference between consecutive samples before attempting to resample. +static const nsecs_t RESAMPLE_MIN_DELTA = 1 * NANOS_PER_MS; + +// Maximum linear interpolation scale value. The larger this is, the more error may +// potentially be introduced. +static const float RESAMPLE_MAX_ALPHA = 2.0f; + // --- InputMessage --- @@ -341,10 +359,10 @@ InputConsumer::~InputConsumer() { } status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, uint32_t* outSeq, InputEvent** outEvent) { + bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s", - mChannel->getName().string(), consumeBatches ? "true" : "false"); + ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld", + mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime); #endif *outSeq = 0; @@ -362,20 +380,15 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, status_t result = mChannel->receiveMessage(&mMsg); if (result) { // Consume the next batched event unless batches are being held for later. - if (!mBatches.isEmpty() && (consumeBatches || result != WOULD_BLOCK)) { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - - const Batch& batch = mBatches.top(); - motionEvent->copyFrom(&batch.event, true /*keepHistory*/); - *outSeq = batch.seq; - *outEvent = motionEvent; - mBatches.pop(); + if (consumeBatches || result != WOULD_BLOCK) { + result = consumeBatch(factory, frameTime, outSeq, outEvent); + if (*outEvent) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", - mChannel->getName().string(), *outSeq); + ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().string(), *outSeq); #endif - break; + break; + } } return result; } @@ -400,35 +413,23 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); if (batchIndex >= 0) { Batch& batch = mBatches.editItemAt(batchIndex); - if (canAppendSamples(&batch.event, &mMsg)) { - // Append to the batch and save the new sequence number for the tail end. - uint32_t chain = batch.seq; - appendSamples(&batch.event, &mMsg); - batch.seq = mMsg.body.motion.seq; - - // Update the sequence number chain. - SeqChain seqChain; - seqChain.seq = batch.seq; - seqChain.chain = chain; - mSeqChains.push(seqChain); + if (canAddSample(batch, &mMsg)) { + batch.samples.push(mMsg); #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ appended to batch event", mChannel->getName().string()); #endif break; } else { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - // We cannot append to the batch in progress, so we need to consume // the previous batch right now and defer the new message until later. mMsgDeferred = true; - - // Return the end of the previous batch. - motionEvent->copyFrom(&batch.event, true /*keepHistory*/); - *outSeq = batch.seq; - *outEvent = motionEvent; + status_t result = consumeSamples(factory, + batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(batchIndex); + if (result) { + return result; + } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event and " "deferred current event, seq=%u", @@ -443,8 +444,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { mBatches.push(); Batch& batch = mBatches.editTop(); - batch.seq = mMsg.body.motion.seq; - initializeMotionEvent(&batch.event, &mMsg); + batch.samples.push(mMsg); #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ started batch event", mChannel->getName().string()); @@ -455,6 +455,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; + updateTouchState(&mMsg); initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; @@ -474,6 +475,213 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, return OK; } +status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { + status_t result; + for (size_t i = mBatches.size(); i-- > 0; ) { + Batch& batch = mBatches.editItemAt(i); + if (frameTime < 0) { + result = consumeSamples(factory, batch, batch.samples.size(), + outSeq, outEvent); + mBatches.removeAt(i); + return result; + } + + nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY; + ssize_t split = findSampleNoLaterThan(batch, sampleTime); + if (split < 0) { + continue; + } + + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); + const InputMessage* next; + if (batch.samples.isEmpty()) { + mBatches.removeAt(i); + next = NULL; + } else { + next = &batch.samples.itemAt(0); + } + if (!result) { + resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); + } + return result; + } + + return WOULD_BLOCK; +} + +status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + uint32_t chain = 0; + for (size_t i = 0; i < count; i++) { + InputMessage& msg = batch.samples.editItemAt(i); + updateTouchState(&msg); + if (i) { + SeqChain seqChain; + seqChain.seq = msg.body.motion.seq; + seqChain.chain = chain; + mSeqChains.push(seqChain); + addSample(motionEvent, &msg); + } else { + initializeMotionEvent(motionEvent, &msg); + } + chain = msg.body.motion.seq; + } + batch.samples.removeItemsAt(0, count); + + *outSeq = chain; + *outEvent = motionEvent; + return OK; +} + +void InputConsumer::updateTouchState(InputMessage* msg) { + if (!(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { + return; + } + + int32_t deviceId = msg->body.motion.deviceId; + int32_t source = msg->body.motion.source; + + // TODO: Filter the incoming touch event so that it aligns better + // with prior predictions. Turning RESAMPLE_LATENCY offsets the need + // for filtering but it would be nice to reduce the latency further. + + switch (msg->body.motion.action) { + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index < 0) { + mTouchStates.push(); + index = mTouchStates.size() - 1; + } + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.initialize(deviceId, source); + touchState.addHistory(msg); + break; + } + + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.addHistory(msg); + } + break; + } + + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + mTouchStates.removeAt(index); + } + break; + } + } +} + +void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, + const InputMessage* next) { + if (event->getAction() != AMOTION_EVENT_ACTION_MOVE + || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER)) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, not a move."); +#endif + return; + } + + ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); + if (index < 0) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, no touch state for device."); +#endif + return; + } + + TouchState& touchState = mTouchStates.editItemAt(index); + if (touchState.historySize < 1) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, no history for device."); +#endif + return; + } + + const History* current = touchState.getHistory(0); + const History* other; + History future; + if (next) { + future.initializeFrom(next); + other = &future; + } else if (touchState.historySize >= 2) { + other = touchState.getHistory(1); + } else { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, insufficient data."); +#endif + return; + } + + nsecs_t delta = current->eventTime - other->eventTime; + if (delta > -RESAMPLE_MIN_DELTA && delta < RESAMPLE_MIN_DELTA) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, delta time is %lld", delta); +#endif + return; + } + + float alpha = float(current->eventTime - sampleTime) / delta; + if (fabs(alpha) > RESAMPLE_MAX_ALPHA) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, alpha is %f", alpha); +#endif + return; + } + + size_t pointerCount = event->getPointerCount(); + PointerCoords resampledCoords[MAX_POINTERS]; + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + if (!current->idBits.hasBit(id)) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, missing id %d", id); +#endif + return; + } + const PointerCoords& currentCoords = + current->pointers[current->idBits.getIndexOfBit(id)]; + if (other->idBits.hasBit(id) + && shouldResampleTool(event->getToolType(i))) { + const PointerCoords& otherCoords = + other->pointers[other->idBits.getIndexOfBit(id)]; + resampledCoords[i].lerp(currentCoords, otherCoords, alpha); +#if DEBUG_RESAMPLING + ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + i, resampledCoords[i].getX(), resampledCoords[i].getY(), + currentCoords.getX(), currentCoords.getY(), + otherCoords.getX(), otherCoords.getY(), + alpha); +#endif + } else { + resampledCoords[i].copyFrom(currentCoords); +#if DEBUG_RESAMPLING + ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", + i, resampledCoords[i].getX(), resampledCoords[i].getY(), + currentCoords.getX(), currentCoords.getY()); +#endif + } + } + + event->addSample(sampleTime, resampledCoords); +} + +bool InputConsumer::shouldResampleTool(int32_t toolType) { + return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER + || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", @@ -538,7 +746,18 @@ bool InputConsumer::hasPendingBatch() const { ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { const Batch& batch = mBatches.itemAt(i); - if (batch.event.getDeviceId() == deviceId && batch.event.getSource() == source) { + const InputMessage& head = batch.samples.itemAt(0); + if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { + return i; + } + } + return -1; +} + +ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mTouchStates.size(); i++) { + const TouchState& touchState = mTouchStates.itemAt(i); + if (touchState.deviceId == deviceId && touchState.source == source) { return i; } } @@ -587,29 +806,41 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords); } -bool InputConsumer::canAppendSamples(const MotionEvent* event, const InputMessage *msg) { +void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->setMetaState(event->getMetaState() | msg->body.motion.metaState); + event->addSample(msg->body.motion.eventTime, pointerCoords); +} + +bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { + const InputMessage& head = batch.samples.itemAt(0); size_t pointerCount = msg->body.motion.pointerCount; - if (event->getPointerCount() != pointerCount - || event->getAction() != msg->body.motion.action) { + if (head.body.motion.pointerCount != pointerCount + || head.body.motion.action != msg->body.motion.action) { return false; } for (size_t i = 0; i < pointerCount; i++) { - if (*event->getPointerProperties(i) != msg->body.motion.pointers[i].properties) { + if (head.body.motion.pointers[i].properties + != msg->body.motion.pointers[i].properties) { return false; } } return true; } -void InputConsumer::appendSamples(MotionEvent* event, const InputMessage* msg) { - size_t pointerCount = msg->body.motion.pointerCount; - PointerCoords pointerCoords[pointerCount]; - for (size_t i = 0; i < pointerCount; i++) { - pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); +ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { + size_t numSamples = batch.samples.size(); + size_t index = 0; + while (index < numSamples + && batch.samples.itemAt(index).body.motion.eventTime <= time) { + index += 1; } - - event->setMetaState(event->getMetaState() | msg->body.motion.metaState); - event->addSample(msg->body.motion.eventTime, pointerCoords); + return ssize_t(index) - 1; } } // namespace android diff --git a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp index 442b62f..f45774b 100644 --- a/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp @@ -88,7 +88,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; @@ -171,7 +171,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, &consumeSeq, &event); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; |