Support non-rectangular input regions.

This enables the system bar to carve out a region through which
events will be sent to the IME behind it.

Bug: 3238092
Change-Id: I69b855a8d9b5b3ee525266c0861826e53e5b5028
diff --git a/api/current.xml b/api/current.xml
index 68d3187..78104b0 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -95568,6 +95568,17 @@
  visibility="public"
 >
 </field>
+<field name="TOUCHABLE_INSETS_REGION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TOUCHABLE_INSETS_VISIBLE"
  type="int"
  transient="false"
@@ -95599,6 +95610,16 @@
  visibility="public"
 >
 </field>
+<field name="touchableRegion"
+ type="android.graphics.Region"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="visibleTopInsets"
  type="int"
  transient="false"
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 406b091..6baf1c2 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -283,6 +283,7 @@
         
         private Rect mContentInsetsBuffer = null;
         private Rect mVisibleInsetsBuffer = null;
+        private Region mTouchableAreaBuffer = null;
         
         Translator(float applicationScale, float applicationInvertedScale) {
             this.applicationScale = applicationScale;
@@ -395,14 +396,25 @@
 
         /**
          * Translate the visible insets in application window to Screen. This uses
-         * the internal buffer for content insets to avoid extra object allocation.
+         * the internal buffer for visible insets to avoid extra object allocation.
          */
-        public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
+        public Rect getTranslatedVisibleInsets(Rect visibleInsets) {
             if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect();
             mVisibleInsetsBuffer.set(visibleInsets);
             translateRectInAppWindowToScreen(mVisibleInsetsBuffer);
             return mVisibleInsetsBuffer;
         }
+
+        /**
+         * Translate the touchable area in application window to Screen. This uses
+         * the internal buffer for touchable area to avoid extra object allocation.
+         */
+        public Region getTranslatedTouchableArea(Region touchableArea) {
+            if (mTouchableAreaBuffer == null) mTouchableAreaBuffer = new Region();
+            mTouchableAreaBuffer.set(touchableArea);
+            mTouchableAreaBuffer.scale(applicationScale);
+            return mTouchableAreaBuffer;
+        }
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 23b9ad5..4d25bac 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -25,6 +25,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ResultReceiver;
@@ -283,11 +284,13 @@
                 View decor = getWindow().getWindow().getDecorView();
                 info.contentInsets.top = info.visibleInsets.top
                         = decor.getHeight();
+                info.touchableRegion.setEmpty();
                 info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
             } else {
                 onComputeInsets(mTmpInsets);
                 info.contentInsets.top = mTmpInsets.contentTopInsets;
                 info.visibleInsets.top = mTmpInsets.visibleTopInsets;
+                info.touchableRegion.set(mTmpInsets.touchableRegion);
                 info.setTouchableInsets(mTmpInsets.touchableInsets);
             }
         }
@@ -510,7 +513,14 @@
          * of the input method window.
          */
         public int visibleTopInsets;
-        
+
+        /**
+         * This is the region of the UI that is touchable.  It is used when
+         * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
+         * The region should be specified relative to the origin of the window frame.
+         */
+        public final Region touchableRegion = new Region();
+
         /**
          * Option for {@link #touchableInsets}: the entire window frame
          * can be touched.
@@ -531,11 +541,19 @@
          */
         public static final int TOUCHABLE_INSETS_VISIBLE
                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-        
+
+        /**
+         * Option for {@link #touchableInsets}: the region specified by
+         * {@link #touchableRegion} can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_REGION
+                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
         /**
          * Determine which area of the window is touchable by the user.  May
          * be one of: {@link #TOUCHABLE_INSETS_FRAME},
-         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_VISIBLE}. 
+         * {@link #TOUCHABLE_INSETS_CONTENT}, {@link #TOUCHABLE_INSETS_VISIBLE},
+         * or {@link #TOUCHABLE_INSETS_REGION}.
          */
         public int touchableInsets;
     }
@@ -950,6 +968,7 @@
         }
         outInsets.visibleTopInsets = loc[1];
         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE;
