Add API for specifying popup window shadows and shadow insets

BUG: 14569120
BUG: 13211941

Change-Id: Ia21596b25a0471344d42d59377074f67fce00042
diff --git a/api/current.txt b/api/current.txt
index 1aac2fd..c4149db 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -918,6 +918,7 @@
     field public static final int popupAnimationStyle = 16843465; // 0x10102c9
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
+    field public static final int popupElevation = 16843918; // 0x101048e
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -37289,6 +37290,7 @@
     method public int getAnimationStyle();
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
+    method public float getElevation();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -37306,6 +37308,7 @@
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
     method public void setClippingEnabled(boolean);
     method public void setContentView(android.view.View);
+    method public void setElevation(float);
     method public void setFocusable(boolean);
     method public void setHeight(int);
     method public void setIgnoreCheekPress();
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d69d01d..be677ea 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.Surface.OutOfResourcesException;
 
@@ -247,21 +248,24 @@
     abstract void detachSurfaceTexture(long hardwareLayer);
 
     /**
-     * Setup the hardware renderer for drawing. This is called whenever the
-     * size of the target surface changes or when the surface is first created.
+     * Setup the hardware renderer for drawing. This is called whenever the size
+     * of the target surface changes or when the surface is first created.
      *
      * @param width Width of the drawing surface.
      * @param height Height of the drawing surface.
+     * @param surfaceInsets Insets between the drawing surface and actual
+     *            surface bounds.
      * @param lightX X position of the shadow casting light
      * @param lightY Y position of the shadow casting light
      * @param lightZ Z position of the shadow casting light
      * @param lightRadius radius of the shadow casting light
      */
-    abstract void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius);
+    abstract void setup(int width, int height, Rect surfaceInsets, float lightX, float lightY,
+            float lightZ, float lightRadius);
 
     /**
      * Gets the current width of the surface. This is the width that the surface
-     * was last set to in a call to {@link #setup(int, int, float, float, float, float)}.
+     * was last set to in a call to {@link #setup(int, int, Rect, float, float, float, float)}.
      *
      * @return the current width of the surface
      */
@@ -269,7 +273,7 @@
 
     /**
      * Gets the current height of the surface. This is the height that the surface
-     * was last set to in a call to {@link #setup(int, int, float, float, float, float)}.
+     * was last set to in a call to {@link #setup(int, int, Rect, float, float, float, float)}.
      *
      * @return the current width of the surface
      */
@@ -344,7 +348,6 @@
      * @param view The view to draw.
      * @param attachInfo AttachInfo tied to the specified view.
      * @param callbacks Callbacks invoked when drawing happens.
-     * @param dirty The dirty rectangle to update, can be null.
      */
     abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks);
 
@@ -369,17 +372,18 @@
      * @param height The height of the drawing surface.
      * @param surface The surface to hardware accelerate
      * @param metrics The display metrics used to draw the output.
+     * @param surfaceInsets The drawing surface insets to apply
      *
      * @return true if the surface was initialized, false otherwise. Returning
      *         false might mean that the surface was already initialized.
      */
-    boolean initializeIfNeeded(int width, int height, Surface surface, DisplayMetrics metrics)
+    boolean initializeIfNeeded(int width, int height, Surface surface, Rect surfaceInsets, DisplayMetrics metrics)
             throws OutOfResourcesException {
         if (isRequested()) {
             // We lost the gl context, so recreate it.
             if (!isEnabled()) {
                 if (initialize(surface)) {
-                    setup(width, height, metrics);
+                    setup(width, height, surfaceInsets, metrics);
                     return true;
                 }
             }
@@ -387,12 +391,12 @@
         return false;
     }
 
-    void setup(int width, int height, DisplayMetrics metrics) {
+    void setup(int width, int height, Rect surfaceInsets, DisplayMetrics metrics) {
         float lightX = width / 2.0f;
         float lightY = -400 * metrics.density;
         float lightZ = 800 * metrics.density;
         float lightRadius = 800 * metrics.density;
-        setup(width, height, lightX, lightY, lightZ, lightRadius);
+        setup(width, height, surfaceInsets, lightX, lightY, lightZ, lightRadius);
     }
 
     /**
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 57d1beb..acb2fe4 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -67,7 +68,16 @@
         PROFILE_PROPERTY_VISUALIZE_BARS,
     };
 
+    // Size of the rendered content.
     private int mWidth, mHeight;
+
+    // Actual size of the drawing surface.
+    private int mSurfaceWidth, mSurfaceHeight;
+
+    // Insets between the drawing surface and rendered content. These are
+    // applied as translation when updating the root render node.
+    private int mInsetTop, mInsetLeft;
+
     private long mNativeProxy;
     private boolean mInitialized = false;
     private RenderNode mRootNode;
@@ -154,11 +164,23 @@
     }
 
     @Override
-    void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius) {
+    void setup(int width, int height, Rect surfaceInsets, float lightX, float lightY, float lightZ,
+            float lightRadius) {
         mWidth = width;
         mHeight = height;
-        mRootNode.setLeftTopRightBottom(0, 0, mWidth, mHeight);
-        nSetup(mNativeProxy, width, height, lightX, lightY, lightZ, lightRadius);
+        if (surfaceInsets != null) {
+            mInsetLeft = surfaceInsets.left;
+            mInsetTop = surfaceInsets.top;
+            mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
+            mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
+        } else {
+            mInsetLeft = 0;
+            mInsetTop = 0;
+            mSurfaceWidth = width;
+            mSurfaceHeight = height;
+        }
+        mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
+        nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, lightX, lightY, lightZ, lightRadius);
     }
 
     @Override
@@ -214,9 +236,10 @@
         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
 
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
-        HardwareCanvas canvas = mRootNode.start(mWidth, mHeight);
+        HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
         try {
             canvas.save();
+            canvas.translate(mInsetLeft, mInsetTop);
             callbacks.onHardwarePreDraw(canvas);
             canvas.drawRenderNode(view.getDisplayList());
             callbacks.onHardwarePostDraw(canvas);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5def940..9405299 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1713,7 +1713,8 @@
                 if (hwInitialized ||
                         mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
                         mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
-                    mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight,
+                    final Rect shadowInsets = params != null ? params.shadowInsets : null;
+                    mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, shadowInsets,
                             mAttachInfo.mRootView.getResources().getDisplayMetrics());
                     if (!hwInitialized) {
                         mAttachInfo.mHardwareRenderer.invalidate(mSurface);
@@ -2211,20 +2212,22 @@
         return measureSpec;
     }
 
+    int mHardwareXOffset;
     int mHardwareYOffset;
     int mResizeAlpha;
     final Paint mResizePaint = new Paint();
 
     @Override
     public void onHardwarePreDraw(HardwareCanvas canvas) {
-        canvas.translate(0, -mHardwareYOffset);
+        canvas.translate(-mHardwareXOffset, -mHardwareYOffset);
     }
 
     @Override
     public void onHardwarePostDraw(HardwareCanvas canvas) {
         if (mResizeBuffer != null) {
             mResizePaint.setAlpha(mResizeAlpha);
-            canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
+            canvas.drawHardwareLayer(mResizeBuffer, mHardwareXOffset, mHardwareYOffset,
+                    mResizePaint);
         }
         drawAccessibilityFocusedDrawableIfNeeded(canvas);
     }
@@ -2368,15 +2371,17 @@
             attachInfo.mTreeObserver.dispatchOnScrollChanged();
         }
 
-        int yoff;
+        final WindowManager.LayoutParams params = mWindowAttributes;
+        final Rect surfaceInsets = params != null ? params.shadowInsets : null;
         boolean animating = mScroller != null && mScroller.computeScrollOffset();
+        final int curScrollY;
         if (animating) {
-            yoff = mScroller.getCurrY();
+            curScrollY = mScroller.getCurrY();
         } else {
-            yoff = mScrollY;
+            curScrollY = mScrollY;
         }
-        if (mCurScrollY != yoff) {
-            mCurScrollY = yoff;
+        if (mCurScrollY != curScrollY) {
+            mCurScrollY = curScrollY;
             fullRedrawNeeded = true;
         }
 
@@ -2425,11 +2430,14 @@
 
         attachInfo.mTreeObserver.dispatchOnDraw();
 
+        final int xOffset = surfaceInsets != null ? -surfaceInsets.left : 0;
+        final int yOffset = curScrollY + (surfaceInsets != null ? -surfaceInsets.top : 0);
         if (!dirty.isEmpty() || mIsAnimating) {
             if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
                 // Draw with hardware renderer.
                 mIsAnimating = false;
-                mHardwareYOffset = yoff;
+                mHardwareYOffset = yOffset;
+                mHardwareXOffset = xOffset;
                 mResizeAlpha = resizeAlpha;
 
                 dirty.setEmpty();
@@ -2450,8 +2458,9 @@
                         attachInfo.mHardwareRenderer.isRequested()) {
 
                     try {
-                        attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
-                                mSurface, attachInfo.mRootView.getResources().getDisplayMetrics());
+                        attachInfo.mHardwareRenderer.initializeIfNeeded(
+                                mWidth, mHeight, mSurface, surfaceInsets,
+                                attachInfo.mRootView.getResources().getDisplayMetrics());
                     } catch (OutOfResourcesException e) {
                         handleOutOfResourcesException(e);
                         return;
@@ -2462,7 +2471,7 @@
                     return;
                 }
 
-                if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
+                if (!drawSoftware(surface, attachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                     return;
                 }
             }
@@ -2475,9 +2484,9 @@
     }
 
     /**
-     * @return true if drawing was succesfull, false if an error occurred
+     * @return true if drawing was successful, false if an error occurred
      */
-    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
+    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
             boolean scalingRequired, Rect dirty) {
 
         // Draw with software renderer.
@@ -2526,7 +2535,7 @@
             // If we are applying an offset, we need to clear the area
             // where the offset doesn't appear to avoid having garbage
             // left in the blank areas.
-            if (!canvas.isOpaque() || yoff != 0) {
+            if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
             }
 
@@ -2542,7 +2551,7 @@
                         ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
             }
             try {
-                canvas.translate(0, -yoff);
+                canvas.translate(-xoff, -yoff);
                 if (mTranslator != null) {
                     mTranslator.translateCanvas(canvas);
                 }
@@ -3147,8 +3156,10 @@
                         if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){
                             mFullRedrawNeeded = true;
                             try {
+                                final WindowManager.LayoutParams lp = mWindowAttributes;
+                                final Rect surfaceInsets = lp != null ? lp.shadowInsets : null;
                                 mAttachInfo.mHardwareRenderer.initializeIfNeeded(
-                                        mWidth, mHeight, mSurface,
+                                        mWidth, mHeight, mSurface, surfaceInsets,
                                         mAttachInfo.mRootView.getResources().getDisplayMetrics());
                             } catch (OutOfResourcesException e) {
                                 Log.e(TAG, "OutOfResourcesException locking surface", e);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4eecc6a..c06b5d8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -19,7 +19,9 @@
 import android.app.Presentation;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1290,6 +1292,13 @@
          * field is added with {@link #y} to supply the <var>yAdj</var> parameter.
          */
         public float verticalMargin;
+
+        /**
+         * Positive insets between the drawing surface and window content.
+         *
+         * @hide
+         */
+        public Rect shadowInsets = new Rect();
     
         /**
          * The desired bitmap format.  May be one of the constants in
@@ -1571,6 +1580,10 @@
             out.writeInt(hasSystemUiListeners ? 1 : 0);
             out.writeInt(inputFeatures);
             out.writeLong(userActivityTimeout);
+            out.writeInt(shadowInsets.left);
+            out.writeInt(shadowInsets.top);
+            out.writeInt(shadowInsets.right);
+            out.writeInt(shadowInsets.bottom);
         }
         
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1613,6 +1626,7 @@
             hasSystemUiListeners = in.readInt() != 0;
             inputFeatures = in.readInt();
             userActivityTimeout = in.readLong();
+            shadowInsets.set(in.readInt(), in.readInt(), in.readInt(), in.readInt());
         }
     
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1644,6 +1658,8 @@
         /** {@hide} */
         public static final int TRANSLUCENT_FLAGS_CHANGED = 1<<19;
         /** {@hide} */
+        public static final int SHADOW_INSETS_CHANGED = 1<<20;
+        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
@@ -1778,6 +1794,11 @@
                 changes |= USER_ACTIVITY_TIMEOUT_CHANGED;
             }
 
+            if (!shadowInsets.equals(o.shadowInsets)) {
+                shadowInsets.set(o.shadowInsets);
+                changes |= SHADOW_INSETS_CHANGED;
+            }
+
             return changes;
         }
     
@@ -1877,6 +1898,9 @@
             if (userActivityTimeout >= 0) {
                 sb.append(" userActivityTimeout=").append(userActivityTimeout);
             }
+            if (!shadowInsets.equals(Insets.NONE)) {
+                sb.append(" shadowInsets=").append(shadowInsets);
+            }
             sb.append('}');
             return sb.toString();
         }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 01632ae..a35d447 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -109,6 +110,8 @@
     private int mPopupWidth;
     private int mPopupHeight;
 
+    private float mElevation;
+
     private int[] mDrawingLocation = new int[2];
     private int[] mScreenLocation = new int[2];
     private Rect mTempRect = new Rect();
@@ -196,6 +199,7 @@
                 attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
 
         mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
+        mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
         mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
 
         final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
@@ -319,25 +323,49 @@
     }
 
     /**
-     * <p>Return the drawable used as the popup window's background.</p>
+     * Return the drawable used as the popup window's background.
      *
-     * @return the background drawable or null
+     * @return the background drawable or {@code null} if not set
+     * @see #setBackgroundDrawable(Drawable)
+     * @attr ref android.R.styleable#PopupWindow_popupBackground
      */
     public Drawable getBackground() {
         return mBackground;
     }
 
     /**
-     * <p>Change the background drawable for this popup window. The background
-     * can be set to null.</p>
+     * Specifies the background drawable for this popup window. The background
+     * can be set to {@code null}.
      *
      * @param background the popup's background
+     * @see #getBackground()
+     * @attr ref android.R.styleable#PopupWindow_popupBackground
      */
     public void setBackgroundDrawable(Drawable background) {
         mBackground = background;
     }
 
     /**
+     * @return the elevation for this popup window in pixels
+     * @see #setElevation(float)
+     * @attr ref android.R.styleable#PopupWindow_popupElevation
+     */
+    public float getElevation() {
+        return mElevation;
+    }
+
+    /**
+     * Specifies the elevation for this popup window.
+     *
+     * @param elevation the popup's elevation in pixels
+     * @see #getElevation()
+     * @attr ref android.R.styleable#PopupWindow_popupElevation
+     */
+    public void setElevation(float elevation) {
+        mElevation = elevation;
+    }
+
+    /**
      * <p>Return the animation style to use the popup appears and disappears</p>
      *
      * @return the animation style to use the popup appears and disappears
@@ -973,7 +1001,7 @@
     /**
      * <p>Prepare the popup by embedding in into a new ViewGroup if the
      * background drawable is not null. If embedding is required, the layout
-     * parameters' height is mnodified to take into account the background's
+     * parameters' height is modified to take into account the background's
      * padding.</p>
      *
      * @param p the layout parameters of the popup's content view
@@ -998,13 +1026,15 @@
             PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, height
             );
-            popupViewContainer.setBackgroundDrawable(mBackground);
+            popupViewContainer.setBackground(mBackground);
             popupViewContainer.addView(mContentView, listParams);
 
             mPopupView = popupViewContainer;
         } else {
             mPopupView = mContentView;
         }
+
+        mPopupView.setElevation(mElevation);
         mPopupViewInitialLayoutDirectionInherited =
                 (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
         mPopupWidth = p.width;
@@ -1066,6 +1096,10 @@
         p.softInputMode = mSoftInputMode;
         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
 
+        // TODO: Use real shadow insets once that algorithm is finalized.
+        final int shadowInset = (int) Math.ceil(mElevation * 2);
+        p.shadowInsets.set(shadowInset, shadowInset, shadowInset, shadowInset);
+
         return p;
     }
 
diff --git a/core/res/res/drawable/popup_background_material.xml b/core/res/res/drawable/popup_background_material.xml
index 9e50790..b1f0cf5 100644
--- a/core/res/res/drawable/popup_background_material.xml
+++ b/core/res/res/drawable/popup_background_material.xml
@@ -14,7 +14,12 @@
      limitations under the License.
 -->
 
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/popup_background_mtrl_mult"
-    android:tint="?attr/colorBackground"
-    android:tintMode="multiply" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+
+    <corners
+            android:radius="2dp" />
+    <solid
+            android:color="?attr/colorBackground" />
+
+</shape>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4b708a7..e95239c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4057,6 +4057,8 @@
     <declare-styleable name="PopupWindow">
         <!-- The background to use for the popup window. -->
         <attr name="popupBackground" format="reference|color" />
+        <!-- Window elevation to use for the popup window. -->
+        <attr name="popupElevation" format="dimension" />
         <!-- The animation style to use for the popup window. -->
         <attr name="popupAnimationStyle" format="reference" />
         <!-- Whether the popup window should overlap its anchor view. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1eb8946..2dd67ba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2231,6 +2231,7 @@
   <public type="attr" name="buttonBarPositiveButtonStyle" />
   <public type="attr" name="buttonBarNeutralButtonStyle" />
   <public type="attr" name="buttonBarNegativeButtonStyle" />
+  <public type="attr" name="popupElevation" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index c1eb999..3880fb3 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -733,6 +733,7 @@
     <style name="Widget.Material.ListPopupWindow" parent="Widget.ListPopupWindow">
         <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
         <item name="popupBackground">@drawable/popup_background_material</item>
+        <item name="popupElevation">@dimen/floating_window_z</item>
         <item name="popupAnimationStyle">@style/Animation.Material.Popup</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index bda10de..8387b65 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -130,7 +130,8 @@
 
     // For debugging, this is the last information given to the surface flinger.
     boolean mSurfaceShown;
-    float mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
+    float mSurfaceX, mSurfaceY;
+    float mSurfaceW, mSurfaceH;
     int mSurfaceLayer;
     float mSurfaceAlpha;
 
@@ -666,117 +667,149 @@
     }
 
     SurfaceControl createSurfaceLocked() {
+        final WindowState w = mWin;
         if (mSurfaceControl == null) {
             if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
                     "createSurface " + this + ": mDrawState=DRAW_PENDING");
             mDrawState = DRAW_PENDING;
-            if (mWin.mAppToken != null) {
-                if (mWin.mAppToken.mAppAnimator.animation == null) {
-                    mWin.mAppToken.allDrawn = false;
-                    mWin.mAppToken.deferClearAllDrawn = false;
+            if (w.mAppToken != null) {
+                if (w.mAppToken.mAppAnimator.animation == null) {
+                    w.mAppToken.allDrawn = false;
+                    w.mAppToken.deferClearAllDrawn = false;
                 } else {
                     // Currently animating, persist current state of allDrawn until animation
                     // is complete.
-                    mWin.mAppToken.deferClearAllDrawn = true;
+                    w.mAppToken.deferClearAllDrawn = true;
                 }
             }
 
-            mService.makeWindowFreezingScreenIfNeededLocked(mWin);
+            mService.makeWindowFreezingScreenIfNeededLocked(w);
 
             int flags = SurfaceControl.HIDDEN;
-            final WindowManager.LayoutParams attrs = mWin.mAttrs;
+            final WindowManager.LayoutParams attrs = w.mAttrs;
 
             if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                 flags |= SurfaceControl.SECURE;
             }
-            if (DEBUG_VISIBILITY) Slog.v(
-                TAG, "Creating surface in session "
-                + mSession.mSurfaceSession + " window " + this
-                + " w=" + mWin.mCompatFrame.width()
-                + " h=" + mWin.mCompatFrame.height() + " format="
-                + attrs.format + " flags=" + flags);
 
-            int w = mWin.mCompatFrame.width();
-            int h = mWin.mCompatFrame.height();
+            int width;
+            int height;
             if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
                 // for a scaled surface, we always want the requested
                 // size.
-                w = mWin.mRequestedWidth;
-                h = mWin.mRequestedHeight;
+                width = w.mRequestedWidth;
+                height = w.mRequestedHeight;
+            } else {
+                width = w.mCompatFrame.width();
+                height = w.mCompatFrame.height();
             }
 
             // Something is wrong and SurfaceFlinger will not like this,
             // try to revert to sane values
-            if (w <= 0) w = 1;
-            if (h <= 0) h = 1;
+            if (width <= 0) {
+                width = 1;
+            }
+            if (height <= 0) {
+                height = 1;
+            }
 
+            float left = w.mFrame.left + w.mXOffset;
+            float top = w.mFrame.top + w.mYOffset;
+
+            // Adjust for surface insets.
+            width += attrs.shadowInsets.left + attrs.shadowInsets.right;
+            height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
+            left -= attrs.shadowInsets.left;
+            top -= attrs.shadowInsets.top;
+
+            if (DEBUG_VISIBILITY) {
+                Slog.v(TAG, "Creating surface in session "
+                        + mSession.mSurfaceSession + " window " + this
+                        + " w=" + width + " h=" + height
+                        + " x=" + left + " y=" + top
+                        + " format=" + attrs.format + " flags=" + flags);
+            }
+
+            // We may abort, so initialize to defaults.
             mSurfaceShown = false;
             mSurfaceLayer = 0;
             mSurfaceAlpha = 0;
             mSurfaceX = 0;
             mSurfaceY = 0;
-            mSurfaceW = w;
-            mSurfaceH = h;
-            mWin.mLastSystemDecorRect.set(0, 0, 0, 0);
+            w.mLastSystemDecorRect.set(0, 0, 0, 0);
+
+            // Set up surface control with initial size.
             try {
+                mSurfaceW = width;
+                mSurfaceH = height;
+
                 final boolean isHwAccelerated = (attrs.flags &
                         WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
                 final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
                 if (!PixelFormat.formatHasAlpha(attrs.format)) {
                     flags |= SurfaceControl.OPAQUE;
                 }
+
                 if (DEBUG_SURFACE_TRACE) {
                     mSurfaceControl = new SurfaceTrace(
                             mSession.mSurfaceSession,
                             attrs.getTitle().toString(),
-                            w, h, format, flags);
+                            width, height, format, flags);
                 } else {
                     mSurfaceControl = new SurfaceControl(
                         mSession.mSurfaceSession,
                         attrs.getTitle().toString(),
-                        w, h, format, flags);
+                        width, height, format, flags);
                 }
-                mWin.mHasSurface = true;
-                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
-                        "  CREATE SURFACE "
-                        + mSurfaceControl + " IN SESSION "
-                        + mSession.mSurfaceSession
-                        + ": pid=" + mSession.mPid + " format="
-                        + attrs.format + " flags=0x"
-                        + Integer.toHexString(flags)
-                        + " / " + this);
+
+                w.mHasSurface = true;
+
+                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                    Slog.i(TAG, "  CREATE SURFACE "
+                            + mSurfaceControl + " IN SESSION "
+                            + mSession.mSurfaceSession
+                            + ": pid=" + mSession.mPid + " format="
+                            + attrs.format + " flags=0x"
+                            + Integer.toHexString(flags)
+                            + " / " + this);
+                }
             } catch (OutOfResourcesException e) {
-                mWin.mHasSurface = false;
+                w.mHasSurface = false;
                 Slog.w(TAG, "OutOfResourcesException creating surface");
                 mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
                 mDrawState = NO_SURFACE;
                 return null;
             } catch (Exception e) {
-                mWin.mHasSurface = false;
+                w.mHasSurface = false;
                 Slog.e(TAG, "Exception creating surface", e);
                 mDrawState = NO_SURFACE;
                 return null;
             }
 
-            if (WindowManagerService.localLOGV) Slog.v(
-                TAG, "Got surface: " + mSurfaceControl
-                + ", set left=" + mWin.mFrame.left + " top=" + mWin.mFrame.top
-                + ", animLayer=" + mAnimLayer);
+            if (WindowManagerService.localLOGV) {
+                Slog.v(TAG, "Got surface: " + mSurfaceControl
+                        + ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+                        + ", animLayer=" + mAnimLayer);
+            }
+
             if (SHOW_LIGHT_TRANSACTIONS) {
                 Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
-                WindowManagerService.logSurface(mWin, "CREATE pos=("
-                        + mWin.mFrame.left + "," + mWin.mFrame.top + ") ("
-                        + mWin.mCompatFrame.width() + "x" + mWin.mCompatFrame.height()
+                WindowManagerService.logSurface(w, "CREATE pos=("
+                        + w.mFrame.left + "," + w.mFrame.top + ") ("
+                        + w.mCompatFrame.width() + "x" + w.mCompatFrame.height()
                         + "), layer=" + mAnimLayer + " HIDE", null);
             }
+
+            // Start a new transaction and apply position & offset.
             SurfaceControl.openTransaction();
             try {
+                mSurfaceX = left;
+                mSurfaceY = top;
+
                 try {
-                    mSurfaceX = mWin.mFrame.left + mWin.mXOffset;
-                    mSurfaceY = mWin.mFrame.top + mWin.mYOffset;
-                    mSurfaceControl.setPosition(mSurfaceX, mSurfaceY);
+                    mSurfaceControl.setPosition(left, top);
                     mSurfaceLayer = mAnimLayer;
-                    final DisplayContent displayContent = mWin.getDisplayContent();
+                    final DisplayContent displayContent = w.getDisplayContent();
                     if (displayContent != null) {
                         mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
                     }
@@ -1107,14 +1140,27 @@
 
     void applyDecorRect(final Rect decorRect) {
         final WindowState w = mWin;
+        int width = w.mFrame.width();
+        int height = w.mFrame.height();
+
         // Compute the offset of the window in relation to the decor rect.
-        final int offX = w.mXOffset + w.mFrame.left;
-        final int offY = w.mYOffset + w.mFrame.top;
+        int left = w.mXOffset + w.mFrame.left;
+        int top = w.mYOffset + w.mFrame.top;
+
+        // Adjust for surface insets.
+        final WindowManager.LayoutParams attrs = w.mAttrs;
+        width += attrs.shadowInsets.left + attrs.shadowInsets.right;
+        height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
+        left -= attrs.shadowInsets.left;
+        top -= attrs.shadowInsets.top;
+
         // Initialize the decor rect to the entire frame.
-        w.mSystemDecorRect.set(0, 0, w.mFrame.width(), w.mFrame.height());
+        w.mSystemDecorRect.set(0, 0, width, height);
+
         // Intersect with the decor rect, offsetted by window position.
-        w.mSystemDecorRect.intersect(decorRect.left-offX, decorRect.top-offY,
-                decorRect.right-offX, decorRect.bottom-offY);
+        w.mSystemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
+                decorRect.right - left, decorRect.bottom - top);
+
         // If size compatibility is being applied to the window, the
         // surface is scaled relative to the screen.  Also apply this
         // scaling to the crop rect.  We aren't using the standard rect
@@ -1212,10 +1258,12 @@
 
     void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
-        int width, height;
+
+        int width;
+        int height;
         if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-            // for a scaled surface, we just want to use
-            // the requested size.
+            // for a scaled surface, we always want the requested
+            // size.
             width  = w.mRequestedWidth;
             height = w.mRequestedHeight;
         } else {
@@ -1223,42 +1271,52 @@
             height = w.mCompatFrame.height();
         }
 
+        // Something is wrong and SurfaceFlinger will not like this,
+        // try to revert to sane values
         if (width < 1) {
             width = 1;
         }
         if (height < 1) {
             height = 1;
         }
-        final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
-        if (surfaceResized) {
-            mSurfaceW = width;
-            mSurfaceH = height;
-        }
 
-        final float left = w.mShownFrame.left;
-        final float top = w.mShownFrame.top;
-        if (mSurfaceX != left || mSurfaceY != top) {
+        float left = w.mShownFrame.left;
+        float top = w.mShownFrame.top;
+
+        // Adjust for surface insets.
+        final LayoutParams attrs = w.getAttrs();
+        width += attrs.shadowInsets.left + attrs.shadowInsets.right;
+        height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
+        left -= attrs.shadowInsets.left;
+        top -= attrs.shadowInsets.top;
+
+        final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
+        if (surfaceMoved) {
+            mSurfaceX = left;
+            mSurfaceY = top;
+
             try {
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
                         "POS " + left + ", " + top, null);
-                mSurfaceX = left;
-                mSurfaceY = top;
                 mSurfaceControl.setPosition(left, top);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error positioning surface of " + w
-                        + " pos=(" + left
-                        + "," + top + ")", e);
+                        + " pos=(" + left + "," + top + ")", e);
                 if (!recoveringMemory) {
                     mService.reclaimSomeSurfaceMemoryLocked(this, "position", true);
                 }
             }
         }
 
+        final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
         if (surfaceResized) {
+            mSurfaceW = width;
+            mSurfaceH = height;
+            mSurfaceResized = true;
+
             try {
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
                         "SIZE " + width + "x" + height, null);
-                mSurfaceResized = true;
                 mSurfaceControl.setSize(width, height);
                 mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                         WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);