SurfaceFlinger: Implement drop input modes
ALL: If this mode is set on a layer, set the DROP_INPUT
feature flag on the layer and its children if it has a valid input
channel. This will ensure these layers will not be able to receive any
input.
OBSCURED: If this mode is set, set the DROP_INPUT
feature flag on the layer and its children if they have a valid input
channel and they are considered occluded. This can happen if the layer
has a alpha set by it's parent OR if its buffer has been cropped by its
parent. Otherwise, the input feature flag DROP_INPUT_IF_OBSCURED flag
will be set so inputflinger can decide to drop input based on
occlusion.
Test: atest libgui_test InputDispatcherDropInputFeatureTest
Bug: 197364677
Change-Id: Ibce11ee7df5b5c8c226ebfab29574a99cd656f6d
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4050dd5..10391a2b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2288,6 +2288,74 @@
}
}
+gui::DropInputMode Layer::getDropInputMode() const {
+ gui::DropInputMode mode = mDrawingState.dropInputMode;
+ if (mode == gui::DropInputMode::ALL) {
+ return mode;
+ }
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent) {
+ gui::DropInputMode parentMode = parent->getDropInputMode();
+ if (parentMode != gui::DropInputMode::NONE) {
+ return parentMode;
+ }
+ }
+ return mode;
+}
+
+void Layer::handleDropInputMode(gui::WindowInfo& info) const {
+ if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+ return;
+ }
+
+ // Check if we need to drop input unconditionally
+ gui::DropInputMode dropInputMode = getDropInputMode();
+ if (dropInputMode == gui::DropInputMode::ALL) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ ALOGV("Dropping input for %s as requested by policy.", getDebugName());
+ return;
+ }
+
+ // Check if we need to check if the window is obscured by parent
+ if (dropInputMode != gui::DropInputMode::OBSCURED) {
+ return;
+ }
+
+ // Check if the parent has set an alpha on the layer
+ sp<Layer> parent = mDrawingParent.promote();
+ if (parent && parent->getAlpha() != 1.0_hf) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
+ static_cast<float>(getAlpha()));
+ }
+
+ // Check if the parent has cropped the buffer
+ Rect bufferSize = getCroppedBufferSize(getDrawingState());
+ if (!bufferSize.isValid()) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+ return;
+ }
+
+ // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
+ // To check if the layer has been cropped, we take the buffer bounds, apply the local
+ // layer crop and apply the same set of transforms to move to screenspace. If the bounds
+ // match then the layer has not been cropped by its parents.
+ Rect bufferInScreenSpace(getTransform().transform(bufferSize));
+ bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
+
+ if (croppedByParent) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
+ getDebugName());
+ } else {
+ // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
+ // input if the window is obscured. This check should be done in surfaceflinger but the
+ // logic currently resides in inputflinger. So pass the if_obscured check to input to only
+ // drop input events if the window is obscured.
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+ }
+}
+
WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
@@ -2315,6 +2383,7 @@
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
info.alpha = getAlpha();
fillTouchOcclusionMode(info);
+ handleDropInputMode(info);
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {