Merge "[sf] Update touchable region crop correctly" into udc-dev
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 2d6d8ad..1e931a7 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -187,6 +187,8 @@
<< " geomLayerTransform={tx=" << geomLayerTransform.tx()
<< ",ty=" << geomLayerTransform.ty() << "}"
<< "}";
+ debug << " input{ touchCropId=" << touchCropId
+ << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}";
return debug.str();
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 5491d9a..b167d3e 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -94,6 +94,7 @@
int32_t frameRateSelectionPriority;
LayerHierarchy::TraversalPath mirrorRootPath;
bool unreachable = true;
+ uint32_t touchCropId;
uid_t uid;
pid_t pid;
ChildState childState;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index babcbe7..25cbe7a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -447,23 +447,36 @@
}
}
+ // Update touchable region crops outside the main update pass. This is because a layer could be
+ // cropped by any other layer and it requires both snapshots to be updated.
+ updateTouchableRegionCrop(args);
+
const bool hasUnreachableSnapshots = sortSnapshotsByZ(args);
clearChanges(mRootSnapshot);
- // Destroy unreachable snapshots
- if (!hasUnreachableSnapshots) {
+ // Destroy unreachable snapshots for clone layers. And destroy snapshots for non-clone
+ // layers if the layer have been destroyed.
+ // TODO(b/238781169) consider making clone layer ids stable as well
+ if (!hasUnreachableSnapshots && args.layerLifecycleManager.getDestroyedLayers().empty()) {
return;
}
+ std::unordered_set<uint32_t> destroyedLayerIds;
+ for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) {
+ destroyedLayerIds.insert(destroyedLayer->id);
+ }
+
auto it = mSnapshots.begin();
while (it < mSnapshots.end()) {
auto& traversalPath = it->get()->path;
- if (!it->get()->unreachable) {
+ if (!it->get()->unreachable &&
+ destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
it++;
continue;
}
mIdToSnapshot.erase(traversalPath);
+ mNeedsTouchableRegionCrop.erase(traversalPath);
mSnapshots.back()->globalZ = it->get()->globalZ;
std::iter_swap(it, mSnapshots.end() - 1);
mSnapshots.erase(mSnapshots.end() - 1);
@@ -554,7 +567,7 @@
mResortSnapshots = false;
for (auto& snapshot : mSnapshots) {
- snapshot->unreachable = true;
+ snapshot->unreachable = snapshot->path.isClone();
}
size_t globalZ = 0;
@@ -739,6 +752,8 @@
// If root layer, use the layer stack otherwise get the parent's layer stack.
snapshot.color.a = parentSnapshot.color.a * requested.color.a;
snapshot.alpha = snapshot.color.a;
+ snapshot.inputInfo.alpha = snapshot.color.a;
+
snapshot.isSecure =
parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
@@ -986,9 +1001,11 @@
snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
snapshot.inputInfo.ownerPid = requested.ownerPid;
}
+ snapshot.touchCropId = requested.touchCropId;
snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
+ updateVisibility(snapshot, snapshot.isVisible);
if (!needsInputInfo(snapshot, requested)) {
return;
}
@@ -1033,27 +1050,13 @@
}
auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
- if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
- Rect inputBoundsInDisplaySpace;
- if (!cropLayerSnapshot) {
- FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
- inputBoundsInDisplaySpace =
- getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
- } else {
- FloatRect inputBounds =
- getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
- inputBoundsInDisplaySpace =
- getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
- displayInfo.transform);
- }
- snapshot.inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace);
- } else if (cropLayerSnapshot) {
- FloatRect inputBounds = getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+ if (cropLayerSnapshot) {
+ mNeedsTouchableRegionCrop.insert(path);
+ } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
+ FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
Rect inputBoundsInDisplaySpace =
- getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
- displayInfo.transform);
- snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect(
- displayInfo.transform.transform(inputBoundsInDisplaySpace));
+ getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
+ snapshot.inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace);
}
// Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -1066,12 +1069,7 @@
// touches from going outside the cloned area.
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
- auto clonedRootSnapshot = getSnapshot(snapshot.mirrorRootPath);
- if (clonedRootSnapshot) {
- const Rect rect =
- displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
- snapshot.inputInfo.touchableRegion = snapshot.inputInfo.touchableRegion.intersect(rect);
- }
+ mNeedsTouchableRegionCrop.insert(path);
}
}
@@ -1117,4 +1115,77 @@
}
}
+void LayerSnapshotBuilder::updateTouchableRegionCrop(const Args& args) {
+ if (mNeedsTouchableRegionCrop.empty()) {
+ return;
+ }
+
+ static constexpr ftl::Flags<RequestedLayerState::Changes> AFFECTS_INPUT =
+ RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Created |
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::Input;
+
+ if (args.forceUpdate != ForceUpdateFlags::ALL &&
+ !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) {
+ return;
+ }
+
+ for (auto& path : mNeedsTouchableRegionCrop) {
+ frontend::LayerSnapshot* snapshot = getSnapshot(path);
+ if (!snapshot) {
+ continue;
+ }
+ const std::optional<frontend::DisplayInfo> displayInfoOpt =
+ args.displays.get(snapshot->outputFilter.layerStack);
+ static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
+ auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
+
+ bool needsUpdate =
+ args.forceUpdate == ForceUpdateFlags::ALL || snapshot->changes.any(AFFECTS_INPUT);
+ auto cropLayerSnapshot = getSnapshot(snapshot->touchCropId);
+ needsUpdate =
+ needsUpdate || (cropLayerSnapshot && cropLayerSnapshot->changes.any(AFFECTS_INPUT));
+ auto clonedRootSnapshot = path.isClone() ? getSnapshot(snapshot->mirrorRootPath) : nullptr;
+ needsUpdate = needsUpdate ||
+ (clonedRootSnapshot && clonedRootSnapshot->changes.any(AFFECTS_INPUT));
+
+ if (!needsUpdate) {
+ continue;
+ }
+
+ if (snapshot->inputInfo.replaceTouchableRegionWithCrop) {
+ Rect inputBoundsInDisplaySpace;
+ if (!cropLayerSnapshot) {
+ FloatRect inputBounds = getInputBounds(*snapshot, /*fillParentBounds=*/true).first;
+ inputBoundsInDisplaySpace =
+ getInputBoundsInDisplaySpace(*snapshot, inputBounds, displayInfo.transform);
+ } else {
+ FloatRect inputBounds =
+ getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+ inputBoundsInDisplaySpace =
+ getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
+ displayInfo.transform);
+ }
+ snapshot->inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace);
+ } else if (cropLayerSnapshot) {
+ FloatRect inputBounds =
+ getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+ Rect inputBoundsInDisplaySpace =
+ getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
+ displayInfo.transform);
+ snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect(
+ displayInfo.transform.transform(inputBoundsInDisplaySpace));
+ }
+
+ // If the layer is a clone, we need to crop the input region to cloned root to prevent
+ // touches from going outside the cloned area.
+ if (clonedRootSnapshot) {
+ const Rect rect =
+ displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
+ snapshot->inputInfo.touchableRegion =
+ snapshot->inputInfo.touchableRegion.intersect(rect);
+ }
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 7b1ff27..148c98e 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -119,10 +119,14 @@
const LayerSnapshot& parentSnapshot);
void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
const Args& args);
+ void updateTouchableRegionCrop(const Args& args);
std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
LayerHierarchy::TraversalPathHash>
mIdToSnapshot;
+ // Track snapshots that needs touchable region crop from other snapshots
+ std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
+ mNeedsTouchableRegionCrop;
std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
LayerSnapshot mRootSnapshot;
bool mResortSnapshots = false;
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 5b3c7ef..79cfd6a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -278,6 +278,25 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
+ bool replaceTouchableRegionWithCrop) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->touchableRegion = region;
+ inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
+ transactions.back().states.front().touchCropId = touchCropId;
+
+ inputInfo->token = sp<BBinder>::make();
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index b8c4781..5a066a6 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -308,6 +308,31 @@
EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
}
+TEST_F(LayerSnapshotTest, canCropTouchableRegion) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111 (touchregion set to touch but cropped by layer 13)
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13 (crop set to touchCrop)
+ // └── 2
+
+ Rect touchCrop{300, 300, 400, 500};
+ setCrop(13, touchCrop);
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegionCrop(111, touch, /*touchCropId=*/13, /*replaceTouchableRegionWithCrop=*/true);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), touchCrop);
+
+ Rect modifiedTouchCrop{100, 300, 400, 700};
+ setCrop(13, modifiedTouchCrop);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), modifiedTouchCrop);
+}
+
// Display Mirroring Tests
// tree with 3 levels of children
// ROOT (DISPLAY 0)