Move letterboxing to AppWinToken

Both letterboxes for filling the DisplayCutout area and to satisfy
maxAspectRatio constraints are moved to AppWindowToken.

Bug: 65689439
Test: adb shell cmd overlay enable com.android.internal.display.cutout.emulation && adb shell stop && adb shell start; verify Launcher has a black bar
Test: on a 18:9 device, open a legacy app. Ensure it has a proper max aspect ratio letterbox.
Change-Id: I8fd7802ce5ce14b8aa71232c62dc4283351b3021
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fc0564d..3473b7d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -24,6 +24,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.SurfaceControl.HIDDEN;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -224,6 +225,7 @@
 
     /** Whether this token should be boosted at the top of all app window tokens. */
     private boolean mNeedsZBoost;
+    private Letterbox mLetterbox;
 
     private final Point mTmpPoint = new Point();
     private final Rect mTmpRect = new Rect();
@@ -657,6 +659,7 @@
         if (destroyedSomething) {
             final DisplayContent dc = getDisplayContent();
             dc.assignWindowLayers(true /*setLayoutNeeded*/);
+            updateLetterbox(null);
         }
     }
 
@@ -923,6 +926,7 @@
     void removeChild(WindowState child) {
         super.removeChild(child);
         checkKeyguardFlagsChanged();
+        updateLetterbox(child);
     }
 
     private boolean waitingForReplacement() {
@@ -1388,6 +1392,33 @@
         return isInterestingAndDrawn;
     }
 
+    void updateLetterbox(WindowState winHint) {
+        final WindowState w = findMainWindow();
+        if (w != winHint && winHint != null && w != null) {
+            return;
+        }
+        final boolean needsLetterbox = w != null && w.isLetterboxedAppWindow()
+                && fillsParent() && w.hasDrawnLw();
+        if (needsLetterbox) {
+            if (mLetterbox == null) {
+                mLetterbox = new Letterbox(() -> makeChildSurface(null));
+            }
+            mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame);
+        } else if (mLetterbox != null) {
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            SurfaceControl.openTransaction();
+            try {
+                mLetterbox.hide(t);
+            } finally {
+                // TODO: This should use pendingTransaction eventually, but right now things
+                // happening on the animation finished callback are happening on the global
+                // transaction.
+                SurfaceControl.mergeToGlobalTransaction(t);
+                SurfaceControl.closeTransaction();
+            }
+        }
+    }
+
     @Override
     boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
         // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent
