Skip to content

Commit 72c6990

Browse files
committed
Use the cloned window's layer stack space for raw coordinates
Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones. This ensures the raw coordinates are consistent between the clone and cloned windows while mirroring. Flag: com.android.input.flags.use_cloned_screen_coordinates_as_raw Test: Presubmit Bug: 377846505 Change-Id: I7370ac64585315d9c754eaa1eb0077c8555fc6a9
1 parent 2e5d4dd commit 72c6990

9 files changed

Lines changed: 162 additions & 46 deletions

File tree

libs/gui/WindowInfo.cpp

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,32 @@ std::ostream& operator<<(std::ostream& out, const Region& region) {
5959
return out;
6060
}
6161

62+
status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) {
63+
return parcel->writeFloat(transform.dsdx()) ?:
64+
parcel->writeFloat(transform.dtdx()) ?:
65+
parcel->writeFloat(transform.tx()) ?:
66+
parcel->writeFloat(transform.dtdy()) ?:
67+
parcel->writeFloat(transform.dsdy()) ?:
68+
parcel->writeFloat(transform.ty());
69+
}
70+
71+
status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) {
72+
float dsdx, dtdx, tx, dtdy, dsdy, ty;
73+
74+
const status_t status = parcel->readFloat(&dsdx) ?:
75+
parcel->readFloat(&dtdx) ?:
76+
parcel->readFloat(&tx) ?:
77+
parcel->readFloat(&dtdy) ?:
78+
parcel->readFloat(&dsdy) ?:
79+
parcel->readFloat(&ty);
80+
if (status != OK) {
81+
return status;
82+
}
83+
84+
transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
85+
return OK;
86+
}
87+
6288
} // namespace
6389

6490
void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
@@ -135,12 +161,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
135161
parcel->writeInt32(surfaceInset) ?:
136162
parcel->writeFloat(globalScaleFactor) ?:
137163
parcel->writeFloat(alpha) ?:
138-
parcel->writeFloat(transform.dsdx()) ?:
139-
parcel->writeFloat(transform.dtdx()) ?:
140-
parcel->writeFloat(transform.tx()) ?:
141-
parcel->writeFloat(transform.dtdy()) ?:
142-
parcel->writeFloat(transform.dsdy()) ?:
143-
parcel->writeFloat(transform.ty()) ?:
164+
writeTransform(parcel, transform) ?:
144165
parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
145166
parcel->writeInt32(ownerPid.val()) ?:
146167
parcel->writeInt32(ownerUid.val()) ?:
@@ -153,8 +174,12 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
153174
parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
154175
parcel->writeStrongBinder(windowToken) ?:
155176
parcel->writeStrongBinder(focusTransferTarget) ?:
156-
parcel->writeBool(canOccludePresentation);
177+
parcel->writeBool(canOccludePresentation) ?:
178+
parcel->writeBool(cloneLayerStackTransform.has_value());
157179
// clang-format on
180+
if (cloneLayerStackTransform) {
181+
status = status ?: writeTransform(parcel, *cloneLayerStackTransform);
182+
}
158183
return status;
159184
}
160185

@@ -174,10 +199,10 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
174199
return status;
175200
}
176201

177-
float dsdx, dtdx, tx, dtdy, dsdy, ty;
178202
int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt,
179203
displayIdInt;
180204
sp<IBinder> touchableRegionCropHandleSp;
205+
bool hasCloneLayerStackTransform = false;
181206

182207
// clang-format off
183208
status = parcel->readInt32(&lpFlags) ?:
@@ -188,12 +213,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
188213
parcel->readInt32(&surfaceInset) ?:
189214
parcel->readFloat(&globalScaleFactor) ?:
190215
parcel->readFloat(&alpha) ?:
191-
parcel->readFloat(&dsdx) ?:
192-
parcel->readFloat(&dtdx) ?:
193-
parcel->readFloat(&tx) ?:
194-
parcel->readFloat(&dtdy) ?:
195-
parcel->readFloat(&dsdy) ?:
196-
parcel->readFloat(&ty) ?:
216+
readTransform(parcel, /*byRef*/ transform) ?:
197217
parcel->readInt32(&touchOcclusionModeInt) ?:
198218
parcel->readInt32(&ownerPidInt) ?:
199219
parcel->readInt32(&ownerUidInt) ?:
@@ -206,8 +226,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
206226
parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
207227
parcel->readNullableStrongBinder(&windowToken) ?:
208228
parcel->readNullableStrongBinder(&focusTransferTarget) ?:
209-
parcel->readBool(&canOccludePresentation);
210-
229+
parcel->readBool(&canOccludePresentation)?:
230+
parcel->readBool(&hasCloneLayerStackTransform);
211231
// clang-format on
212232

