Sync MT slots on reset and buffer overflow
At present we don't repopulate the MT slot values when a buffer overflow
occurs. This may lead to incosistent event being generate due to out of
sync Mt Slot values.
In this change we repopulate the MT slot values with EVIOCGMTSLOTS
system call after resetting the mapper. Now in case of a buffer overflow
a ongoing multitouch gesture will be cancelled and restarted.
Test: atest inputflinger_tests
Bug: b/291626046
Change-Id: I8195406ce1f9e3e704381b47e282c65463537745
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index f7bbc51..40e8628 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1141,6 +1141,22 @@
return OK;
}
+base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const {
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->absBitmask.test(axis)) {
+ return base::ResultError("device problem or axis not supported", NAME_NOT_FOUND);
+ }
+ std::vector<int32_t> outValues(slotCount + 1);
+ outValues[0] = axis;
+ const size_t bufferSize = outValues.size() * sizeof(int32_t);
+ if (ioctl(device->fd, EVIOCGMTSLOTS(bufferSize), outValues.data()) != OK) {
+ return base::ErrnoError();
+ }
+ return std::move(outValues);
+}
+
bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index fb32f96..a41064b 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -350,6 +350,7 @@
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out += reset(rawEvent->when);
mDropUntilNextSync = false;
ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");
} else {
@@ -359,7 +360,6 @@
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
mDropUntilNextSync = true;
- out += reset(rawEvent->when);
} else {
for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
out += mapper.process(rawEvent);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 0bcab42..a7e0675 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -336,6 +336,10 @@
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const = 0;
+ /* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size
+ * (slotCount + 1). The value at the 0 index is set to queried axis. */
+ virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const = 0;
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/*
@@ -552,6 +556,8 @@
int32_t locationKeyCode) const override final;
status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const override final;
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override final;
bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 31dcb2e..c076d33 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -372,6 +372,10 @@
inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
}
+ inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis,
+ size_t slotCount) const {
+ return mEventHub->getMtSlotValues(mId, axis, slotCount);
+ }
inline bool markSupportedKeyCodes(const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const {
return mEventHub->markSupportedKeyCodes(mId, keyCodes, outFlags);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 2dd05f5..0c58dab 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -35,12 +35,8 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
- // The evdev multi-touch protocol does not allow userspace applications to query the initial or
- // current state of the pointers at any time. This means if we clear our accumulated state when
- // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
- // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
- // rebuilding the state from scratch, we work around this kernel API limitation by never
- // fully clearing any state specific to the multi-touch protocol.
+ mPointerIdBits.clear();
+ mMultiTouchMotionAccumulator.reset(mDeviceContext);
return TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0fc903..b3f1700 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -30,24 +30,12 @@
size_t slotCount, bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
mSlots = std::vector<Slot>(slotCount);
+ populateCurrentSlot(deviceContext);
+}
- mCurrentSlot = -1;
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot before we
- // start reading events from the device. It is possible that the current slot index will
- // not be the same as it was when the first event was written into the evdev buffer, which
- // means the input mapper could start out of sync with the initial state of the events in
- // the evdev buffer. In the extremely unlikely case that this happens, the data from two
- // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
- // touch point to "jump", but at least there will be no stuck touches.
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
- } else {
- ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
- }
- }
+void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) {
+ resetSlots();
+ syncSlots(deviceContext);
}
void MultiTouchMotionAccumulator::resetSlots() {
@@ -84,54 +72,10 @@
if (!mUsingSlotsProtocol) {
slot.mInUse = true;
}
-
- switch (rawEvent->code) {
- case ABS_MT_POSITION_X:
- slot.mAbsMtPositionX = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_POSITION_Y:
- slot.mAbsMtPositionY = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_TOUCH_MAJOR:
- slot.mAbsMtTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- slot.mAbsMtTouchMinor = rawEvent->value;
- slot.mHaveAbsMtTouchMinor = true;
- break;
- case ABS_MT_WIDTH_MAJOR:
- slot.mAbsMtWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- slot.mAbsMtWidthMinor = rawEvent->value;
- slot.mHaveAbsMtWidthMinor = true;
- break;
- case ABS_MT_ORIENTATION:
- slot.mAbsMtOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (mUsingSlotsProtocol && rawEvent->value < 0) {
- // The slot is no longer in use but it retains its previous contents,
- // which may be reused for subsequent touches.
- slot.mInUse = false;
- } else {
- slot.mInUse = true;
- slot.mAbsMtTrackingId = rawEvent->value;
- }
- break;
- case ABS_MT_PRESSURE:
- slot.mAbsMtPressure = rawEvent->value;
- break;
- case ABS_MT_DISTANCE:
- slot.mAbsMtDistance = rawEvent->value;
- break;
- case ABS_MT_TOOL_TYPE:
- slot.mAbsMtToolType = rawEvent->value;
- slot.mHaveAbsMtToolType = true;
- break;
+ if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) {
+ warnIfNotInUse(*rawEvent, slot);
}
+ slot.populateAxisValue(rawEvent->code, rawEvent->value);
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
@@ -139,6 +83,36 @@
}
}
+void MultiTouchMotionAccumulator::syncSlots(const InputDeviceContext& deviceContext) {
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+ constexpr std::array<int32_t, 11> axisCodes = {ABS_MT_POSITION_X, ABS_MT_POSITION_Y,
+ ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR,
+ ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR,
+ ABS_MT_ORIENTATION, ABS_MT_TRACKING_ID,
+ ABS_MT_PRESSURE, ABS_MT_DISTANCE,
+ ABS_MT_TOOL_TYPE};
+ const size_t numSlots = mSlots.size();
+ for (int32_t axisCode : axisCodes) {
+ if (!deviceContext.hasAbsoluteAxis(axisCode)) {
+ continue;
+ }
+ const auto result = deviceContext.getMtSlotValues(axisCode, numSlots);
+ if (result.ok()) {
+ const std::vector<int32_t>& mtSlotValues = result.value();
+ for (size_t i = 1; i <= numSlots; ++i) {
+ // The returned slot values are in a 1-indexed vector of size numSlots + 1.
+ mSlots[i - 1].populateAxisValue(axisCode, mtSlotValues[i]);
+ }
+ } else {
+ ALOGE("Could not retrieve multi-touch slot value for axis=%d error=%s status=%d",
+ axisCode, result.error().message().c_str(), result.error().code().value());
+ }
+ }
+ populateCurrentSlot(deviceContext);
+}
+
void MultiTouchMotionAccumulator::finishSync() {
if (!mUsingSlotsProtocol) {
resetSlots();
@@ -160,6 +134,21 @@
[](const Slot& slot) { return slot.mInUse; });
}
+void MultiTouchMotionAccumulator::populateCurrentSlot(
+ const android::InputDeviceContext& deviceContext) {
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+ int32_t initialSlot;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGE("Could not retrieve current multi-touch slot index. status=%s",
+ statusToString(status).c_str());
+ }
+}
+
// --- MultiTouchMotionAccumulator::Slot ---
ToolType MultiTouchMotionAccumulator::Slot::getToolType() const {
@@ -176,4 +165,52 @@
return ToolType::UNKNOWN;
}
+void MultiTouchMotionAccumulator::Slot::populateAxisValue(int32_t axisCode, int32_t value) {
+ switch (axisCode) {
+ case ABS_MT_POSITION_X:
+ mAbsMtPositionX = value;
+ break;
+ case ABS_MT_POSITION_Y:
+ mAbsMtPositionY = value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ mAbsMtTouchMajor = value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ mAbsMtTouchMinor = value;
+ mHaveAbsMtTouchMinor = true;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ mAbsMtWidthMajor = value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ mAbsMtWidthMinor = value;
+ mHaveAbsMtWidthMinor = true;
+ break;
+ case ABS_MT_ORIENTATION:
+ mAbsMtOrientation = value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (value < 0) {
+ // The slot is no longer in use but it retains its previous contents,
+ // which may be reused for subsequent touches.
+ mInUse = false;
+ } else {
+ mInUse = true;
+ mAbsMtTrackingId = value;
+ }
+ break;
+ case ABS_MT_PRESSURE:
+ mAbsMtPressure = value;
+ break;
+ case ABS_MT_DISTANCE:
+ mAbsMtDistance = value;
+ break;
+ case ABS_MT_TOOL_TYPE:
+ mAbsMtToolType = value;
+ mHaveAbsMtToolType = true;
+ break;
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 0e3e2bb..a0f2147 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -68,12 +68,14 @@
int32_t mAbsMtToolType = 0;
void clear() { *this = Slot(); }
+ void populateAxisValue(int32_t axisCode, int32_t value);
};
MultiTouchMotionAccumulator();
void configure(const InputDeviceContext& deviceContext, size_t slotCount,
bool usingSlotsProtocol);
+ void reset(const InputDeviceContext& deviceContext);
void process(const RawEvent* rawEvent);
void finishSync();
@@ -85,12 +87,14 @@
}
private:
- int32_t mCurrentSlot;
+ int32_t mCurrentSlot{-1};
std::vector<Slot> mSlots;
bool mUsingSlotsProtocol;
void resetSlots();
+ void syncSlots(const InputDeviceContext& deviceContext);
void warnIfNotInUse(const RawEvent& event, const Slot& slot);
+ void populateCurrentSlot(const android::InputDeviceContext& deviceContext);
};
} // namespace android