Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8131f53 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "DisplayCutout: Properly letterbox bottom-only cutouts" into pi-dev

parents a501257e 5ed644f2
Loading
Loading
Loading
Loading
+13 −8
Original line number Diff line number Diff line
@@ -660,6 +660,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    static final Rect mTmpStableFrame = new Rect();
    static final Rect mTmpNavigationFrame = new Rect();
    static final Rect mTmpOutsetFrame = new Rect();
    private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
    private static final Rect mTmpRect = new Rect();

    WindowState mTopFullscreenOpaqueWindowState;
@@ -4636,7 +4637,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {

            w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
                    df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout);
                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout,
                    false /* parentFrameWasClippedByDisplayCutout */);
            final Rect frame = w.getFrameLw();

            if (frame.left <= 0 && frame.top <= 0) {
@@ -4699,7 +4701,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
                displayFrames.mDisplayCutout);
                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);

        // For layout, the status bar is always at the top with our fixed height.
        displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4844,7 +4846,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
                displayFrames.mDisplayCutout);
                displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());

        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
@@ -5299,10 +5301,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                        vf.set(cf);
                    }
                }
                pf.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
            }
        }

        boolean parentFrameWasClippedByDisplayCutout = false;
        final int cutoutMode = attrs.layoutInDisplayCutoutMode;
        final boolean attachedInParent = attached != null && !layoutInScreen;
        final boolean requestedHideNavigation =
@@ -5310,7 +5312,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
        // the cutout safe zone.
        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
            final Rect displayCutoutSafeExceptMaybeBars = mTmpRect;
            final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
            displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
            if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
@@ -5339,10 +5341,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                // The IME can always extend under the bottom cutout if the navbar is there.
                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
            }
            // Windows that are attached to a parent and laid out in said parent are already
            // avoidingthe cutout according to that parent and don't need to be further constrained.
            // Windows that are attached to a parent and laid out in said parent already avoid
            // the cutout according to that parent and don't need to be further constrained.
            if (!attachedInParent) {
                mTmpRect.set(pf);
                pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
                parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf);
            }
            // Make sure that NO_LIMITS windows clipped to the display don't extend under the
            // cutout.
@@ -5400,7 +5404,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                + " sf=" + sf.toShortString()
                + " osf=" + (osf == null ? "null" : osf.toShortString()));

        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout);
        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout,
                parentFrameWasClippedByDisplayCutout);

        // Dock windows carve out the bottom of the screen, so normal windows
        // can't appear underneath them.
+4 −1
Original line number Diff line number Diff line
@@ -220,10 +220,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
         * @param outsetFrame The frame that includes areas that aren't part of the surface but we
         * want to treat them as such.
         * @param displayCutout the display cutout
         * @param parentFrameWasClippedByDisplayCutout true if the parent frame would have been
         * different if there was no display cutout.
         */
        public void computeFrameLw(Rect parentFrame, Rect displayFrame,
                Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
                Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout);
                Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
                boolean parentFrameWasClippedByDisplayCutout);

        /**
         * Retrieve the current frame of the window that has been assigned by
+24 −14
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.os.PowerManager.DRAW_WAKE_LOCK;
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.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -35,7 +34,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCRE
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;
@@ -46,7 +44,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -410,6 +407,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP

    private final Rect mParentFrame = new Rect();

    /** Whether the parent frame would have been different if there was no display cutout. */
    private boolean mParentFrameWasClippedByDisplayCutout;

    // The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
    // screen area of the device.
    final Rect mDisplayFrame = new Rect();
@@ -839,7 +839,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    @Override
    public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
            Rect outsetFrame, WmDisplayCutout displayCutout) {
            Rect outsetFrame, WmDisplayCutout displayCutout,
            boolean parentFrameWasClippedByDisplayCutout) {
        if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
            // This window is being replaced and either already got information that it's being
            // removed or we are still waiting for some information. Because of this we don't
@@ -848,6 +849,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            return;
        }
        mHaveFrame = true;
        mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout;

        final Task task = getTask();
        final boolean inFullscreenContainer = inFullscreenContainer();
@@ -3055,23 +3057,31 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            // Only windows with an AppWindowToken are letterboxed.
            return false;
        }
        if (getDisplayContent().getDisplayInfo().displayCutout == null) {
            // No cutout, no letterbox.
        if (!mParentFrameWasClippedByDisplayCutout) {
            // Cutout didn't make a difference, no letterbox
            return false;
        }
        if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
            // Layout in cutout, no letterbox.
            return false;
        }
        // TODO: handle dialogs and other non-filling windows
        if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
            // Never layout in cutout, always letterbox.
            return true;
        if (!mAttrs.isFullscreen()) {
            // Not filling the parent frame, no letterbox
            return false;
        }
        // Otherwise we need a letterbox if the layout was smaller than the app window token allowed
        // it to be.
        return !frameCoversEntireAppTokenBounds();
    }
        // 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_FULLSCREEN)) != 0;

    /**
     * @return true if this window covers the entire bounds of its app window token
     * @throws NullPointerException if there is no app window token for this window
     */
    private boolean frameCoversEntireAppTokenBounds() {
        mTmpRect.set(mAppToken.getBounds());
        mTmpRect.intersectUnchecked(mFrame);
        return mAppToken.getBounds().equals(mTmpRect);
    }

    @Override
