Input: Override touchable region bounds with surface bounds 2/2

Take advantage of the surface flinger layer hierarchy to set touchable
region.

- Let a client set a surface touchable region to its own bounds.
- Let a client set a surface touchable region to another surface bounds.
- Let a client bound its touchable region to a surface.

Test: go/wm-smoke
Test: existing tests

Change-Id: I447c93353d067a296007ba8f8341d2420b941d71
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5c3fb05..af27ca3 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2009,8 +2009,24 @@
     mDrawingParent = mCurrentParent;
 }
 
+static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) {
+    if (weakBinderHandle == nullptr) {
+        return nullptr;
+    }
+    sp<IBinder> binderHandle = weakBinderHandle.promote();
+    if (binderHandle == nullptr) {
+        return nullptr;
+    }
+    sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get());
+    if (handle == nullptr) {
+        return nullptr;
+    }
+    return handle->owner;
+}
+
 void Layer::setInputInfo(const InputWindowInfo& info) {
     mCurrentState.inputInfo = info;
+    mCurrentState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
     mCurrentState.modified = true;
     mCurrentState.inputInfoChanged = true;
     setTransactionFlags(eTransactionNeeded);
@@ -2199,6 +2215,18 @@
     // bounds.
     info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
     info.visible = canReceiveInput();
+
+    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+    if (info.replaceTouchableRegionWithCrop) {
+        if (cropLayer == nullptr) {
+            info.touchableRegion = Region(Rect{mScreenBounds});
+        } else {
+            info.touchableRegion = Region(Rect{cropLayer->mScreenBounds});
+        }
+    } else if (cropLayer != nullptr) {
+        info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
+    }
+
     return info;
 }