@@ -1614,6 +1645,8 @@
             // the status bar). In that case we need to use the final frame.
             if (freeform) {
                 frame.set(win.mFrame);
+            } else if (win.isLetterboxedAppWindow()) {
+                frame.set(getTask().getBounds());
             } else {
                 frame.set(win.mContainingFrame);
             }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4c5520f..a8e00dd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -697,6 +697,7 @@
 
         final AppWindowToken atoken = w.mAppToken;
         if (atoken != null) {
+            atoken.updateLetterbox(w);
             final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
             if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
                 mTmpUpdateAllDrawn.add(atoken);
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
new file mode 100644
index 0000000..8fa79ab
--- /dev/null
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.server.wm;
+
+import static android.view.SurfaceControl.HIDDEN;
+
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import java.util.function.Supplier;
+
+/**
+ * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an
+ * outer rect and an inner rect.
+ */
+public class Letterbox {
+
+    private static final Rect EMPTY_RECT = new Rect();
+
+    private final Supplier<SurfaceControl.Builder> mFactory;
+    private final Rect mOuter = new Rect();
+    private final Rect mInner = new Rect();
+    private final LetterboxSurface mTop = new LetterboxSurface("top");
+    private final LetterboxSurface mLeft = new LetterboxSurface("left");
+    private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
+    private final LetterboxSurface mRight = new LetterboxSurface("right");
+
+    /**
+     * Constructs a Letterbox.
+     *
+     * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
+     */
+    public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) {
+        mFactory = surfaceControlFactory;
+    }
+
+    /**
+     * Sets the dimensions of the the letterbox, such that the area between the outer and inner
+     * frames will be covered by black color surfaces.
+     *
+     * @param t     a transaction in which to set the dimensions
+     * @param outer the outer frame of the letterbox (this frame will be black, except the area
+     *              that intersects with the {code inner} frame).
+     * @param inner the inner frame of the letterbox (this frame will be clear)
+     */
+    public void setDimensions(SurfaceControl.Transaction t, Rect outer, Rect inner) {
+        mOuter.set(outer);
+        mInner.set(inner);
+
+        mTop.setRect(t, outer.left, outer.top, inner.right, inner.top);
+        mLeft.setRect(t, outer.left, inner.top, inner.left, outer.bottom);
+        mBottom.setRect(t, inner.left, inner.bottom, outer.right, outer.bottom);
+        mRight.setRect(t, inner.right, outer.top, outer.right, inner.bottom);
+    }
+
+    /**
+     * Hides the letterbox.
+     *
+     * @param t a transaction in which to hide the letterbox
+     */
+    public void hide(SurfaceControl.Transaction t) {
+        setDimensions(t, EMPTY_RECT, EMPTY_RECT);
+    }
+
+    /**
+     * Destroys the managed {@link SurfaceControl}s.
+     */
+    public void destroy() {
+        mOuter.setEmpty();
+        mInner.setEmpty();
+
+        mTop.destroy();
+        mLeft.destroy();
+        mBottom.destroy();
+        mRight.destroy();
+    }
+
+    private class LetterboxSurface {
+
+        private final String mType;
+        private SurfaceControl mSurface;
+
+        private int mLastLeft = 0;
+        private int mLastTop = 0;
+        private int mLastRight = 0;
+        private int mLastBottom = 0;
+
+        public LetterboxSurface(String type) {
+            mType = type;
+        }
+
+        public void setRect(SurfaceControl.Transaction t,
+                int left, int top, int right, int bottom) {
+            if (mLastLeft == left && mLastTop == top
+                    && mLastRight == right && mLastBottom == bottom) {
+                // Nothing changed.
+                return;
+            }
+
+            if (left < right && top < bottom) {
+                if (mSurface == null) {
+                    createSurface();
+                }
+                t.setPosition(mSurface, left, top);
+                t.setSize(mSurface, right - left, bottom - top);
+                t.show(mSurface);
+            } else if (mSurface != null) {
+                t.hide(mSurface);
+            }
+
+            mLastLeft = left;
+            mLastTop = top;
+            mLastRight = right;
+            mLastBottom = bottom;
+        }
+
+        private void createSurface() {
+            mSurface = mFactory.get().setName("Letterbox - " + mType)
+                    .setFlags(HIDDEN).setColorLayer(true).build();
+            mSurface.setLayer(-1);
+            mSurface.setColor(new float[]{0, 0, 0});
+        }
+
+        public void destroy() {
+            if (mSurface != null) {
+                mSurface.destroy();
+                mSurface = null;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index d2cbf88..33e560f 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -33,7 +33,7 @@
 // the surface control.
 //
 // See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side.
-class RemoteSurfaceTrace extends SurfaceControlWithBackground {
+class RemoteSurfaceTrace extends SurfaceControl {
     static final String TAG = "RemoteSurfaceTrace";
 
     final FileDescriptor mWriteFd;
@@ -42,7 +42,7 @@
     final WindowManagerService mService;
     final WindowState mWindow;
 
-    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped,
+    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped,
             WindowState window) {
         super(wrapped);
 
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
deleted file mode 100644
index 7c5bd43..0000000
--- a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2017 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
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
-
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-
-/**
- * SurfaceControl extension that has black background behind navigation bar area for fullscreen
- * letterboxed apps.
- */
-class SurfaceControlWithBackground extends SurfaceControl {
-    // SurfaceControl that holds the background.
-    private SurfaceControl mBackgroundControl;
-
-    // Flag that defines whether the background should be shown.
-    private boolean mVisible;
-
-    // Way to communicate with corresponding window.
-    private WindowSurfaceController mWindowSurfaceController;
-
-    // Rect to hold task bounds when computing metrics for background.
-    private Rect mTmpContainerRect = new Rect();
-
-    // Last metrics applied to the main SurfaceControl.
-    private float mLastWidth, mLastHeight;
-    private float mLastDsDx = 1, mLastDsDy = 1;
-    private float mLastX, mLastY;
-
-    // SurfaceFlinger doesn't support crop rectangles where width or height is non-positive.
-    // If we just set an empty crop it will behave as if there is no crop at all.
-    // To fix this we explicitly hide the surface and won't let it to be shown.
-    private boolean mHiddenForCrop = false;
-
-    public SurfaceControlWithBackground(SurfaceControlWithBackground other) {
-        super(other);
-        mBackgroundControl = other.mBackgroundControl;
-        mVisible = other.mVisible;
-        mWindowSurfaceController = other.mWindowSurfaceController;
-    }
-
-    public SurfaceControlWithBackground(String name, SurfaceControl.Builder b,
-            int windowType, int w, int h,
-            WindowSurfaceController windowSurfaceController) throws OutOfResourcesException {
-        super(b.build());
-
-        // We should only show background behind app windows that are letterboxed in a task.
-        if ((windowType != TYPE_BASE_APPLICATION && windowType != TYPE_APPLICATION_STARTING)
-                || !windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) {
-            return;
-        }
-        mWindowSurfaceController = windowSurfaceController;
-        mLastWidth = w;
-        mLastHeight = h;
-        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        mBackgroundControl = b.setName("Background for - " + name)
-                .setSize(mTmpContainerRect.width(), mTmpContainerRect.height())
-                .setFormat(OPAQUE)
-                .setColorLayer(true)
-                .build();
-    }
-
-    @Override
-    public void setAlpha(float alpha) {
-        super.setAlpha(alpha);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.setAlpha(alpha);
-    }
-
-    @Override
-    public void setLayer(int zorder) {
-        super.setLayer(zorder);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed.
-        mBackgroundControl.setLayer(zorder - 1);
-    }
-
-    @Override
-    public void setPosition(float x, float y) {
-        super.setPosition(x, y);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mLastX = x;
-        mLastY = y;
-        updateBgPosition();
-    }
-
-    private void updateBgPosition() {
-        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
-        final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx;
-        final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy;
-        mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY);
-    }
-
-    @Override
-    public void setSize(int w, int h) {
-        super.setSize(w, h);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mLastWidth = w;
-        mLastHeight = h;
-        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height());
-    }
-
-    @Override
-    public void setWindowCrop(Rect crop) {
-        super.setWindowCrop(crop);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        calculateBgCrop(crop);
-        mBackgroundControl.setWindowCrop(mTmpContainerRect);
-        mHiddenForCrop = mTmpContainerRect.isEmpty();
-        updateBackgroundVisibility();
-    }
-
-    @Override
-    public void setFinalCrop(Rect crop) {
-        super.setFinalCrop(crop);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        mBackgroundControl.setFinalCrop(mTmpContainerRect);
-    }
-
-    /**
-     * Compute background crop based on current animation progress for main surface control and
-     * update {@link #mTmpContainerRect} with new values.
-     */
-    private void calculateBgCrop(Rect crop) {
-        // Track overall progress of animation by computing cropped portion of status bar.
-        final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets;
-        float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top;
-        if (d > 1.f) {
-            // We're running expand animation from launcher, won't compute custom bg crop here.
-            mTmpContainerRect.setEmpty();
-            return;
-        }
-
-        // Compute new scaled width and height for background that will depend on current animation
-        // progress. Those consist of current crop rect for the main surface + scaled areas outside
-        // of letterboxed area.
-        // TODO: Because the progress is computed with low precision we're getting smaller values
-        // for background width/height then screen size at the end of the animation. Will round when
-        // the value is smaller then some empiric epsilon. However, this should be fixed by
-        // computing correct frames for letterboxed windows in WindowState.
-        d = d < 0.025f ? 0 : d;
-        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
-        int backgroundWidth = 0, backgroundHeight = 0;
-        // Compute additional offset for the background when app window is positioned not at (0,0).
-        // E.g. landscape with navigation bar on the left.
-        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
-        int offsetX = (int)((winFrame.left - mTmpContainerRect.left) * mLastDsDx),
-                offsetY = (int) ((winFrame.top - mTmpContainerRect.top) * mLastDsDy);
-
-        // Position and size background.
-        final int bgPosition = mWindowSurfaceController.mAnimator.mService.getNavBarPosition();
-
-        switch (bgPosition) {
-            case NAV_BAR_LEFT:
-                backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
-                backgroundHeight = crop.height();
-                offsetX += crop.left - backgroundWidth;
-                offsetY += crop.top;
-                break;
-            case NAV_BAR_RIGHT:
-                backgroundWidth = (int) ((mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
-                backgroundHeight = crop.height();
-                offsetX += crop.right;
-                offsetY += crop.top;
-                break;
-            case NAV_BAR_BOTTOM:
-                backgroundWidth = crop.width();
-                backgroundHeight = (int) ((mTmpContainerRect.height() - mLastHeight) * (1 - d)
-                        + 0.5);
-                offsetX += crop.left;
-                offsetY += crop.bottom;
-                break;
-        }
-        mTmpContainerRect.set(offsetX, offsetY, offsetX + backgroundWidth,
-                offsetY + backgroundHeight);
-    }
-
-    @Override
-    public void setLayerStack(int layerStack) {
-        super.setLayerStack(layerStack);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.setLayerStack(layerStack);
-    }
-
-    @Override
-    public void setOpaque(boolean isOpaque) {
-        super.setOpaque(isOpaque);
-        updateBackgroundVisibility();
-    }
-
-    @Override
-    public void setSecure(boolean isSecure) {
-        super.setSecure(isSecure);
-    }
-
-    @Override
-    public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
-        super.setMatrix(dsdx, dtdx, dtdy, dsdy);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy);
-        mLastDsDx = dsdx;
-        mLastDsDy = dsdy;
-        updateBgPosition();
-    }
-
-    @Override
-    public void hide() {
-        super.hide();
-        mVisible = false;
-        updateBackgroundVisibility();
-    }
-
-    @Override
-    public void show() {
-        super.show();
-        mVisible = true;
-        updateBackgroundVisibility();
-    }
-
-    @Override
-    public void destroy() {
-        super.destroy();
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.destroy();
-    }
-
-    @Override
-    public void release() {
-        super.release();
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.release();
-    }
-
-    @Override
-    public void setTransparentRegionHint(Region region) {
-        super.setTransparentRegionHint(region);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.setTransparentRegionHint(region);
-    }
-
-    @Override
-    public void deferTransactionUntil(IBinder handle, long frame) {
-        super.deferTransactionUntil(handle, frame);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.deferTransactionUntil(handle, frame);
-    }
-
-    @Override
-    public void deferTransactionUntil(Surface barrier, long frame) {
-        super.deferTransactionUntil(barrier, frame);
-
-        if (mBackgroundControl == null) {
-            return;
-        }
-        mBackgroundControl.deferTransactionUntil(barrier, frame);
-    }
-
-    private void updateBackgroundVisibility() {
-        if (mBackgroundControl == null) {
-            return;
-        }
-        final AppWindowToken appWindowToken = mWindowSurfaceController.mAnimator.mWin.mAppToken;
-        if (!mHiddenForCrop && mVisible && appWindowToken != null && appWindowToken.fillsParent()) {
-            mBackgroundControl.show();
-        } else {
-            mBackgroundControl.hide();
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0ad60c9..28e2760 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -20,16 +20,20 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.SurfaceControl.Transaction;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -2977,7 +2981,25 @@
 
     /** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */
     boolean isLetterboxedAppWindow() {
-        return !isInMultiWindowMode() && mAppToken != null && !mAppToken.matchParentBounds();
+        return !isInMultiWindowMode() && mAppToken != null && (!mAppToken.matchParentBounds()
+                || isLetterboxedForCutout());
+    }
+
+    private boolean isLetterboxedForCutout() {
+        if (getDisplayContent().getDisplayInfo().displayCutout == null) {
+            // No cutout, no letterbox.
+            return false;
+        }
+        if ((mAttrs.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0) {
+            // Layout in cutout, no letterbox.
+            return false;
+        }
+        // TODO: handle dialogs and other non-filling windows
+        // Letterbox if any fullscreen mode is set.
+        final int fl = mAttrs.flags;
+        final int sysui = mSystemUiVisibility;
+        return (fl & FLAG_FULLSCREEN) != 0
+                || (sysui & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_FULLSCREEN)) != 0;
     }
 
     boolean isDragResizeChanged() {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index e26a362..2f38556 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@
 
     final WindowStateAnimator mAnimator;
 
-    SurfaceControlWithBackground mSurfaceControl;
+    SurfaceControl mSurfaceControl;
 
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
@@ -108,13 +108,11 @@
                 .setFormat(format)
                 .setFlags(flags)
                 .setMetadata(windowType, ownerUid);
-        mSurfaceControl = new SurfaceControlWithBackground(
-                name, b, windowType, w, h, this);
+        mSurfaceControl = b.build();
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
         if (mService.mRoot.mSurfaceTraceEnabled) {
-            mSurfaceControl = new RemoteSurfaceTrace(
-                    mService.mRoot.mSurfaceTraceFd.getFileDescriptor(), mSurfaceControl, win);
+            installRemoteTrace(mService.mRoot.mSurfaceTraceFd.getFileDescriptor());
         }
     }
 
@@ -123,7 +121,7 @@
     }
 
     void removeRemoteTrace() {
-        mSurfaceControl = new SurfaceControlWithBackground(mSurfaceControl);
+        mSurfaceControl = new SurfaceControl(mSurfaceControl);
     }