213233
if (status != OK) {
@@ -216,14 +236,22 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
216236

217237
layoutParamsFlags = ftl::Flags<Flag>(lpFlags);
218238
layoutParamsType = static_cast<Type>(lpType);
219-
transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
220239
touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
221240
inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
222241
ownerPid = Pid{ownerPidInt};
223242
ownerUid = Uid{static_cast<uid_t>(ownerUidInt)};
224243
touchableRegionCropHandle = touchableRegionCropHandleSp;
225244
displayId = ui::LogicalDisplayId{displayIdInt};
226245

246+
cloneLayerStackTransform =
247+
hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt;
248+
if (cloneLayerStackTransform) {
249+
status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform);
250+
if (status != OK) {
251+
return status;
252+
}
253+
}
254+
227255
return OK;
228256
}
229257

libs/gui/include/gui/WindowInfo.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,14 @@ struct WindowInfo : public Parcelable {
220220
// An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
221221
float alpha;
222222

223-
// Transform applied to individual windows.
223+
// Transform applied to individual windows for input.
224+
// Maps display coordinates to the window's input coordinate space.
224225
ui::Transform transform;
225226

227+
// Transform applied to get to the layer stack space of the cloned window for input.
228+
// Maps display coordinates of the clone window to the layer stack space of the cloned window.
229+
std::optional<ui::Transform> cloneLayerStackTransform;
230+
226231
/*
227232
* This is filled in by the WM relative to the frame and then translated
228233
* to absolute coordinates by SurfaceFlinger once the frame is computed.

libs/gui/tests/WindowInfo_test.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,18 @@ TEST(WindowInfo, ParcellingWithoutToken) {
4040
ASSERT_EQ(OK, i.writeToParcel(&p));
4141
p.setDataPosition(0);
4242
i2.readFromParcel(&p);
43-
ASSERT_TRUE(i2.token == nullptr);
43+
ASSERT_EQ(i2.token, nullptr);
44+
}
45+
46+
TEST(WindowInfo, ParcellingWithoutCloneTransform) {
47+
WindowInfo i, i2;
48+
i.cloneLayerStackTransform.reset();
49+
50+
Parcel p;
51+
ASSERT_EQ(OK, i.writeToParcel(&p));
52+
p.setDataPosition(0);
53+
i2.readFromParcel(&p);
54+
ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt);
4455
}
4556

4657
TEST(WindowInfo, Parcelling) {
@@ -71,6 +82,8 @@ TEST(WindowInfo, Parcelling) {
7182
i.applicationInfo.token = new BBinder();
7283
i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
7384
i.focusTransferTarget = new BBinder();
85+
i.cloneLayerStackTransform = ui::Transform();
86+
i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1});
7487

7588
Parcel p;
7689
i.writeToParcel(&p);
@@ -100,6 +113,7 @@ TEST(WindowInfo, Parcelling) {
100113
ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
101114
ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
102115
ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget);
116+
ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform);
103117
}
104118

105119
TEST(InputApplicationInfo, Parcelling) {

libs/input/input_flags.aconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,13 @@ flag {
224224
description: "Allow cursor to transition across multiple connected displays"
225225
bug: "362719483"
226226
}
227+
228+
flag {
229+
name: "use_cloned_screen_coordinates_as_raw"
230+
namespace: "input"
231+
description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones"
232+
bug: "377846505"
233+
metadata {
234+
purpose: PURPOSE_BUGFIX
235+
}
236+
}

services/inputflinger/dispatcher/InputDispatcher.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3013,11 +3013,10 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa
30133013
windowHandle->getName().c_str());
30143014
return;
30153015
}
3016-
inputTargets.push_back(createInputTarget(connection, windowHandle, dispatchMode,
3017-
targetFlags,
3018-
mWindowInfos.getDisplayTransform(
3019-
windowHandle->getInfo()->displayId),
3020-
firstDownTimeInTarget));
3016+
inputTargets.push_back(
3017+
createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
3018+
mWindowInfos.getRawTransform(*windowHandle->getInfo()),
3019+
firstDownTimeInTarget));
30213020
it = inputTargets.end() - 1;
30223021
}
30233022

@@ -3068,11 +3067,10 @@ void InputDispatcher::addPointerWindowTargetLocked(
30683067
windowHandle->getName().c_str());
30693068
return;
30703069
}
3071-
inputTargets.push_back(createInputTarget(connection, windowHandle, dispatchMode,
3072-
targetFlags,
3073-
mWindowInfos.getDisplayTransform(
3074-
windowHandle->getInfo()->displayId),
3075-
firstDownTimeInTarget));
3070+
inputTargets.push_back(
3071+
createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
3072+
mWindowInfos.getRawTransform(*windowHandle->getInfo()),
3073+
firstDownTimeInTarget));
30763074
it = inputTargets.end() - 1;
30773075
}
30783076

@@ -3104,7 +3102,8 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>&
31043102
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
31053103
InputTarget target{monitor.connection};
31063104
// target.firstDownTimeInTarget is not set for global monitors. It is only required in split
3107-
// touch and global monitoring works as intended even without setting firstDownTimeInTarget
3105+
// touch and global monitoring works as intended even without setting firstDownTimeInTarget.
3106+
// Since global monitors don't have windows, use the display transform as the raw transform.
31083107
target.rawTransform = mWindowInfos.getDisplayTransform(displayId);
31093108
target.setDefaultPointerTransform(target.rawTransform);
31103109
inputTargets.push_back(target);
@@ -4291,6 +4290,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
42914290
motionEntry.downTime, targets);
42924291
} else {
42934292
targets.emplace_back(fallbackTarget);
4293+
// Since we don't have a window, use the display transform as the raw transform.
42944294
const ui::Transform displayTransform =
42954295
mWindowInfos.getDisplayTransform(motionEntry.displayId);
42964296
targets.back().rawTransform = displayTransform;
@@ -4376,6 +4376,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
43764376
targets);
43774377
} else {
43784378
targets.emplace_back(connection, targetFlags);
4379+
// Since we don't have a window, use the display transform as the raw transform.
43794380
const ui::Transform displayTransform =
43804381
mWindowInfos.getDisplayTransform(motionEntry.displayId);
43814382
targets.back().rawTransform = displayTransform;
@@ -5289,6 +5290,16 @@ ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform(
52895290
: kIdentityTransform;
52905291
}
52915292

5293+
ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
5294+
const android::gui::WindowInfo& windowInfo) const {
5295+
// If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
5296+
// APIs. If not, fall back to using the DisplayInfo transform of the window's display.
5297+
return (input_flags::use_cloned_screen_coordinates_as_raw() &&
5298+
windowInfo.cloneLayerStackTransform)
5299+
? *windowInfo.cloneLayerStackTransform
5300+
: getDisplayTransform(windowInfo.displayId);
5301+
}
5302+
52925303
std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
52935304
std::string dump;
52945305
if (!mWindowHandlesByDisplay.empty()) {
@@ -6567,9 +6578,8 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
65676578
createInputTarget(connection, windowHandle,
65686579
InputTarget::DispatchMode::AS_IS,
65696580
dispatchEntry->targetFlags,
6570-
mWindowInfos.getDisplayTransform(
6571-
windowHandle->getInfo()
6572-
->displayId),
6581+
mWindowInfos.getRawTransform(
6582+
*windowHandle->getInfo()),
65736583
downTime));
65746584
}
65756585
}

services/inputflinger/dispatcher/InputDispatcher.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,9 @@ class InputDispatcher : public android::InputDispatcherInterface {
384384
// Get the transform for display, returns Identity-transform if display is missing.
385385
ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
386386

387+
// Get the raw transform to use for motion events going to the given window.
388+
ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
389+
387390
// Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
388391
// display-id is not provided lookup is done for all displays.
389392
sp<android::gui::WindowInfoHandle> findWindowHandle(

services/inputflinger/tests/InputDispatcher_test.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6501,19 +6501,47 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate
65016501
ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}}));
65026502

65036503
firstWindow->assertNoEvents();
6504-
std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
6505-
ASSERT_NE(nullptr, event);
6506-
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
6504+
secondWindow->consumeMotionEvent(
6505+
AllOf(WithMotionAction(ACTION_DOWN),
6506+
// Ensure that the events from the "getRaw" API are in logical display
6507+
// coordinates, which has an x-scale of 2 and y-scale of 4.
6508+
WithRawCoords(300, 880),
6509+
// Ensure that the x and y values are in the window's coordinate space.
6510+
// The left-top of the second window is at (100, 200) in display space, which is
6511+
// (200, 800) in the logical display space. This will be the origin of the window
6512+
// space.
6513+
WithCoords(100, 80)));
6514+
}
65076515

6508-
// Ensure that the events from the "getRaw" API are in logical display coordinates.
6509-
EXPECT_EQ(300, event->getRawX(0));
6510-
EXPECT_EQ(880, event->getRawY(0));
6516+
TEST_F(InputDispatcherDisplayProjectionTest, UseCloneLayerStackTransformForRawCoordinates) {
6517+
SCOPED_FLAG_OVERRIDE(use_cloned_screen_coordinates_as_raw, true);
65116518

6512-
// Ensure that the x and y values are in the window's coordinate space.
6513-
// The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
6514-
// the logical display space. This will be the origin of the window space.
6515-
EXPECT_EQ(100, event->getX(0));
6516-
EXPECT_EQ(80, event->getY(0));
6519+
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
6520+
6521+
const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
6522+
ui::Transform secondDisplayTransform;
6523+
secondDisplayTransform.set(matrix);
6524+
addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
6525+
6526+
// When a clone layer stack transform is provided for a window, we should use that as the
6527+
// "display transform" for input going to that window.
6528+
sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
6529+
secondWindowClone->editInfo()->cloneLayerStackTransform = ui::Transform();
6530+
secondWindowClone->editInfo()->cloneLayerStackTransform->set(0.5, 0, 0, 0.25);
6531+
addWindow(secondWindowClone);
6532+
6533+
// Touch down on the clone window, and ensure its raw coordinates use
6534+
// the clone layer stack transform.
6535+
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
6536+
SECOND_DISPLAY_ID, {PointF{150, 220}}));
6537+
secondWindowClone->consumeMotionEvent(
6538+
AllOf(WithMotionAction(ACTION_DOWN),
6539+
// Ensure the x and y coordinates are in the window's coordinate space.
6540+
// See previous test case for calculation.
6541+
WithCoords(100, 80),
6542+
// Ensure the "getRaw" API uses the clone layer stack transform when it is
6543+
// provided for the window. It has an x-scale of 0.5 and y-scale of 0.25.
6544+
WithRawCoords(75, 55)));
65176545
}
65186546

65196547
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {

services/surfaceflinger/FrontEnd/LayerHierarchy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class LayerHierarchy {
102102
// Returns true if the node is a clone.
103103
bool isClone() const { return !mirrorRootIds.empty(); }
104104

105+
TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; }
106+
105107
bool operator==(const TraversalPath& other) const {
106108
return id == other.id && mirrorRootIds == other.mirrorRootIds;
107109
}

services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
// #define LOG_NDEBUG 0
1818
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
19+
#include "FrontEnd/LayerSnapshot.h"
20+
#include "ui/Transform.h"
1921
#undef LOG_TAG
2022
#define LOG_TAG "SurfaceFlinger"
2123

@@ -1205,13 +1207,27 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot,
12051207
snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
12061208
snapshot.croppedBufferSize.getWidth()};
12071209

1208-
// If the layer is a clone, we need to crop the input region to cloned root to prevent
1209-
// touches from going outside the cloned area.
1210+
snapshot.inputInfo.cloneLayerStackTransform.reset();
1211+
12101212
if (path.isClone()) {
12111213
snapshot.inputInfo.inputConfig |= InputConfig::CLONE;
12121214
// Cloned layers shouldn't handle watch outside since their z order is not determined by
12131215
// WM or the client.
12141216
snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH);
1217+
1218+
// Compute the transform that maps the clone's display to the layer stack space of the
1219+
// cloned window.
1220+
const LayerSnapshot* clonedSnapshot = getSnapshot(path.getClonedFrom());
1221+
if (clonedSnapshot != nullptr) {
1222+
const auto& [clonedInputBounds, s] =
1223+
getInputBounds(*clonedSnapshot, /*fillParentBounds=*/false);
1224+
ui::Transform inputToLayer;
1225+
inputToLayer.set(clonedInputBounds.left, clonedInputBounds.top);
1226+
const ui::Transform& layerToLayerStack = getInputTransform(*clonedSnapshot);
1227+
const auto& displayToInput = snapshot.inputInfo.transform;
1228+
snapshot.inputInfo.cloneLayerStackTransform =
1229+
layerToLayerStack * inputToLayer * displayToInput;
1230+
}
12151231
}
12161232
}
12171233

0 commit comments

Comments
 (0)