InputDispatcherPolicy: Transform incoming/outgoing events
The InputDispatcherPolicy expects events to be in the logical display
space. When per-window-input-rotation is enabled, the dispatcher works
in the display space, so we need to transform events accordingly.
1. MotionEvents that are injected from the policy are in logical display
coordinates. We need to transform them to display space before
proceeding with injection.
2. When sending events to the policy to be filtered by the InputFilter,
we need to include the display transform so that the events are in the
logical display space.
This also removes the flag gurad for the per-window-input-rotation
feature in Input.cpp, which is required for the tests to pass when the
flag is disabled. This guard does not do anything anymore because the
RawTransforms are blocked by the flag in SF.
Bug: 179274888
Test: manual: adb shell input swipe 200 200 500 500 1000
Test: atest InputShellCommandTest // CTS test for input injeciton
Test: atest inputflinger_tests
Change-Id: I122e511039ca629ab8982ed27d3d35f9e7b37d70
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5f48c1d..f094fee 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -524,6 +524,16 @@
return true;
}
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
+vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
+ const vec2 transformedXy = transform.transform(x, y);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -3962,15 +3972,19 @@
mLock.lock();
if (shouldSendMotionToInputFilterLocked(args)) {
+ ui::Transform displayTransform;
+ if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
+ displayTransform = it->second.transform;
+ }
+
mLock.unlock();
MotionEvent event;
- ui::Transform identityTransform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
args->metaState, args->buttonState, args->classification,
- identityTransform, args->xPrecision, args->yPrecision,
- args->xCursorPosition, args->yCursorPosition, identityTransform,
+ displayTransform, args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition, displayTransform,
args->downTime, args->eventTime, args->pointerCount,
args->pointerProperties, args->pointerCoords);
@@ -4220,6 +4234,7 @@
pointerProperties, samplePointerCoords,
motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*injectedEntry);
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
@@ -4241,6 +4256,7 @@
uint32_t(pointerCount), pointerProperties,
samplePointerCoords, motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*nextInjectedEntry);
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
@@ -4404,6 +4420,38 @@
}
}
+void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const {
+ const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE);
+ if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ return;
+ }
+
+ // Input injection works in the logical display coordinate space, but the input pipeline works
+ // display space, so we need to transform the injected events accordingly.
+ const auto it = mDisplayInfos.find(entry.displayId);
+ if (it == mDisplayInfos.end()) return;
+ const auto& transformToDisplay = it->second.transform.inverse();
+
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ PointerCoords& pc = entry.pointerCoords[i];
+ const auto xy = isRelativeMouseEvent
+ ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY())
+ : transformToDisplay.transform(pc.getXYValue());
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ // Axes with relative values never represent points on a screen, so they should never have
+ // translation applied. If a device does not report relative values, these values are always
+ // 0, and will remain unaffected by the following operation.
+ const auto rel =
+ transformWithoutTranslation(transformToDisplay,
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y);
+ }
+}
+
void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
InjectionState* injectionState = entry.injectionState;
if (injectionState) {