+        outInsets.touchableRegion.setEmpty();
     }
     
     /**
@@ -2153,6 +2172,7 @@
         p.println("Last computed insets:");
         p.println("  contentTopInsets=" + mTmpInsets.contentTopInsets
                 + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
-                + " touchableInsets=" + mTmpInsets.touchableInsets);
+                + " touchableInsets=" + mTmpInsets.touchableInsets
+                + " touchableRegion=" + mTmpInsets.touchableRegion);
     }
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index a5f405a..1218e81 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -101,7 +101,7 @@
      * {@link android.view.ViewTreeObserver.InternalInsetsInfo}.
      */
     void setInsets(IWindow window, int touchableInsets, in Rect contentInsets,
-            in Rect visibleInsets);
+            in Rect visibleInsets, in Region touchableRegion);
     
     /**
      * Return the current display size in which the window is being laid out,
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index ad9e6863..b7ab3c1 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1229,24 +1229,34 @@
         }
 
         if (computesInternalInsets) {
-            ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
-            final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
-            final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
-            givenContent.left = givenContent.top = givenContent.right
-                    = givenContent.bottom = givenVisible.left = givenVisible.top
-                    = givenVisible.right = givenVisible.bottom = 0;
+            // Clear the original insets.
+            final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
+            insets.reset();
+
+            // Compute new insets in place.
             attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
-            Rect contentInsets = insets.contentInsets;
-            Rect visibleInsets = insets.visibleInsets;
-            if (mTranslator != null) {
-                contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
-                visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
-            }
+
+            // Tell the window manager.
             if (insetsPending || !mLastGivenInsets.equals(insets)) {
                 mLastGivenInsets.set(insets);
+
+                // Translate insets to screen coordinates if needed.
+                final Rect contentInsets;
+                final Rect visibleInsets;
+                final Region touchableRegion;
+                if (mTranslator != null) {
+                    contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
+                    visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
+                    touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
+                } else {
+                    contentInsets = insets.contentInsets;
+                    visibleInsets = insets.visibleInsets;
+                    touchableRegion = insets.touchableRegion;
+                }
+
                 try {
                     sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
-                            contentInsets, visibleInsets);
+                            contentInsets, visibleInsets, touchableRegion);
                 } catch (RemoteException e) {
                 }
             }
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 06a0fa6..db87175 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Rect;
+import android.graphics.Region;
 
 import java.util.ArrayList;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -126,11 +127,18 @@
         public final Rect contentInsets = new Rect();
         
         /**
-         * Offsets from the fram of the window at which windows behind it
+         * Offsets from the frame of the window at which windows behind it
          * are visible.
          */
         public final Rect visibleInsets = new Rect();
-        
+
+        /**
+         * Touchable region defined relative to the origin of the frame of the window.
+         * Only used when {@link #setTouchableInsets(int)} is called with
+         * the option {@link #TOUCHABLE_INSETS_REGION}.
+         */
+        public final Region touchableRegion = new Region();
+
         /**
          * Option for {@link #setTouchableInsets(int)}: the entire window frame
          * can be touched.
@@ -148,11 +156,17 @@
          * the visible insets can be touched.
          */
         public static final int TOUCHABLE_INSETS_VISIBLE = 2;
