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/include/input/InputWindow.h b/include/input/InputWindow.h
index a065a4c..916af69 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -116,7 +116,7 @@
         INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
         INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
     };
-    
+
     /* These values are filled in by the WM and passed through SurfaceFlinger
      * unless specified otherwise.
      */
@@ -165,6 +165,8 @@
     int32_t displayId;
     int32_t portalToDisplayId = ADISPLAY_ID_NONE;
     InputApplicationInfo applicationInfo;
+    bool replaceTouchableRegionWithCrop;
+    wp<IBinder> touchableRegionCropHandle;
 
     void addTouchableRegion(const Rect& region);
 
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 5c5613d..5a60347 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -98,7 +98,8 @@
     output.writeInt32(portalToDisplayId);
     applicationInfo.write(output);
     output.write(touchableRegion);
-
+    output.writeBool(replaceTouchableRegionWithCrop);
+    output.writeWeakBinder(touchableRegionCropHandle);
     return OK;
 }
 
@@ -140,6 +141,8 @@
     ret.portalToDisplayId = from.readInt32();
     ret.applicationInfo = InputApplicationInfo::read(from);
     from.read(ret.touchableRegion);
+    ret.replaceTouchableRegionWithCrop = from.readBool();
+    ret.touchableRegionCropHandle = from.readWeakBinder();
 
     return ret;
 }
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 09dd72b..6db18ab 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -37,6 +37,7 @@
 }
 
 TEST(InputWindowInfo, Parcelling) {
+    sp<IBinder> touchableRegionCropHandle = new BBinder();
     InputWindowInfo i;
     i.token = new BBinder();
     i.name = "Foobar";
@@ -62,6 +63,8 @@
     i.inputFeatures = 29;
     i.displayId = 34;
     i.portalToDisplayId = 2;
+    i.replaceTouchableRegionWithCrop = true;
+    i.touchableRegionCropHandle = touchableRegionCropHandle;
 
     Parcel p;
     i.write(p);
@@ -92,6 +95,8 @@
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.displayId, i2.displayId);
     ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
+    ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
+    ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
 }
 
 } // namespace test
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;
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1afb917..af50f59 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -181,6 +181,7 @@
 
         bool inputInfoChanged;
         InputWindowInfo inputInfo;
+        wp<Layer> touchableRegionCrop;
 
         // The fields below this point are only used by BufferStateLayer
         Geometry active;