Reland "Input injection: Assume transformed values are i..."

Revert submission 16306432-revert-16295572-ORWQJPYRMQ

Reason for revert: The broken test was fixed in ag/16365181, so
this should be safe to re-land now.

Reverted Changes:
Iab395d41d:Revert "Remove x/y offset from MotionEntry"
I7beca684d:Revert "Input injection: Assume transformed values...

Change-Id: I68b5619bf52d999a653bb246e1acb02f5d07991e
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7f68d1b..3fd1c8a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -506,12 +506,6 @@
     return true;
 }
 
-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;
-}
-
 // Returns true if the event type passed as argument represents a user activity.
 bool isUserActivityEvent(const EventEntry& eventEntry) {
     switch (eventEntry.type) {
@@ -4214,10 +4208,8 @@
                                                   motionEvent.getRawXCursorPosition(),
                                                   motionEvent.getRawYCursorPosition(),
                                                   motionEvent.getDownTime(), uint32_t(pointerCount),
-                                                  pointerProperties, samplePointerCoords,
-                                                  motionEvent.getXOffset(),
-                                                  motionEvent.getYOffset());
-            transformMotionEntryForInjectionLocked(*injectedEntry);
+                                                  pointerProperties, samplePointerCoords, 0, 0);
+            transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
             injectedEntries.push(std::move(injectedEntry));
             for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
@@ -4236,9 +4228,9 @@
                                                       motionEvent.getRawYCursorPosition(),
                                                       motionEvent.getDownTime(),
                                                       uint32_t(pointerCount), pointerProperties,
-                                                      samplePointerCoords, motionEvent.getXOffset(),
-                                                      motionEvent.getYOffset());
-                transformMotionEntryForInjectionLocked(*nextInjectedEntry);
+                                                      samplePointerCoords, 0, 0);
+                transformMotionEntryForInjectionLocked(*nextInjectedEntry,
+                                                       motionEvent.getTransform());
                 injectedEntries.push(std::move(nextInjectedEntry));
             }
             break;
@@ -4402,35 +4394,28 @@
     }
 }
 
-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;
-    }
-
+void InputDispatcher::transformMotionEntryForInjectionLocked(
+        MotionEntry& entry, const ui::Transform& injectedTransform) const {
     // 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();
+    const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
 
     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);
+        // Make a copy of the injected coords. We cannot change them in place because some of them
+        // are interdependent (for example, X coordinate might depend on the Y coordinate).
+        PointerCoords injectedCoords = entry.pointerCoords[i];
 
-        // 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);
+        BitSet64 bits(injectedCoords.bits);
+        while (!bits.isEmpty()) {
+            const auto axis = static_cast<int32_t>(bits.clearFirstMarkedBit());
+            const float value =
+                    MotionEvent::calculateTransformedAxisValue(axis, entry.source,
+                                                               transformToDisplay, injectedCoords);
+            pc.setAxisValue(axis, value);
+        }
     }
 }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8a551cf..6f05670 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -280,7 +280,9 @@
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
     void setInjectionResult(EventEntry& entry,
                             android::os::InputEventInjectionResult injectionResult);
-    void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);
+    void transformMotionEntryForInjectionLocked(MotionEntry&,
+                                                const ui::Transform& injectedTransform) const
+            REQUIRES(mLock);
 
     std::condition_variable mInjectionSyncFinished;
     void incrementPendingForegroundDispatches(EventEntry& entry);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 515a01e..eaea4e2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2079,6 +2079,33 @@
     secondWindow->assertNoEvents();
 }
 
+// Ensure that when a MotionEvent that has a custom transform is injected, the post-transformed
+// event should be treated as being in the logical display space.
+TEST_F(InputDispatcherDisplayProjectionTest, InjectionWithTransformInLogicalDisplaySpace) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+    ui::Transform injectedEventTransform;
+    injectedEventTransform.set(matrix);
+    const vec2 expectedPoint{75, 55}; // The injected point in the logical display space.
+    const vec2 untransformedPoint = injectedEventTransform.inverse().transform(expectedPoint);
+
+    MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                .displayId(ADISPLAY_ID_DEFAULT)
+                                .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                                .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                                 .x(untransformedPoint.x)
+                                                 .y(untransformedPoint.y))
+                                .build();
+    event.transform(matrix);
+
+    injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
+                      InputEventInjectionSync::WAIT_FOR_RESULT);
+
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+}
+
 TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinateSpace) {
     auto [firstWindow, secondWindow] = setupScaledDisplayScenario();