-        
+
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the area inside of
+         * the provided touchable region in {@link #touchableRegion} can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_REGION = 3;
+
         /**
          * Set which parts of the window can be touched: either
          * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
-         * or {@link #TOUCHABLE_INSETS_VISIBLE}. 
+         * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
          */
         public void setTouchableInsets(int val) {
             mTouchableInsets = val;
@@ -165,11 +179,9 @@
         int mTouchableInsets;
         
         void reset() {
-            final Rect givenContent = contentInsets;
-            final Rect givenVisible = visibleInsets;
-            givenContent.left = givenContent.top = givenContent.right
-                    = givenContent.bottom = givenVisible.left = givenVisible.top
-                    = givenVisible.right = givenVisible.bottom = 0;
+            contentInsets.setEmpty();
+            visibleInsets.setEmpty();
+            touchableRegion.setEmpty();
             mTouchableInsets = TOUCHABLE_INSETS_FRAME;
         }
         
@@ -179,13 +191,16 @@
                     return false;
                 }
                 InternalInsetsInfo other = (InternalInsetsInfo)o;
+                if (mTouchableInsets != other.mTouchableInsets) {
+                    return false;
+                }
                 if (!contentInsets.equals(other.contentInsets)) {
                     return false;
                 }
                 if (!visibleInsets.equals(other.visibleInsets)) {
                     return false;
                 }
-                return mTouchableInsets == other.mTouchableInsets;
+                return touchableRegion.equals(other.touchableRegion);
             } catch (ClassCastException e) {
                 return false;
             }
@@ -194,6 +209,7 @@
         void set(InternalInsetsInfo other) {
             contentInsets.set(other.contentInsets);
             visibleInsets.set(other.visibleInsets);
+            touchableRegion.set(other.touchableRegion);
             mTouchableInsets = other.mTouchableInsets;
         }
     }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d28bdc9..f023e94 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -63,7 +63,6 @@
 extern int register_android_graphics_Movie(JNIEnv* env);
 extern int register_android_graphics_NinePatch(JNIEnv*);
 extern int register_android_graphics_PathEffect(JNIEnv* env);
-extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_Shader(JNIEnv* env);
 extern int register_android_graphics_Typeface(JNIEnv* env);
 extern int register_android_graphics_YuvImage(JNIEnv* env);
@@ -111,6 +110,7 @@
 extern int register_android_graphics_Picture(JNIEnv*);
 extern int register_android_graphics_PorterDuff(JNIEnv* env);
 extern int register_android_graphics_Rasterizer(JNIEnv* env);
+extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
 extern int register_android_graphics_Xfermode(JNIEnv* env);
 extern int register_android_graphics_PixelFormat(JNIEnv* env);
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 723cd37..c43b5ce 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -1,8 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #include "SkRegion.h"
 #include "SkPath.h"
 #include "GraphicsJNI.h"
 
+#include <binder/Parcel.h>
+#include "android_util_Binder.h"
+
 #include <jni.h>
