Use empty frame for input when layer bounds is invalid
Fixed a few issues:
1. Return INVALID_RECT for size if there's no buffer for BLAST. This was
already done in BufferQueueLayer. This ensures we don't assume the layer
has a valid size when there's no buffer
2. Don't transform invalid layer bounds in fillInputInfo since it could
result in sending a valid frame when the layer actually has no bounds.
Instead just set input frame to empty, reset the transform, and return
early.
Test: Window with no width or height doesn't get untrusted touch
Test: InputSurfacesTest
Fixes: 173297887
Change-Id: I191d411b0f83ce57b6fdc20a1def8070110d418b
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c39b0b5..c75c46c 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -95,6 +95,15 @@
return std::make_unique<InputSurface>(surfaceControl, width, height);
}
+ static std::unique_ptr<InputSurface> makeBlastInputSurface(const sp<SurfaceComposerClient> &scc,
+ int width, int height) {
+ sp<SurfaceControl> surfaceControl =
+ scc->createSurface(String8("Test Buffer Surface"), width, height,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+ return std::make_unique<InputSurface>(surfaceControl, width, height);
+ }
+
static std::unique_ptr<InputSurface> makeContainerInputSurface(
const sp<SurfaceComposerClient> &scc, int width, int height) {
sp<SurfaceControl> surfaceControl =
@@ -180,13 +189,13 @@
t.apply(true);
}
- void showAt(int x, int y) {
+ void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) {
SurfaceComposerClient::Transaction t;
t.show(mSurfaceControl);
t.setInputWindowInfo(mSurfaceControl, mInputInfo);
t.setLayer(mSurfaceControl, LAYER_BASE);
t.setPosition(mSurfaceControl, x, y);
- t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
+ t.setCrop_legacy(mSurfaceControl, crop);
t.setAlpha(mSurfaceControl, 1);
t.apply(true);
}
@@ -684,4 +693,34 @@
surface->expectTap(1, 10);
}
+TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
+ bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ bufferSurface->mInputInfo.ownerUid = 22222;
+
+ surface->showAt(10, 10);
+ bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+
+ std::unique_ptr<InputSurface> bufferSurface =
+ InputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
+ bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ bufferSurface->mInputInfo.ownerUid = 22222;
+
+ surface->showAt(10, 10);
+ bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
+
} // namespace android::test
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5cf0460..b6c59cd 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -479,6 +479,10 @@
return Rect(getActiveWidth(s), getActiveHeight(s));
}
+ if (mBufferInfo.mBuffer == nullptr) {
+ return Rect::INVALID_RECT;
+ }
+
// if the display frame is not defined, use the parent bounds as the buffer size.
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b273712..9407c92 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2432,27 +2432,7 @@
return mRemovedFromCurrentState;
}
-InputWindowInfo Layer::fillInputInfo() {
- if (!hasInputInfo()) {
- mDrawingState.inputInfo.name = getName();
- mDrawingState.inputInfo.ownerUid = mOwnerUid;
- mDrawingState.inputInfo.ownerPid = mOwnerPid;
- mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
- mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
- mDrawingState.inputInfo.displayId = getLayerStack();
- }
-
- InputWindowInfo info = mDrawingState.inputInfo;
- info.id = sequence;
-
- if (info.displayId == ADISPLAY_ID_NONE) {
- info.displayId = getLayerStack();
- }
-
- ui::Transform t = getTransform();
- int32_t xSurfaceInset = info.surfaceInset;
- int32_t ySurfaceInset = info.surfaceInset;
-
+void Layer::fillInputFrameInfo(InputWindowInfo& info) {
// 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
@@ -2462,6 +2442,20 @@
layerBounds = getCroppedBufferSize(getDrawingState());
}
+ 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();
+ return;
+ }
+
+ ui::Transform t = getTransform();
+ int32_t xSurfaceInset = info.surfaceInset;
+ int32_t ySurfaceInset = info.surfaceInset;
+
const float xScale = t.getScaleX();
const float yScale = t.getScaleY();
if (xScale != 1.0f || yScale != 1.0f) {
@@ -2528,6 +2522,27 @@
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = inputTransform.transform(info.touchableRegion);
+}
+
+InputWindowInfo Layer::fillInputInfo() {
+ if (!hasInputInfo()) {
+ mDrawingState.inputInfo.name = getName();
+ mDrawingState.inputInfo.ownerUid = mOwnerUid;
+ mDrawingState.inputInfo.ownerPid = mOwnerPid;
+ mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
+ mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+ mDrawingState.inputInfo.displayId = getLayerStack();
+ }
+
+ InputWindowInfo info = mDrawingState.inputInfo;
+ info.id = sequence;
+
+ if (info.displayId == ADISPLAY_ID_NONE) {
+ info.displayId = getLayerStack();
+ }
+
+ fillInputFrameInfo(info);
+
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index cc47d12..ac0eb90 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1114,6 +1114,9 @@
// null.
sp<Layer> getRootLayer();
+ // Fills in the frame and transform info for the InputWindowInfo
+ void fillInputFrameInfo(InputWindowInfo& info);
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.