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

Commit 51311816 authored by Adrian Roos's avatar Adrian Roos Committed by Jorim Jaggi
Browse files

WM: Fix broken layout hint calculation

Fixes issues with the layout hint calculation that would end up
dispatching negative insets to applications and either causing a WTF
or previous to the workaround crashing apps.

The somewhat complex way of calculating insets ad-hoc is replaced with
a much clearer and less error prone approach that mirrors how insets
are actually calculated during regular layout. Doing that conveniently
eliminates the code that was buggy in the first place (the intersect in
calculateRelevantTaskInsets did not properly check for the non-interecting
case).

Change-Id: I05682943c7174a6c5a73862152bfbe4876f517f7
Fixes: 110834518
Test: atest PhoneWindowManagerLayoutTest
parent f8585dcb
Loading
Loading
Loading
Loading
+21 −42
Original line number Diff line number Diff line
@@ -290,6 +290,7 @@ import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayFrames;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import com.android.server.wm.utils.InsetUtils;

import java.io.File;
import java.io.FileReader;
@@ -4536,16 +4537,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    @Override
    // TODO: Should probably be moved into DisplayFrames.
    public boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
            DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
    public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
            DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
        final int fl = PolicyControl.getWindowFlags(null, attrs);
        final int pfl = attrs.privateFlags;
        final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
        final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
        final int displayRotation = displayFrames.mRotation;
        final int displayWidth = displayFrames.mDisplayWidth;
        final int displayHeight = displayFrames.mDisplayHeight;

        final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
        if (useOutsets) {
@@ -4569,45 +4569,40 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;

        if (layoutInScreenAndInsetDecor && !screenDecor) {
            int availRight, availBottom;
            if (canHideNavigationBar() &&
                    (sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
                outFrame.set(displayFrames.mUnrestricted);
                availRight = displayFrames.mUnrestricted.right;
                availBottom = displayFrames.mUnrestricted.bottom;
            } else {
                outFrame.set(displayFrames.mRestricted);
                availRight = displayFrames.mRestricted.right;
                availBottom = displayFrames.mRestricted.bottom;
            }
            outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top,
                    availRight - displayFrames.mStable.right,
                    availBottom - displayFrames.mStable.bottom);

            if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
            final Rect sf;
            if (floatingStack) {
                sf = null;
            } else {
                sf = displayFrames.mStable;
            }

            final Rect cf;
            if (floatingStack) {
                cf = null;
            } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
                if ((fl & FLAG_FULLSCREEN) != 0) {
                    outContentInsets.set(displayFrames.mStableFullscreen.left,
                            displayFrames.mStableFullscreen.top,
                            availRight - displayFrames.mStableFullscreen.right,
                            availBottom - displayFrames.mStableFullscreen.bottom);
                    cf = displayFrames.mStableFullscreen;
                } else {
                    outContentInsets.set(outStableInsets);
                    cf = displayFrames.mStable;
                }
            } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
                outContentInsets.setEmpty();
                cf = displayFrames.mOverscan;
            } else {
                outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
                        availRight - displayFrames.mCurrent.right,
                        availBottom - displayFrames.mCurrent.bottom);
                cf = displayFrames.mCurrent;
            }

            if (taskBounds != null) {
                calculateRelevantTaskInsets(taskBounds, outContentInsets,
                        displayWidth, displayHeight);
                calculateRelevantTaskInsets(taskBounds, outStableInsets,
                        displayWidth, displayHeight);
                outFrame.intersect(taskBounds);
            }
            InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
            InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
                    .getDisplayCutout());
            return mForceShowSystemBars;
@@ -4628,22 +4623,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    }

    /**
     * For any given task bounds, the insets relevant for these bounds given the insets relevant
     * for the entire display.
     */
    private void calculateRelevantTaskInsets(Rect taskBounds, Rect inOutInsets, int displayWidth,
            int displayHeight) {
        mTmpRect.set(0, 0, displayWidth, displayHeight);
        mTmpRect.inset(inOutInsets);
        mTmpRect.intersect(taskBounds);
        int leftInset = mTmpRect.left - taskBounds.left;
        int topInset = mTmpRect.top - taskBounds.top;
        int rightInset = taskBounds.right - mTmpRect.right;
        int bottomInset = taskBounds.bottom - mTmpRect.bottom;
        inOutInsets.set(leftInset, topInset, rightInset, bottomInset);
    }

    private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
        return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
+3 −3
Original line number Diff line number Diff line
@@ -71,7 +71,6 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -1180,6 +1179,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     * @param taskBounds The bounds of the task this window is on or {@code null} if no task is
     *                   associated with the window.
     * @param displayFrames display frames.
     * @param floatingStack Whether the window's stack is floating.
     * @param outFrame The frame of the window.
     * @param outContentInsets The areas covered by system windows, expressed as positive insets.
     * @param outStableInsets The areas covered by stable system windows irrespective of their