+#include <android_runtime/AndroidRuntime.h>
+
+namespace android {
 
 static jfieldID gRegion_nativeInstanceFieldID;
 
@@ -134,9 +156,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include <binder/Parcel.h>
-#include "android_util_Binder.h"
-
 static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
 {
     if (parcel == NULL) {
@@ -215,8 +234,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include <android_runtime/AndroidRuntime.h>
-
 static JNINativeMethod gRegionIterMethods[] = {
     { "nativeConstructor",  "(I)I",                         (void*)RegionIter_constructor   },
     { "nativeDestructor",   "(I)V",                         (void*)RegionIter_destructor    },
@@ -268,3 +285,9 @@
     return android::AndroidRuntime::registerNativeMethods(env, "android/graphics/RegionIterator",
                                                        gRegionIterMethods, SK_ARRAY_COUNT(gRegionIterMethods));
 }
+
+SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj) {
+    return GetSkRegion(env, regionObj);
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/Region.h b/core/jni/android/graphics/Region.h
new file mode 100644
index 0000000..c15f06e
--- /dev/null
+++ b/core/jni/android/graphics/Region.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_GRAPHICS_REGION_H
+#define _ANDROID_GRAPHICS_REGION_H
+
+#include "jni.h"
+#include "SkRegion.h"
+
+namespace android {
+
+/* Gets the underlying SkRegion from a Region object. */
+extern SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_REGION_H
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index b5a4bd2..e314145 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -154,6 +154,24 @@
     return true;
 }
 
+static void dumpRegion(String8& dump, const SkRegion& region) {
+    if (region.isEmpty()) {
+        dump.append("<empty>");
+        return;
+    }
+
+    bool first = true;
+    for (SkRegion::Iterator it(region); !it.done(); it.next()) {
+        if (first) {
+            first = false;
+        } else {
+            dump.append("|");
+        }
+        const SkIRect& rect = it.rect();
+        dump.appendFormat("[%d,%d][%d,%d]", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+    }
+}
+
 
 // --- InputDispatcher ---
 
@@ -495,7 +513,7 @@
             if (!(flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
                 bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
                         | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
-                if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+                if (isTouchModal || window->touchableRegionContainsPoint(x, y)) {
                     // Found window.
                     return window;
                 }
@@ -1188,7 +1206,7 @@
                 if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
                     bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
                             | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
-                    if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+                    if (isTouchModal || window->touchableRegionContainsPoint(x, y)) {
                         if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
                             newTouchedWindow = window;
                         }
@@ -2884,9 +2902,7 @@
             dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
                     "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
                     "frame=[%d,%d][%d,%d], "
-                    "visibleFrame=[%d,%d][%d,%d], "
-                    "touchableArea=[%d,%d][%d,%d], "
-                    "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                    "touchableRegion=",
                     i, window.name.string(),
                     toString(window.paused),
                     toString(window.hasFocus),
@@ -2896,11 +2912,9 @@
                     window.layoutParamsFlags, window.layoutParamsType,
                     window.layer,
                     window.frameLeft, window.frameTop,
-                    window.frameRight, window.frameBottom,
-                    window.visibleFrameLeft, window.visibleFrameTop,
-                    window.visibleFrameRight, window.visibleFrameBottom,
-                    window.touchableAreaLeft, window.touchableAreaTop,
-                    window.touchableAreaRight, window.touchableAreaBottom,
+                    window.frameRight, window.frameBottom);
+            dumpRegion(dump, window.touchableRegion);
+            dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
                     window.ownerPid, window.ownerUid,
                     window.dispatchingTimeout / 1000000.0);
         }
diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp
index 9ce45f5..b552f6d 100644
--- a/services/input/InputWindow.cpp
+++ b/services/input/InputWindow.cpp
@@ -24,9 +24,8 @@
 
 // --- InputWindow ---
 
-bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
-    return x >= touchableAreaLeft && x <= touchableAreaRight
-            && y >= touchableAreaTop && y <= touchableAreaBottom;
+bool InputWindow::touchableRegionContainsPoint(int32_t x, int32_t y) const {
+    return touchableRegion.contains(x, y);
 }
 
 bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index b3d5a65..9c43067 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -23,6 +23,8 @@
 #include <utils/Timers.h>
 #include <utils/String8.h>
 
+#include <SkRegion.h>
+
 #include "InputApplication.h"
 
 namespace android {
@@ -129,14 +131,7 @@
     int32_t frameTop;
     int32_t frameRight;
     int32_t frameBottom;
-    int32_t visibleFrameLeft;
-    int32_t visibleFrameTop;
-    int32_t visibleFrameRight;
-    int32_t visibleFrameBottom;
-    int32_t touchableAreaLeft;
-    int32_t touchableAreaTop;
-    int32_t touchableAreaRight;
-    int32_t touchableAreaBottom;
+    SkRegion touchableRegion;
     bool visible;
     bool canReceiveKeys;
     bool hasFocus;
@@ -146,7 +141,7 @@
     int32_t ownerPid;
     int32_t ownerUid;
 
-    bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
+    bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
     bool frameContainsPoint(int32_t x, int32_t y) const;
 
     /* Returns true if the window is of a trusted type that is allowed to silently
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
index 1515290..2c2cdfe 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/InputWindow.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.graphics.Region;
 import android.view.InputChannel;
 
 /**
@@ -39,23 +40,14 @@
     // Dispatching timeout.
     public long dispatchingTimeoutNanos;
 
-    // Window frame area.
+    // Window frame.
     public int frameLeft;
     public int frameTop;
     public int frameRight;
     public int frameBottom;
 
-    // Window visible frame area.
-    public int visibleFrameLeft;
-    public int visibleFrameTop;
-    public int visibleFrameRight;
-    public int visibleFrameBottom;
-
-    // Window touchable area.
-    public int touchableAreaLeft;
-    public int touchableAreaTop;
-    public int touchableAreaRight;
-    public int touchableAreaBottom;
+    // Window touchable region.
+    public final Region touchableRegion = new Region();
 
     // Window is visible.
     public boolean visible;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index bdc779c..1b3725c 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -520,7 +520,7 @@
         int mRotation;
         int mAnimFlags;
 
-        private final Rect tmpRect = new Rect();
+        private final Region mTmpRegion = new Region();
 
         DragState(IBinder token, Surface surface, int flags, IBinder localWin) {
             mToken = token;
@@ -816,31 +816,13 @@
                     // not touchable == don't tell about drags
                     continue;
                 }
-                // account for the window's decor etc
-                tmpRect.set(child.mFrame);
-                if (child.mTouchableInsets == ViewTreeObserver
-                            .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
-                    // The point is inside of the window if it is
-                    // inside the frame, AND the content part of that
-                    // frame that was given by the application.
-                    tmpRect.left += child.mGivenContentInsets.left;
-                    tmpRect.top += child.mGivenContentInsets.top;
-                    tmpRect.right -= child.mGivenContentInsets.right;
-                    tmpRect.bottom -= child.mGivenContentInsets.bottom;
-                } else if (child.mTouchableInsets == ViewTreeObserver
-                            .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
-                    // The point is inside of the window if it is
-                    // inside the frame, AND the visible part of that
-                    // frame that was given by the application.
-                    tmpRect.left += child.mGivenVisibleInsets.left;
-                    tmpRect.top += child.mGivenVisibleInsets.top;
-                    tmpRect.right -= child.mGivenVisibleInsets.right;
-                    tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
-                }
+
+                child.getTouchableRegion(mTmpRegion);
+
                 final int touchFlags = flags &
-                    (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-                if (tmpRect.contains(x, y) || touchFlags == 0) {
+                        (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+                if (mTmpRegion.contains(x, y) || touchFlags == 0) {
                     // Found it
                     touchedWin = child;
                     break;
@@ -2667,7 +2649,7 @@
 
     void setInsetsWindow(Session session, IWindow client,
             int touchableInsets, Rect contentInsets,
-            Rect visibleInsets) {
+            Rect visibleInsets, Region touchableRegion) {
         long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mWindowMap) {
@@ -2676,6 +2658,7 @@
                     w.mGivenInsetsPending = false;
                     w.mGivenContentInsets.set(contentInsets);
                     w.mGivenVisibleInsets.set(visibleInsets);
+                    w.mGivenTouchableRegion.set(touchableRegion);
                     w.mTouchableInsets = touchableInsets;
                     mLayoutNeeded = true;
                     performLayoutAndPlaceSurfacesLocked();
@@ -5882,7 +5865,7 @@
             final InputWindow inputWindow = windowList.add();
             inputWindow.inputChannel = mDragState.mServerChannel;
             inputWindow.name = "drag";
-            inputWindow.layoutParamsFlags = 0;
+            inputWindow.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
             inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
             inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
             inputWindow.visible = true;
@@ -5900,15 +5883,8 @@
             inputWindow.frameRight = mDisplay.getWidth();
             inputWindow.frameBottom = mDisplay.getHeight();
 
-            inputWindow.visibleFrameLeft = inputWindow.frameLeft;
-            inputWindow.visibleFrameTop = inputWindow.frameTop;
-            inputWindow.visibleFrameRight = inputWindow.frameRight;
-            inputWindow.visibleFrameBottom = inputWindow.frameBottom;
-
-            inputWindow.touchableAreaLeft = inputWindow.frameLeft;
-            inputWindow.touchableAreaTop = inputWindow.frameTop;
-            inputWindow.touchableAreaRight = inputWindow.frameRight;
-            inputWindow.touchableAreaBottom = inputWindow.frameBottom;
+            // The drag window cannot receive new touches.
+            inputWindow.touchableRegion.setEmpty();
         }
 
         /* Updates the cached window information provided to the input dispatcher. */
@@ -5973,40 +5949,8 @@
                 inputWindow.frameTop = frame.top;
                 inputWindow.frameRight = frame.right;
                 inputWindow.frameBottom = frame.bottom;
-                
-                final Rect visibleFrame = child.mVisibleFrame;
-                inputWindow.visibleFrameLeft = visibleFrame.left;
-                inputWindow.visibleFrameTop = visibleFrame.top;
-                inputWindow.visibleFrameRight = visibleFrame.right;
-                inputWindow.visibleFrameBottom = visibleFrame.bottom;
-                
-                switch (child.mTouchableInsets) {
-                    default:
-                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
-                        inputWindow.touchableAreaLeft = frame.left;
-                        inputWindow.touchableAreaTop = frame.top;
-                        inputWindow.touchableAreaRight = frame.right;
-                        inputWindow.touchableAreaBottom = frame.bottom;
-                        break;
-                        
-                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
-                        Rect inset = child.mGivenContentInsets;
-                        inputWindow.touchableAreaLeft = frame.left + inset.left;
-                        inputWindow.touchableAreaTop = frame.top + inset.top;
-                        inputWindow.touchableAreaRight = frame.right - inset.right;
-                        inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
-                        break;
-                    }
-                        
-                    case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
-                        Rect inset = child.mGivenVisibleInsets;
-                        inputWindow.touchableAreaLeft = frame.left + inset.left;
-                        inputWindow.touchableAreaTop = frame.top + inset.top;
-                        inputWindow.touchableAreaRight = frame.right - inset.right;
-                        inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
-                        break;
-                    }
-                }
+
+                child.getTouchableRegion(inputWindow.touchableRegion);
             }
 
             // Send windows to native code.
@@ -6516,9 +6460,9 @@
         }
 
         public void setInsets(IWindow window, int touchableInsets,
-                Rect contentInsets, Rect visibleInsets) {
+                Rect contentInsets, Rect visibleInsets, Region touchableArea) {
             setInsetsWindow(this, window, touchableInsets, contentInsets,
-                    visibleInsets);
+                    visibleInsets, touchableArea);
         }
 
         public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
