Merge "Input: Override touchable region bounds with surface bounds 1/2"
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index ec79eea..16a6412 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -18,9 +18,12 @@
 
 import static android.view.Display.INVALID_DISPLAY;
 
+import android.annotation.Nullable;
 import android.graphics.Region;
 import android.os.IBinder;
 
+import java.lang.ref.WeakReference;
+
 /**
  * Functions as a handle for a window that can receive input.
  * Enables the native input dispatcher to refer indirectly to the window manager's window state.
@@ -38,7 +41,7 @@
     // The client window.
     public final IWindow clientWindow;
 
-    // The token assosciated with the window.
+    // The token associated with the window.
     public IBinder token;
 
     // The window name.
@@ -98,6 +101,23 @@
     // transports the touch of this window to the display indicated by portalToDisplayId.
     public int portalToDisplayId = INVALID_DISPLAY;
 
+    /**
+     * Crops the touchable region to the bounds of the surface provided.
+     *
+     * This can be used in cases where the window is not
+     * {@link android.view.WindowManager#FLAG_NOT_TOUCH_MODAL} but should be constrained to the
+     * bounds of a parent window. That is the window should receive touch events outside its
+     * window but be limited to its stack bounds, such as in the case of split screen.
+     */
+    public WeakReference<IBinder> touchableRegionCropHandle = new WeakReference<>(null);
+
+    /**
+     * Replace {@link touchableRegion} with the bounds of {@link touchableRegionCropHandle}. If
+     * the handle is {@code null}, the bounds of the surface associated with this window is used
+     * as the touchable region.
+     */
+    public boolean replaceTouchableRegionWithCrop;
+
     private native void nativeDispose();
 
     public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
@@ -127,4 +147,25 @@
             super.finalize();
         }
     }
+
+    /**
+     * Set the window touchable region to the bounds of {@link touchableRegionBounds} ignoring any
+     * touchable region provided.
+     *
+     * @param bounds surface to set the touchable region to. Set to {@code null} to set the bounds
+     * to the current surface.
+     */
+    public void replaceTouchableRegionWithCrop(@Nullable SurfaceControl bounds) {
+        setTouchableRegionCrop(bounds);
+        replaceTouchableRegionWithCrop = true;
+    }
+
+    /**
+     * Crop the window touchable region to the bounds of the surface provided.
+     */
+    public void setTouchableRegionCrop(@Nullable SurfaceControl bounds) {
+        if (bounds != null) {
+            touchableRegionCropHandle = new WeakReference<>(bounds.getHandle());
+        }
+    }
 }
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index eb71052..a1d1d4f 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -31,6 +31,11 @@
 
 namespace android {
 
+struct WeakRefHandleField {
+    jfieldID handle;
+    jmethodID get;
+};
+
 static struct {
     jfieldID ptr;
     jfieldID inputApplicationHandle;
@@ -57,6 +62,8 @@
     jfieldID inputFeatures;
     jfieldID displayId;
     jfieldID portalToDisplayId;
+    jfieldID replaceTouchableRegionWithCrop;
+    WeakRefHandleField touchableRegionCropHandle;
 } gInputWindowHandleClassInfo;
 
 static Mutex gHandleMutex;
@@ -90,6 +97,7 @@
     jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token);
     if (tokenObj) {
         mInfo.token = ibinderForJavaObject(env, tokenObj);
+        env->DeleteLocalRef(tokenObj);
     } else {
         mInfo.token.clear();
     }
@@ -161,6 +169,24 @@
         env->DeleteLocalRef(inputApplicationHandleObj);
     }
 
+    mInfo.replaceTouchableRegionWithCrop = env->GetBooleanField(obj,
+            gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop);
+
+    jobject handleObj = env->GetObjectField(obj,
+            gInputWindowHandleClassInfo.touchableRegionCropHandle.handle);
+    if (handleObj) {
+        // Promote java weak reference.
+        jobject strongHandleObj = env->CallObjectMethod(handleObj,
+                gInputWindowHandleClassInfo.touchableRegionCropHandle.get);
+        if (strongHandleObj) {
+            mInfo.touchableRegionCropHandle = ibinderForJavaObject(env, strongHandleObj);
+            env->DeleteLocalRef(strongHandleObj);
+        } else {
+            mInfo.touchableRegionCropHandle.clear();
+        }
+        env->DeleteLocalRef(handleObj);
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -220,6 +246,10 @@
         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
         LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
 
+#define GET_METHOD_ID(var, clazz, methodName, methodSignature) \
+        var = env->GetMethodID(clazz, methodName, methodSignature); \
+        LOG_FATAL_IF(! (var), "Unable to find method " methodName);
+
 int register_android_view_InputWindowHandle(JNIEnv* env) {
     int res = jniRegisterNativeMethods(env, "android/view/InputWindowHandle",
             gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
@@ -303,6 +333,18 @@
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz,
             "portalToDisplayId", "I");
+
+    GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz,
+            "replaceTouchableRegionWithCrop", "Z");
+
+    jclass weakRefClazz;
+    FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
+
+    GET_METHOD_ID(gInputWindowHandleClassInfo.touchableRegionCropHandle.get, weakRefClazz,
+             "get", "()Ljava/lang/Object;")
+
+    GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionCropHandle.handle, clazz,
+            "touchableRegionCropHandle", "Ljava/lang/ref/WeakReference;");
     return 0;
 }