+2 −1
Original line number Diff line number Diff line
@@ -63,7 +63,8 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState {
    @Override
    public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
            Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
            @Nullable Rect outsetFrame, WmDisplayCutout displayCutout) {
            @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
            boolean parentFrameWasClippedByDisplayCutout) {
        this.parentFrame.set(parentFrame);
        this.displayFrame.set(displayFrame);
        this.overscanFrame.set(overlayFrame);
+20 −20
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@ public class WindowFrameTests extends WindowTestsBase {
        // When mFrame extends past cf, the content insets are
        // the difference between mFrame and ContentFrame. Visible
        // and stable frames work the same way.
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame,0, 0, 1000, 1000);
        assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
        assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -175,7 +175,7 @@ public class WindowFrameTests extends WindowTestsBase {
        w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
        w.mRequestedWidth = 100;
        w.mRequestedHeight = 100;
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 100, 100, 200, 200);
        assertRect(w.mContentInsets, 0, 0, 0, 0);
        // In this case the frames are shrunk to the window frame.
@@ -196,7 +196,7 @@ public class WindowFrameTests extends WindowTestsBase {

        // Here the window has FILL_PARENT, FILL_PARENT
        // so we expect it to fill the entire available frame.
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 0, 0, 1000, 1000);

        // It can select various widths and heights within the bounds.
@@ -204,14 +204,14 @@ public class WindowFrameTests extends WindowTestsBase {
        // and we use mRequestedWidth/mRequestedHeight
        w.mAttrs.width = 300;
        w.mAttrs.height = 300;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        // Explicit width and height without requested width/height
        // gets us nothing.
        assertRect(w.mFrame, 0, 0, 0, 0);

        w.mRequestedWidth = 300;
        w.mRequestedHeight = 300;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        // With requestedWidth/Height we can freely choose our size within the
        // parent bounds.
        assertRect(w.mFrame, 0, 0, 300, 300);
@@ -224,14 +224,14 @@ public class WindowFrameTests extends WindowTestsBase {
        w.mRequestedWidth = -1;
        w.mAttrs.width = 100;
        w.mAttrs.height = 100;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 0, 0, 100, 100);
        w.mAttrs.flags = 0;

        // But sizes too large will be clipped to the containing frame
        w.mRequestedWidth = 1200;
        w.mRequestedHeight = 1200;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 0, 0, 1000, 1000);

        // Before they are clipped though windows will be shifted
@@ -239,7 +239,7 @@ public class WindowFrameTests extends WindowTestsBase {
        w.mAttrs.y = 300;
        w.mRequestedWidth = 1000;
        w.mRequestedHeight = 1000;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 0, 0, 1000, 1000);

        // If there is room to move around in the parent frame the window will be shifted according
@@ -249,16 +249,16 @@ public class WindowFrameTests extends WindowTestsBase {
        w.mRequestedWidth = 300;
        w.mRequestedHeight = 300;
        w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 700, 0, 1000, 300);
        w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 700, 700, 1000, 1000);
        // Window specified  x and y are interpreted as offsets in the opposite
        // direction of gravity
        w.mAttrs.x = 100;
        w.mAttrs.y = 100;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, 600, 600, 900, 900);
    }

@@ -279,7 +279,7 @@ public class WindowFrameTests extends WindowTestsBase {
        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;

        final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT, false);
        // For non fullscreen tasks the containing frame is based off the
        // task bounds not the parent frame.
        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -291,7 +291,7 @@ public class WindowFrameTests extends WindowTestsBase {
        final int cfRight = logicalWidth / 2;
        final int cfBottom = logicalHeight / 2;
        final Rect cf = new Rect(0, 0, cfRight, cfBottom);
        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
        int contentInsetRight = taskRight - cfRight;
        int contentInsetBottom = taskBottom - cfBottom;
@@ -308,7 +308,7 @@ public class WindowFrameTests extends WindowTestsBase {
        final int insetRight = insetLeft + (taskRight - taskLeft);
        final int insetBottom = insetTop + (taskBottom - taskTop);
        task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
        contentInsetRight = insetRight - cfRight;
        contentInsetBottom = insetBottom - cfBottom;
@@ -340,13 +340,13 @@ public class WindowFrameTests extends WindowTestsBase {

        final Rect policyCrop = new Rect();

        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
        w.calculatePolicyCrop(policyCrop);
        assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);

        dcf.setEmpty();
        // Likewise with no decor frame we would get no crop
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
        w.calculatePolicyCrop(policyCrop);
        assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);

@@ -359,7 +359,7 @@ public class WindowFrameTests extends WindowTestsBase {
        w.mAttrs.height = logicalHeight / 2;
        w.mRequestedWidth = logicalWidth / 2;
        w.mRequestedHeight = logicalHeight / 2;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);

        w.calculatePolicyCrop(policyCrop);
        // Normally the crop is shrunk from the decor frame
@@ -396,7 +396,7 @@ public class WindowFrameTests extends WindowTestsBase {
        final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
        w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
                pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT);
                pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
        // For non fullscreen tasks the containing frame is based off the
        // task bounds not the parent frame.
        assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -415,7 +415,7 @@ public class WindowFrameTests extends WindowTestsBase {

        w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
                cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT);
                cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
        assertEquals(cf, w.mFrame);
        assertEquals(cf, w.getContentFrameLw());
        assertRect(w.mContentInsets, 0, 0, 0, 0);
@@ -433,7 +433,7 @@ public class WindowFrameTests extends WindowTestsBase {
        final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());

        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout);
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);

        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
        assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);