@@ -1190,8 +1190,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     *         See {@link #isNavBarForcedShownLw(WindowState)}.
     */
    default boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
            DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets, Rect outOutsets,
            DisplayFrames displayFrames, boolean floatingStack,
            Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout) {
        return false;
    }
+6 −2
Original line number Diff line number Diff line
@@ -1469,14 +1469,18 @@ public class WindowManagerService extends IWindowManager.Stub
            displayFrames.onDisplayInfoUpdated(displayInfo,
                    displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation));
            final Rect taskBounds;
            final boolean floatingStack;
            if (atoken != null && atoken.getTask() != null) {
                taskBounds = mTmpRect;
                atoken.getTask().getBounds(mTmpRect);
                floatingStack = atoken.getTask().isFloating();
            } else {
                taskBounds = null;
                floatingStack = false;
            }
            if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, outFrame,
                    outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
            if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
                    outFrame, outContentInsets, outStableInsets, outOutsets,
                    outDisplayCutout)) {
                res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
            }

+29 −0
Original line number Diff line number Diff line
@@ -16,8 +16,11 @@

package com.android.server.wm.utils;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;


/**
 * Utility methods to handle insets represented as rects.
 */
@@ -35,4 +38,30 @@ public class InsetUtils {
        inOutInsets.right += insetsToAdd.right;
        inOutInsets.bottom += insetsToAdd.bottom;
    }

    /**
     * Calculates the insets from the {@code outerFrame} to the {@code innerFrame}.
     *
     * Note that if a side of the outer frame is not actually on the outside, the inset for that
     * side will be clamped to zero.
     *
     * @param outerFrame the reference frame from which the insets are calculated
     * @param innerFrame the inset frame, to which the insets are calculated,
     *                   or null to clear the insets.
     * @param outInsets is set to the result of the inset calculation.
     */
    public static void insetsBetweenFrames(@NonNull Rect outerFrame, @Nullable Rect innerFrame,
            @NonNull Rect outInsets) {
        if (innerFrame == null) {
            outInsets.setEmpty();
            return;
        }
        final int w = outerFrame.width();
        final int h = outerFrame.height();
        outInsets.set(
                Math.min(w, Math.max(0, innerFrame.left - outerFrame.left)),
                Math.min(h, Math.max(0, innerFrame.top - outerFrame.top)),
                Math.min(w, Math.max(0, outerFrame.right - innerFrame.right)),
                Math.min(h, Math.max(0, outerFrame.bottom - innerFrame.bottom)));
    }
}
+33 −7
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -309,8 +308,8 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
        final Rect stable = new Rect();
        final Rect outsets = new Rect();
        final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, frame, content,
                stable, outsets, cutout);
        mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
                false /* floatingStack */, frame, content, stable, outsets, cutout);

        assertThat(frame, equalTo(mFrames.mUnrestricted));
        assertThat(content, equalTo(new Rect()));
@@ -331,8 +330,8 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
        final DisplayCutout.ParcelableWrapper outDisplayCutout =
                new DisplayCutout.ParcelableWrapper();

        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, outFrame, outContentInsets,
                outStableInsets, outOutsets, outDisplayCutout);
        mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */,
                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);

        assertThat(outFrame, is(mFrames.mUnrestricted));
        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
@@ -355,8 +354,35 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
        final DisplayCutout.ParcelableWrapper outDisplayCutout =
                new DisplayCutout.ParcelableWrapper();

        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, outFrame, outContentInsets,
                outStableInsets, outOutsets, outDisplayCutout);
        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */,
                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);

        assertThat(outFrame, is(taskBounds));
        assertThat(outContentInsets, is(new Rect()));
        assertThat(outStableInsets, is(new Rect()));
        assertThat(outOutsets, is(new Rect()));
        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
    }

    @Test
    public void layoutHint_appWindowInTask_outsideContentFrame() {
        // Initialize DisplayFrames
        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);

        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
        // possible overlap with the IME)
        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
                200, mFrames.mContent.bottom + 10);

        final Rect outFrame = new Rect();
        final Rect outContentInsets = new Rect();
        final Rect outStableInsets = new Rect();
        final Rect outOutsets = new Rect();
        final DisplayCutout.ParcelableWrapper outDisplayCutout =
                new DisplayCutout.ParcelableWrapper();

        mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */,
                outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);

        assertThat(outFrame, is(taskBounds));
        assertThat(outContentInsets, is(new Rect()));