@@ -6648,7 +6592,7 @@
             synchronized (mWindowMap) {
                 long ident = Binder.clearCallingIdentity();
                 try {
-                    if (mDragState.mToken != token) {
+                    if (mDragState == null || mDragState.mToken != token) {
                         Slog.w(TAG, "Invalid drop-result claim by " + window);
                         throw new IllegalStateException("reportDropResult() by non-recipient");
                     }
@@ -6857,6 +6801,11 @@
         final Rect mGivenVisibleInsets = new Rect();
 
         /**
+         * This is the given touchable area relative to the window frame, or null if none.
+         */
+        final Region mGivenTouchableRegion = new Region();
+
+        /**
          * Flag indicating whether the touchable region should be adjusted by
          * the visible insets; if false the area outside the visible insets is
          * NOT touchable, so we must use those to adjust the frame during hit
@@ -8139,6 +8088,36 @@
             return true;
         }
 
+        public void getTouchableRegion(Region outRegion) {
+            final Rect frame = mFrame;
+            switch (mTouchableInsets) {
+                default:
+                case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+                    outRegion.set(frame);
+                    break;
+                case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+                    final Rect inset = mGivenContentInsets;
+                    outRegion.set(
+                            frame.left + inset.left, frame.top + inset.top,
+                            frame.right - inset.right, frame.bottom - inset.bottom);
+                    break;
+                }
+                case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+                    final Rect inset = mGivenVisibleInsets;
+                    outRegion.set(
+                            frame.left + inset.left, frame.top + inset.top,
+                            frame.right - inset.right, frame.bottom - inset.bottom);
+                    break;
+                }
+                case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
+                    final Region givenTouchableRegion = mGivenTouchableRegion;
+                    outRegion.set(givenTouchableRegion);
+                    outRegion.translate(frame.left, frame.top);
+                    break;
+                }
+            }
+        }
+
         void dump(PrintWriter pw, String prefix) {
             pw.print(prefix); pw.print("mSession="); pw.print(mSession);
                     pw.print(" mClient="); pw.println(mClient.asBinder());
diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp
index a4609a0..75154567c 100644
--- a/services/jni/com_android_server_InputWindow.cpp
+++ b/services/jni/com_android_server_InputWindow.cpp
@@ -21,6 +21,7 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <android_view_InputChannel.h>
+#include <android/graphics/Region.h>
 #include "com_android_server_InputWindow.h"
 #include "com_android_server_InputWindowHandle.h"
 
@@ -39,14 +40,7 @@
     jfieldID frameTop;
     jfieldID frameRight;
     jfieldID frameBottom;
-    jfieldID visibleFrameLeft;
-    jfieldID visibleFrameTop;
-    jfieldID visibleFrameRight;
-    jfieldID visibleFrameBottom;
-    jfieldID touchableAreaLeft;
-    jfieldID touchableAreaTop;
-    jfieldID touchableAreaRight;
-    jfieldID touchableAreaBottom;
+    jfieldID touchableRegion;
     jfieldID visible;
     jfieldID canReceiveKeys;
     jfieldID hasFocus;
@@ -108,22 +102,17 @@
             gInputWindowClassInfo.frameRight);
     outInputWindow->frameBottom = env->GetIntField(inputWindowObj,
             gInputWindowClassInfo.frameBottom);
-    outInputWindow->visibleFrameLeft = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.visibleFrameLeft);
-    outInputWindow->visibleFrameTop = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.visibleFrameTop);
-    outInputWindow->visibleFrameRight = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.visibleFrameRight);
-    outInputWindow->visibleFrameBottom = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.visibleFrameBottom);
-    outInputWindow->touchableAreaLeft = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.touchableAreaLeft);
-    outInputWindow->touchableAreaTop = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.touchableAreaTop);
-    outInputWindow->touchableAreaRight = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.touchableAreaRight);
-    outInputWindow->touchableAreaBottom = env->GetIntField(inputWindowObj,
-            gInputWindowClassInfo.touchableAreaBottom);
+
+    jobject regionObj = env->GetObjectField(inputWindowObj,
+            gInputWindowClassInfo.touchableRegion);
+    if (regionObj) {
+        SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
+        outInputWindow->touchableRegion.set(*region);
+        env->DeleteLocalRef(regionObj);
+    } else {
+        outInputWindow->touchableRegion.setEmpty();
+    }
+
     outInputWindow->visible = env->GetBooleanField(inputWindowObj,
             gInputWindowClassInfo.visible);
     outInputWindow->canReceiveKeys = env->GetBooleanField(inputWindowObj,
@@ -187,29 +176,8 @@
     GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz,
             "frameBottom", "I");
 
-    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameLeft, gInputWindowClassInfo.clazz,
-            "visibleFrameLeft", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameTop, gInputWindowClassInfo.clazz,
-            "visibleFrameTop", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameRight, gInputWindowClassInfo.clazz,
-            "visibleFrameRight", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.visibleFrameBottom, gInputWindowClassInfo.clazz,
-            "visibleFrameBottom", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz,
-            "touchableAreaLeft", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaTop, gInputWindowClassInfo.clazz,
-            "touchableAreaTop", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaRight, gInputWindowClassInfo.clazz,
-            "touchableAreaRight", "I");
-
-    GET_FIELD_ID(gInputWindowClassInfo.touchableAreaBottom, gInputWindowClassInfo.clazz,
-            "touchableAreaBottom", "I");
+    GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, gInputWindowClassInfo.clazz,
+            "touchableRegion", "Landroid/graphics/Region;");
 
     GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
             "visible", "Z");
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 443d881..8422d48 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -102,7 +102,7 @@
     }
 
     public void setInsets(IWindow window, int touchable, Rect contentInsets,
-            Rect visibleInsets) {
+            Rect visibleInsets, Region touchableRegion) {
         // pass for now.
     }