Merge changes Ia1ed53b6,Ic561e8b3
* changes:
SF: Clean up input info calculations
SF: Enforce that layers with invalid input bounds are not focusable
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 83e9858..06a0aca 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -546,7 +546,10 @@
}
TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
+ std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
+ bgSurface->showAt(100, 100);
+
// In case we pass the very big inset without any checking.
fgSurface->mInputInfo.surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
@@ -554,8 +557,8 @@
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
// expect no crash for overflow, and inset size to be clamped to surface size
- injectTap(202, 202);
- fgSurface->expectTap(1, 1);
+ injectTap(112, 124);
+ bgSurface->expectTap(12, 24);
}
// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
@@ -987,6 +990,31 @@
EXPECT_EQ(surface->consumeEvent(100), nullptr);
}
+TEST_F(InputSurfacesTest, layer_with_empty_crop_cannot_be_focused) {
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+
+ bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
+
+ bufferSurface->requestFocus();
+ EXPECT_EQ(bufferSurface->consumeEvent(100), nullptr);
+
+ bufferSurface->showAt(50, 50, Rect::INVALID_RECT);
+
+ bufferSurface->requestFocus();
+ EXPECT_EQ(bufferSurface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) {
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
+
+ bufferSurface->showAt(50, 50, Rect{0, 0, 100, 100});
+
+ bufferSurface->requestFocus();
+ bufferSurface->assertFocusChange(true);
+}
+
/**
* If a cropped layer's touchable region is replaced with a null crop, it should receive input in
* its own crop.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9fffd12..645d4d1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2160,91 +2160,71 @@
return getCroppedBufferSize(getDrawingState());
}
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) {
- Rect layerBounds = getInputBounds();
- if (!layerBounds.isValid()) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
+ Rect tmpBounds = getInputBounds();
+ if (!tmpBounds.isValid()) {
info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE;
+ info.focusable = false;
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;
+ tmpBounds = Rect::EMPTY_RECT;
}
- const ui::Transform layerTransform = getInputTransform();
- // Transform that takes window coordinates to non-rotated display coordinates
- ui::Transform t = displayTransform * layerTransform;
- int32_t xSurfaceInset = info.surfaceInset;
- int32_t ySurfaceInset = info.surfaceInset;
- // Bring screenBounds into non-unrotated space
- Rect screenBounds = displayTransform.transform(Rect{mScreenBounds});
+ // InputDispatcher works in the display device's coordinate space. Here, we calculate the
+ // frame and transform used for the layer, which determines the bounds and the coordinate space
+ // within which the layer will receive input.
+ //
+ // The coordinate space within which each of the bounds are specified is explicitly documented
+ // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A
+ // Transform converts one coordinate space to another, which is apparent in its naming. For
+ // example, "layerToDisplay" transforms layer space to display space.
+ //
+ // Coordinate space definitions:
+ // - display: The display device's coordinate space. Correlates to pixels on the display.
+ // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
+ // - layer: The coordinate space of this layer.
+ // - input: The coordinate space in which this layer will receive input events. This could be
+ // different than layer space if a surfaceInset is used, which changes the origin
+ // of the input space.
+ const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect();
- const float xScale = t.getScaleX();
- const float yScale = t.getScaleY();
- if (xScale != 1.0f || yScale != 1.0f) {
- xSurfaceInset = std::round(xSurfaceInset * xScale);
- ySurfaceInset = std::round(ySurfaceInset * yScale);
- }
+ // Clamp surface inset to the input bounds.
+ const auto surfaceInset = static_cast<float>(info.surfaceInset);
+ const float xSurfaceInset =
+ std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f));
+ const float ySurfaceInset =
+ std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f));
- // Transform the layer bounds from layer coordinate space to display coordinate space.
- Rect transformedLayerBounds = t.transform(layerBounds);
+ // Apply the insets to the input bounds.
+ const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset,
+ inputBoundsInLayer.top + ySurfaceInset,
+ inputBoundsInLayer.right - xSurfaceInset,
+ inputBoundsInLayer.bottom - ySurfaceInset);
- // clamp inset to layer bounds
- xSurfaceInset = (xSurfaceInset >= 0)
- ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2)
- : 0;
- ySurfaceInset = (ySurfaceInset >= 0)
- ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2)
- : 0;
+ // Crop the input bounds to ensure it is within the parent's bounds.
+ const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer);
- // inset while protecting from overflow TODO(b/161235021): What is going wrong
- // in the overflow scenario?
- {
- int32_t tmp;
- if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp))
- transformedLayerBounds.left = tmp;
- if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp))
- transformedLayerBounds.right = tmp;
- if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp))
- transformedLayerBounds.top = tmp;
- if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp))
- transformedLayerBounds.bottom = tmp;
- }
+ const ui::Transform layerToScreen = getInputTransform();
+ const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
- // Compute the correct transform to send to input. This will allow it to transform the
- // input coordinates from display space into window space. Therefore, it needs to use the
- // final layer frame to create the inverse transform. Since surface insets are added later,
- // along with the overflow, the best way to ensure we get the correct transform is to use
- // the final frame calculated.
- // 1. Take the original transform set on the window and get the inverse transform. This is
- // used to get the final bounds in display space (ignorning the transform). Apply the
- // inverse transform on the layerBounds to get the untransformed frame (in layer space)
- // 2. Take the top and left of the untransformed frame to get the real position on screen.
- // Apply the layer transform on top/left so it includes any scale or rotation. These will
- // be the new translation values for the transform.
- // 3. Update the translation of the original transform to the new translation values.
- // 4. Send the inverse transform to input so the coordinates can be transformed back into
- // window space.
- ui::Transform inverseTransform = t.inverse();
- Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds);
- vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top);
- ui::Transform inputTransform(t);
- inputTransform.set(translation.x, translation.y);
- info.transform = inputTransform.inverse();
+ const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+ info.frameLeft = roundedFrameInDisplay.left;
+ info.frameTop = roundedFrameInDisplay.top;
+ info.frameRight = roundedFrameInDisplay.right;
+ info.frameBottom = roundedFrameInDisplay.bottom;
- // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped.
- // The frame should be the area the user sees on screen since it's used for occlusion
- // detection.
- transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds);
- info.frameLeft = transformedLayerBounds.left;
- info.frameTop = transformedLayerBounds.top;
- info.frameRight = transformedLayerBounds.right;
- info.frameBottom = transformedLayerBounds.bottom;
+ ui::Transform inputToLayer;
+ inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top);
+ const ui::Transform inputToDisplay = layerToDisplay * inputToLayer;
- // Position the touchable region relative to frame screen location and restrict it to frame
- // bounds.
- info.touchableRegion = inputTransform.transform(info.touchableRegion);
+ // InputDispatcher expects a display-to-input transform.
+ info.transform = inputToDisplay.inverse();
+
+ // The touchable region is specified in the input coordinate space. Change it to display space.
+ info.touchableRegion = inputToDisplay.transform(info.touchableRegion);
}
void Layer::fillTouchOcclusionMode(WindowInfo& info) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 40fb946..605a27e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1093,7 +1093,7 @@
void fillTouchOcclusionMode(gui::WindowInfo& info);
// Fills in the frame and transform info for the gui::WindowInfo.
- void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform);
+ void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is