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

Commit 2bf469b4 authored by Tiger Huang's avatar Tiger Huang
Browse files

Don't let windows in non-fill-screen windowing modes control insets

Controllable insets sources are attached to a display. If a window is in
a non-fill-screen windowing mode, (1) the window frame might not be able
to reach the insts sources, or (2) the window might need to share the
insets sources with others.

For (1), it makes less sense to let window to control insets sources,
since the window won't receive the insets regardless.

For (2), letting one of the windows control insets might make the layout
unstable when switching the control target to another window with a
different set of requested visible types.

This CL prevents windows in non-fill-screen windowing modes from
controlling insets.

Bug: 329124127
Flag: com.android.window.flags.force_show_system_bar_for_bubble
Test: atest InsetsPolicyTest
Change-Id: I97ae1040743f68a2b20cba1386a36ed4149f40d3
parent e3b2dbbf
Loading
Loading
Loading
Loading
+56 −22
Original line number Diff line number Diff line
@@ -31,12 +31,14 @@ import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_B
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
@@ -82,6 +84,7 @@ import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.LoadedApk;
import android.app.ResourcesManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -1610,7 +1613,8 @@ public class DisplayPolicy {

            // Record the top-fullscreen-app-window which will be used to determine the system UI
            // controlling window.
            if (mTopFullscreenOpaqueWindowState == null && !exitingStartingWindow) {
            if (mTopFullscreenOpaqueWindowState == null && !exitingStartingWindow
                    && fillsDisplayWindowingMode(win)) {
                mTopFullscreenOpaqueWindowState = win;
            }

@@ -2606,30 +2610,49 @@ public class DisplayPolicy {
        updateSystemBarAttributes();
    }

    private boolean fillsDisplayWindowingMode(@NonNull WindowState win) {
        if (!com.android.window.flags.Flags.forceShowSystemBarForBubble()) {
            return true;
        }
        if (!WindowConfiguration.inMultiWindowMode(win.getWindowingMode())) {
            // Always accept the window not in multi-window mode.
            return true;
        }
        // Accept the window in multi-window mode only if it fills the display.
        // e.g., A maximized free-form window.
        final Task task = win.getTask();
        final Rect bounds = task != null ? task.getBounds() : win.getBounds();
        return bounds.equals(mDisplayContent.getBounds());
    }

    void updateSystemBarAttributes() {
        // If there is no window focused, there will be nobody to handle the events
        // anyway, so just hang on in whatever state we're in until things settle down.
        WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
        WindowState winCandidate =
                mFocusedWindow != null && fillsDisplayWindowingMode(mFocusedWindow)
                        ? mFocusedWindow
                        : mTopFullscreenOpaqueWindowState;
        if (winCandidate == null) {
        if (winCandidate == null && !com.android.window.flags.Flags.forceShowSystemBarForBubble()) {
            return;
        }

        // Immersive mode confirmation should never affect the system bar visibility, otherwise
        // it will unhide the navigation bar and hide itself.
        if ((winCandidate.mAttrs.privateFlags
        if (winCandidate != null && (winCandidate.mAttrs.privateFlags
                & PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW) != 0) {
            if (mNotificationShade != null && mNotificationShade.canReceiveKeys()) {
                // Let notification shade control the system bar visibility.
                winCandidate = mNotificationShade;
            } else if (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()) {
            } else if (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()
                    && fillsDisplayWindowingMode(mLastFocusedWindow)) {
                // Immersive mode confirmation took the focus from mLastFocusedWindow which was
                // controlling the system bar visibility. Let it keep controlling the visibility.
                winCandidate = mLastFocusedWindow;
            } else {
                winCandidate = mTopFullscreenOpaqueWindowState;
            }
            if (winCandidate == null) {
            if (winCandidate == null
                    && !com.android.window.flags.Flags.forceShowSystemBarForBubble()) {
                return;
            }
        }
@@ -2637,7 +2660,7 @@ public class DisplayPolicy {
        mSystemUiControllingWindow = win;

        final int displayId = getDisplayId();
        final int disableFlags = win.getDisableFlags();
        final int disableFlags = win != null ? win.getDisableFlags() : 0;
        final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
        if (!mRelaunchingSystemBarColorApps.isEmpty()) {
            // The appearance of system bars might change while relaunching apps. We don't report
@@ -2648,25 +2671,30 @@ public class DisplayPolicy {
                mDisplayContent.mInputMethodWindow, mHasBottomNavigationBar);
        final boolean isNavbarColorManagedByIme =
                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
        final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
                navColorWin) | opaqueAppearance;
        final int appearance = updateLightNavigationBarLw(win != null
                        ? win.mAttrs.insetsFlags.appearance
                        : 0, navColorWin)
                | opaqueAppearance;
        final WindowState navBarControlWin = topAppHidesSystemBar(Type.navigationBars())
                ? mTopFullscreenOpaqueWindowState
                : win;
        final int behavior = navBarControlWin.mAttrs.insetsFlags.behavior;
        final String focusedApp = win.mAttrs.packageName;
        final boolean isFullscreen = !win.isRequestedVisible(Type.statusBars())
                || !win.isRequestedVisible(Type.navigationBars());
        final int behavior = navBarControlWin != null
                ? navBarControlWin.mAttrs.insetsFlags.behavior
                : BEHAVIOR_DEFAULT;
        final String focusedApp = win != null ? win.mAttrs.packageName : "none";
        final boolean isFullscreen = win != null && (!win.isRequestedVisible(Type.statusBars())
                || !win.isRequestedVisible(Type.navigationBars()));
        final AppearanceRegion[] statusBarAppearanceRegions =
                new AppearanceRegion[mStatusBarAppearanceRegionList.size()];
        mStatusBarAppearanceRegionList.toArray(statusBarAppearanceRegions);
        if (mLastDisableFlags != disableFlags) {
            mLastDisableFlags = disableFlags;
            final String cause = win.toString();
            final String cause = win != null ? win.toString() : "null";
            callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
                    cause));
        }
        final @InsetsType int requestedVisibleTypes = win.getRequestedVisibleTypes();
        final @InsetsType int requestedVisibleTypes = win != null
                ? win.getRequestedVisibleTypes() : 0;
        final LetterboxDetails[] letterboxDetails = new LetterboxDetails[mLetterboxDetails.size()];
        mLetterboxDetails.toArray(letterboxDetails);
        if (mLastAppearance == appearance
@@ -2737,7 +2765,7 @@ public class DisplayPolicy {

    @VisibleForTesting
    int updateLightNavigationBarLw(int appearance, WindowState navColorWin) {
        if (navColorWin == null || !isLightBarAllowed(navColorWin, Type.navigationBars())) {
        if (!isLightBarAllowed(navColorWin, Type.navigationBars())) {
            // Clear the light flag while not allowed.
            appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
            return appearance;
@@ -2750,7 +2778,7 @@ public class DisplayPolicy {
        return appearance;
    }

    private int updateSystemBarsLw(WindowState win, int disableFlags) {
    private int updateSystemBarsLw(@Nullable WindowState win, int disableFlags) {
        final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
        // TODO(b/407898759): Migrate to have WM Shell to override the insets visibility based on
        // top focused Task.
@@ -2798,15 +2826,17 @@ public class DisplayPolicy {
        if (wasImmersiveMode != isImmersiveMode) {
            mIsImmersiveMode = isImmersiveMode;
            // The immersive confirmation window should be attached to the immersive window root.
            final RootDisplayArea root = win.getRootDisplayArea();
            final int rootDisplayAreaId = root == null ? FEATURE_UNDEFINED : root.mFeatureId;
            final RootDisplayArea root = win != null ? win.getRootDisplayArea() : null;
            final int rootDisplayAreaId = root != null ? root.mFeatureId : FEATURE_UNDEFINED;
            final int windowType = win != null ? win.getWindowType() : INVALID_WINDOW_TYPE;
            // TODO(b/277290737): Move this to the client side, instead of using a proxy.
            callStatusBarSafely(statusBar -> statusBar.immersiveModeChanged(getDisplayId(),
                        rootDisplayAreaId, isImmersiveMode, win.getWindowType()));
                        rootDisplayAreaId, isImmersiveMode, windowType));
        }

        // Show transient bars for panic if needed.
        final boolean requestHideNavBar = !win.isRequestedVisible(Type.navigationBars());
        final boolean requestHideNavBar =
                win != null && !win.isRequestedVisible(Type.navigationBars());
        final long now = SystemClock.uptimeMillis();
        final boolean pendingPanic = mPendingPanicGestureUptime != 0
                && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
@@ -3133,6 +3163,10 @@ public class DisplayPolicy {
            pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
            pw.println(mTopFullscreenOpaqueWindowState);
        }
        if (mSystemUiControllingWindow != null) {
            pw.print(prefix); pw.print("mSystemUiControllingWindow=");
            pw.println(mSystemUiControllingWindow);
        }
        if (!mSystemBarColorApps.isEmpty()) {
            pw.print(prefix); pw.print("mSystemBarColorApps=");
            pw.println(mSystemBarColorApps);
+2 −2
Original line number Diff line number Diff line
@@ -672,7 +672,7 @@ class InsetsPolicy {
        return (mForciblyHidingTypes & types) == types;
    }

    void updateSystemBars(WindowState win, @InsetsType int displayForciblyShowingTypes,
    void updateSystemBars(@Nullable WindowState win, @InsetsType int displayForciblyShowingTypes,
            @InsetsType int displayForciblyHidingTypes, boolean showSystemBarsByLegacyPolicy) {
        final boolean hasDisplayOverride = displayForciblyShowingTypes != 0
                || displayForciblyHidingTypes != 0;
@@ -700,7 +700,7 @@ class InsetsPolicy {
        updateBarControlTarget(win);
    }

    private boolean forceShowingNavigationBars(WindowState win) {
    private boolean forceShowingNavigationBars(@Nullable WindowState win) {
        // When "force show navigation bar" is enabled, it means both force visible is true, and
        // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
        // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
+27 −15
Original line number Diff line number Diff line
@@ -158,6 +158,22 @@ public class InsetsPolicyTest extends WindowTestsBase {
        assertNull(controls);
    }

    @Test
    @EnableFlags(Flags.FLAG_FORCE_SHOW_SYSTEM_BAR_FOR_BUBBLE)
    public void testControlsForDispatch_nonFullscreenMultiWindowTaskVisible() {
        addStatusBar();
        addNavigationBar();

        final WindowState win = newWindowBuilder("app", TYPE_APPLICATION).setActivityType(
                ACTIVITY_TYPE_STANDARD).setWindowingMode(WINDOWING_MODE_MULTI_WINDOW).setDisplay(
                mDisplayContent).build();
        win.getTask().setBounds(new Rect(1, 1, 10, 10));
        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);

        // The non fullscreen multi window app window must not control any system bars.
        assertNull(controls);
    }

    @Test
    public void testControlsForDispatch_forceStatusBarVisible() {
        addStatusBar().mAttrs.forciblyShownTypes |= statusBars();
@@ -190,7 +206,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
        notifShade.mAttrs.forciblyShownTypes |= navigationBars();
        addNavigationBar();

        mDisplayContent.getInsetsPolicy().updateBarControlTarget(notifShade);
        mDisplayContent.getDisplayPolicy().focusChangedLw(null, notifShade);
        InsetsSourceControl[] controls
                = mDisplayContent.getInsetsStateController().getControlsForDispatch(notifShade);

@@ -232,7 +248,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
        displayPolicy.applyPostLayoutPolicyLw(dialog, dialog.mAttrs, fullscreenApp, null);
        displayPolicy.applyPostLayoutPolicyLw(fullscreenApp, fullscreenApp.mAttrs, null, null);
        displayPolicy.finishPostLayoutPolicyLw();
        mDisplayContent.getInsetsPolicy().updateBarControlTarget(dialog);
        displayPolicy.focusChangedLw(null, dialog);

        assertEquals(fullscreenApp, displayPolicy.getTopFullscreenOpaqueWindow());

@@ -255,12 +271,12 @@ public class InsetsPolicyTest extends WindowTestsBase {
        newFocusedFullscreenApp.setRequestedVisibleTypes(
                WindowInsets.Type.statusBars(), WindowInsets.Type.statusBars());
        // Make sure status bar is hidden by previous insets state.
        mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp);
        displayPolicy.focusChangedLw(dialog, fullscreenApp);

        final StatusBarManagerInternal sbmi =
                mDisplayContent.getDisplayPolicy().getStatusBarManagerInternal();
        clearInvocations(sbmi);
        mDisplayContent.getInsetsPolicy().updateBarControlTarget(newFocusedFullscreenApp);
        displayPolicy.focusChangedLw(fullscreenApp, newFocusedFullscreenApp);
        // The status bar should be shown by newFocusedFullscreenApp even
        // mTopFullscreenOpaqueWindowState is still fullscreenApp.
        verify(sbmi).setWindowState(mDisplayContent.mDisplayId, StatusBarManager.WINDOW_STATUS_BAR,
@@ -268,7 +284,7 @@ public class InsetsPolicyTest extends WindowTestsBase {

        // Add a system window: panel.
        final WindowState panel = addWindow(TYPE_STATUS_BAR_SUB_PANEL, "panel");
        mDisplayContent.getInsetsPolicy().updateBarControlTarget(panel);
        displayPolicy.focusChangedLw(newFocusedFullscreenApp, panel);

        // panel is the focused window, but it can only control navigation bar.
        // Because fullscreenApp is hiding status bar.
@@ -626,7 +642,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
        // Make both system bars invisible.
        mAppWindow.setRequestedVisibleTypes(
                0, navigationBars() | statusBars());
        policy.updateBarControlTarget(mAppWindow);
        mDisplayContent.getDisplayPolicy().focusChangedLw(null, mAppWindow);
        waitUntilWindowAnimatorIdle();
        assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
                .isSourceOrDefaultVisible(statusBarId, statusBars()));
@@ -656,8 +672,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
        addStatusBar().getControllableInsetProvider().getSource().setVisible(false);
        addNavigationBar().getControllableInsetProvider().setServerVisible(true);

        mDisplayContent.getDisplayPolicy().focusChangedLw(null, mAppWindow);
        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        policy.updateBarControlTarget(mAppWindow);
        policy.showTransient(navigationBars() | statusBars(),
                true /* isGestureOnSystemBar */);
        waitUntilWindowAnimatorIdle();
@@ -691,8 +707,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
        mAppWindow.setRequestedVisibleTypes(0, navigationBars() | statusBars());
        mAppWindow.mAboveInsetsState.addSource(navBarSource);
        mAppWindow.mAboveInsetsState.addSource(statusBarSource);
        mDisplayContent.getDisplayPolicy().focusChangedLw(null, mAppWindow);
        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        policy.updateBarControlTarget(mAppWindow);
        policy.showTransient(navigationBars() | statusBars(),
                true /* isGestureOnSystemBar */);
        waitUntilWindowAnimatorIdle();
@@ -738,13 +754,10 @@ public class InsetsPolicyTest extends WindowTestsBase {
        final WindowState app = addWindow(TYPE_APPLICATION, "app");
        final WindowState app2 = addWindow(TYPE_APPLICATION, "app");

        mDisplayContent.getDisplayPolicy().focusChangedLw(null, app);
        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
        policy.updateBarControlTarget(app);
        policy.showTransient(navigationBars() | statusBars(),
                true /* isGestureOnSystemBar */);
        final InsetsSourceControl[] controls =
                mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
        policy.updateBarControlTarget(app2);
        policy.showTransient(navigationBars() | statusBars(), true /* isGestureOnSystemBar */);
        mDisplayContent.getDisplayPolicy().focusChangedLw(app, app2);
        assertFalse(policy.isTransient(statusBars()));
        assertFalse(policy.isTransient(navigationBars()));
    }
@@ -941,7 +954,6 @@ public class InsetsPolicyTest extends WindowTestsBase {
        // Force update the focus in DisplayPolicy here. Otherwise, without server side focus
        // update, the policy relying on windowing type will never get updated.
        mDisplayContent.getDisplayPolicy().focusChangedLw(null, win);
        mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
        return mDisplayContent.getInsetsStateController().getControlsForDispatch(win);
    }
}