Merge "SF: Calculate WindowInfo frame correctly for all layers"
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 8d356aa..1c7b270 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -52,7 +52,8 @@
}
bool WindowInfo::overlaps(const WindowInfo* other) const {
- return frameLeft < other->frameRight && frameRight > other->frameLeft &&
+ const bool nonEmpty = (frameRight - frameLeft > 0) || (frameBottom - frameTop > 0);
+ return nonEmpty && frameLeft < other->frameRight && frameRight > other->frameLeft &&
frameTop < other->frameBottom && frameBottom > other->frameTop;
}
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 6f1263b..83e9858 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -987,6 +987,82 @@
EXPECT_EQ(surface->consumeEvent(100), nullptr);
}
+/**
+ * If a cropped layer's touchable region is replaced with a null crop, it should receive input in
+ * its own crop.
+ */
+TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) {
+ std::unique_ptr<InputSurface> parentContainer =
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ std::unique_ptr<InputSurface> containerSurface =
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+ containerSurface->doTransaction(
+ [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
+ containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+ parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
+ containerSurface->showAt(10, 10, Rect(0, 0, 5, 5));
+
+ // Receives events inside its own crop
+ injectTap(21, 21);
+ containerSurface->expectTap(1, 1); // Event is in layer space
+
+ // Does not receive events outside its crop
+ injectTap(26, 26);
+ EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
+}
+
+/**
+ * If an un-cropped layer's touchable region is replaced with a null crop, it should receive input
+ * in its parent's touchable region. The input events should be in the layer's coordinate space.
+ */
+TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) {
+ std::unique_ptr<InputSurface> parentContainer =
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ std::unique_ptr<InputSurface> containerSurface =
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+ containerSurface->doTransaction(
+ [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
+ containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
+ parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
+ containerSurface->showAt(10, 10, Rect::INVALID_RECT);
+
+ // Receives events inside parent bounds
+ injectTap(21, 21);
+ containerSurface->expectTap(1, 1); // Event is in layer space
+
+ // Does not receive events outside parent bounds
+ injectTap(31, 31);
+ EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
+}
+
+/**
+ * If a layer's touchable region is replaced with a layer crop, it should receive input in the crop
+ * layer's bounds. The input events should be in the layer's coordinate space.
+ */
+TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) {
+ std::unique_ptr<InputSurface> cropLayer =
+ InputSurface::makeContainerInputSurface(mComposerClient, 0, 0);
+ cropLayer->showAt(50, 50, Rect(0, 0, 20, 20));
+
+ std::unique_ptr<InputSurface> containerSurface =
+ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
+ containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
+ containerSurface->mInputInfo.touchableRegionCropHandle =
+ cropLayer->mSurfaceControl->getHandle();
+ containerSurface->showAt(10, 10, Rect::INVALID_RECT);
+
+ // Receives events inside crop layer bounds
+ injectTap(51, 51);
+ containerSurface->expectTap(41, 41); // Event is in layer space
+
+ // Does not receive events outside crop layer bounds
+ injectTap(21, 21);
+ injectTap(71, 71);
+ EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
+}
+
class MultiDisplayTests : public InputSurfacesTest {
public:
MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7af1237..9fffd12 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2161,25 +2161,15 @@
}
void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) {
- // Transform layer size to screen space and inset it by surface insets.
- // If this is a portal window, set the touchableRegion to the layerBounds.
- Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
- ? getInputBounds()
- : info.touchableRegion.getBounds();
+ Rect layerBounds = getInputBounds();
if (!layerBounds.isValid()) {
- layerBounds = getInputBounds();
- }
-
- if (!layerBounds.isValid()) {
- // If the layer bounds is empty, set the frame to empty and clear the transform
- info.frameLeft = 0;
- info.frameTop = 0;
- info.frameRight = 0;
- info.frameBottom = 0;
- info.transform.reset();
- info.touchableRegion = Region();
info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE;
- return;
+ info.touchableRegion.clear();
+ // A layer could have invalid input bounds and still expect to receive touch input if it has
+ // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated
+ // correctly to determine the coordinate space for input events. Use an empty rect so that
+ // the layer will receive input in its own layer space.
+ layerBounds = Rect::EMPTY_RECT;
}
const ui::Transform layerTransform = getInputTransform();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 991256b..40fb946 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -959,6 +959,19 @@
bool usingRelativeZ(LayerVector::StateSet) const;
virtual ui::Transform getInputTransform() const;
+ /**
+ * Get the bounds in layer space within which this layer can receive input.
+ *
+ * These bounds are used to:
+ * - Determine the input frame for the layer to be used for occlusion detection; and
+ * - Determine the coordinate space within which the layer will receive input. The top-left of
+ * this rect will be the origin of the coordinate space that the input events sent to the
+ * layer will be in (prior to accounting for surface insets).
+ *
+ * The layer can still receive touch input if these bounds are invalid if
+ * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input
+ * in this layer's space, regardless of the specified crop layer.
+ */
virtual Rect getInputBounds() const;
// constant