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)