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

Commit 55a37bb4 authored by Charles Chen's avatar Charles Chen Committed by Android Build Coastguard Worker
Browse files

Override type only if it's necessary

Before, WindowContext overrode type to all attached windows.
However, it led to unexpected layout when overriding an application sub panel.

This CL changes to override window type only if the type is not valid for
the attached WindowContext.

Test: atest WindowContextTest
Bug: 435160153
Flag: EXEMPT BUGFIX
Cherrypick-From: https://googleplex-android-review.googlesource.com/q/commit:dec18d7cffd6c57e8205716ababbb8c495fe2633
Merged-In: I161fc42386d5981b91505305110fa6214870e3e7
Change-Id: I161fc42386d5981b91505305110fa6214870e3e7
parent 86759676
Loading
Loading
Loading
Loading
+7 −10
Original line number Diff line number Diff line
@@ -392,11 +392,10 @@ public final class WindowManagerGlobal {
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
        if (!(params instanceof WindowManager.LayoutParams wparams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        final Context context = view.getContext();
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
@@ -507,12 +506,10 @@ public final class WindowManagerGlobal {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
        if (!(params instanceof WindowManager.LayoutParams wparams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
@@ -1115,14 +1112,14 @@ public final class WindowManagerGlobal {
    }

    /**
     * Checks whether {@link WindowContext#getWindowTypeOverride()} can be applied when
     * Checks whether {@link WindowContext#getFallbackWindowType()} can be applied when
     * {@link WindowManager#addView} or {@link WindowManager#updateViewLayout}.
     *
     * @param windowTypeToOverride the window type to override
     * @param fallbackWindowType the fallback window type
     * @param view the view that applies the window type
     */
    public boolean canApplyWindowTypeOverride(
            @WindowManager.LayoutParams.WindowType int windowTypeToOverride,
    public boolean canApplyFallbackWindowType(
            @WindowManager.LayoutParams.WindowType int fallbackWindowType,
            @NonNull View view) {
        synchronized (mLock) {
            final int index = findViewLocked(view, false /* required */);
@@ -1133,7 +1130,7 @@ public final class WindowManagerGlobal {
            final WindowManager.LayoutParams params = mParams.get(index);
            // If the view has been attached, we should make sure the override type matches the
            // existing one. The window type can't be changed after the view was added.
            return windowTypeToOverride == params.type;
            return fallbackWindowType == params.type;
        }
    }
}
+17 −6
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ public final class WindowManagerImpl implements WindowManager {

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyWindowTypeOverrideIfNeeded(params, view);
        fallbackWindowTypeIfNeeded(params, view);
        applyTokens(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
@@ -170,7 +170,7 @@ public final class WindowManagerImpl implements WindowManager {

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyWindowTypeOverrideIfNeeded(params, view);
        fallbackWindowTypeIfNeeded(params, view);
        applyTokens(params);
        mGlobal.updateViewLayout(view, params);
    }
@@ -191,7 +191,7 @@ public final class WindowManagerImpl implements WindowManager {
        if (!(mContext instanceof WindowProvider windowProvider)) {
            return;
        }
        if (windowProvider.isValidWindowType(windowType)) {
        if (windowProvider.isSelfOrSubWindowType(windowType)) {
            return;
        }
        IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
@@ -209,7 +209,14 @@ public final class WindowManagerImpl implements WindowManager {
                + " match type in WindowManager.LayoutParams", exception);
    }

    private void applyWindowTypeOverrideIfNeeded(
    /**
     * Fallbacks to {@link WindowContext#getFallbackWindowType()} if the type of the window context
     * associated window is not {@link WindowContext#isSelfOrSubWindowType}.
     *
     * @param params the passed {@link android.view.WindowManager.LayoutParams}
     * @param view   the window that are going to be attached or relayout
     */
    private void fallbackWindowTypeIfNeeded(
            @NonNull ViewGroup.LayoutParams params,
            @NonNull View view) {
        if (!(params instanceof WindowManager.LayoutParams wparams)) {
@@ -218,11 +225,15 @@ public final class WindowManagerImpl implements WindowManager {
        if (!(mContext instanceof WindowProvider windowProvider)) {
            return;
        }
        final int windowTypeOverride = windowProvider.getWindowTypeOverride();
        final int windowTypeOverride = windowProvider.getFallbackWindowType();
        if (windowTypeOverride == INVALID_WINDOW_TYPE) {
            return;
        }
        if (!mGlobal.canApplyWindowTypeOverride(windowTypeOverride, view)) {
        if (windowProvider.isSelfOrSubWindowType(wparams.type)) {
            // Don't need to override the type if the type is valid for this WindowContext.
            return;
        }
        if (!mGlobal.canApplyFallbackWindowType(windowTypeOverride, view)) {
            return;
        }
        if (isSubWindowType(windowTypeOverride) && mParentWindow == null) {
+37 −36
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
    @Nullable
    private final Bundle mOptions;
    @WindowType
    private int mWindowTypeOverride = INVALID_WINDOW_TYPE;
    private int mFallbackWindowType = INVALID_WINDOW_TYPE;
    private final ComponentCallbacksController mCallbacksController =
            new ComponentCallbacksController();
    private final WindowContextController mController;
@@ -176,37 +176,10 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
        mCallbacksController.unregisterCallbacks(callback);
    }

    /**
     * If set, this {@code WindowContext} will override the window type when
     * {@link WindowManager#addView} or {@link WindowManager#updateViewLayout}.
     * <p>
     * Allowed window types are {@link #getWindowType()} and
     * any sub-window types. If set to {@link WindowManager.LayoutParams#INVALID_WINDOW_TYPE},
     * this {@code WindowContext} won't override the type.
     * <p>
     * Note:
     * <ol>
     *   <li>If a view is attached, the window type won't be overridden to another window type.</li>
     *   <li>If a sub-window override is requested, a parent window must be prepared. It can
     *   be either by using {@link WindowManager} from a {@link Window} or calling
     *   {@link #attachWindow(View)} before adding any sub-windows, or
     *   {@link IllegalArgumentException} throws when {@link WindowManager#addView}.
     *   </li>
     * </ol>
     *
     * @throws IllegalArgumentException if the passed {@code windowTypeOverride} is not an allowed
     *     window type mentioned above.
     */
    public void setWindowTypeOverride(@WindowType int windowTypeOverride) {
        if (!isValidWindowType(windowTypeOverride) && windowTypeOverride != INVALID_WINDOW_TYPE) {
            throw new IllegalArgumentException(
                    "The window type override must be either "
                    + mType
                    + " or a sub window type, but it's "
                    + windowTypeOverride
            );
        }
        mWindowTypeOverride = windowTypeOverride;
    @WindowType
    @Override
    public int getFallbackWindowType() {
        return mFallbackWindowType;
    }

    /**
@@ -263,10 +236,38 @@ public class WindowContext extends ContextWrapper implements WindowProvider,
        return mOptions;
    }

    @WindowType
    @Override
    public int getWindowTypeOverride() {
        return mWindowTypeOverride;
    /**
     * If set, this {@code WindowContext} will override the window type when
     * {@link WindowManager#addView} or {@link WindowManager#updateViewLayout} if the window does
     * not use {@link #isSelfOrSubWindowType(int)}.
     * <p>
     * The default is {@link WindowManager.LayoutParams#INVALID_WINDOW_TYPE},
     * which won't override the type.
     * <p>
     * Note:
     * <ol>
     *   <li>If a view is attached, the window type won't be overridden to another window type.</li>
     *   <li>If a sub-window override is requested, a parent window must be prepared. It can
     *   be either by using {@link WindowManager} from a {@link Window} or calling
     *   {@link #attachWindow(View)} before adding any sub-windows, or
     *   {@link IllegalArgumentException} throws when {@link WindowManager#addView}.
     *   </li>
     * </ol>
     *
     * @throws IllegalArgumentException if the passed {@code fallbackWindowType} is not
     * {@link #isSelfOrSubWindowType(int)}.
     */
    public void setFallbackWindowType(@WindowType int fallbackWindowType) {
        if (!isSelfOrSubWindowType(fallbackWindowType)
                && fallbackWindowType != INVALID_WINDOW_TYPE) {
            throw new IllegalArgumentException(
                    "The window type override must be either "
                    + mType
                    + " or a sub window type, but it's "
                            + fallbackWindowType
            );
        }
        mFallbackWindowType = fallbackWindowType;
    }

/* === ConfigurationDispatcher APIs === */
+6 −11
Original line number Diff line number Diff line
@@ -66,25 +66,20 @@ public interface WindowProvider {

    /**
     * Gets the window type to be overridden when {@link android.view.WindowManager#addView}
     * and {@link android.view.WindowManager#updateViewLayout} if the added window is not
     * {@link #isSelfOrSubWindowType(int)}.
     */
    @WindowType
    default int getWindowTypeOverride() {
    default int getFallbackWindowType() {
        return INVALID_WINDOW_TYPE;
    }

    /**
     * Returns {@code true} if the given type is a valid window type for this
     * {@link WindowProvider}.
     * Returns {@code true} if the given type is {@link #getWindowType()} or a sub-window type.
     *
     * @param type the requested window type
     */
    default boolean isValidWindowType(@WindowType int type) {
        if (type == getWindowType()) {
            // Valid. The requested window type is the type of WindowContext.
            return true;
        }
        // Don't need to check sub-window type because sub window should be allowed to be attached
        // to the parent window.
        return isSubWindowType(type);
    default boolean isSelfOrSubWindowType(@WindowType int type) {
        return getWindowType() == type || isSubWindowType(type);
    }
}
+63 −18
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.window.WindowProvider.KEY_REPARENT_TO_DEFAULT_DISPLAY_WITH_DISPLAY_REMOVAL;

@@ -441,29 +442,29 @@ public class WindowContextTest {
    }

    @Test
    public void testSetWindowTypeOverride() {
    public void testSetFallbackWindowType() {
        int windowType = INVALID_WINDOW_TYPE;
        mWindowContext.setWindowTypeOverride(windowType);
        assertThat(mWindowContext.getWindowTypeOverride()).isEqualTo(windowType);
        mWindowContext.setFallbackWindowType(windowType);
        assertThat(mWindowContext.getFallbackWindowType()).isEqualTo(windowType);

        windowType = mWindowContext.getWindowType();
        mWindowContext.setWindowTypeOverride(windowType);
        assertThat(mWindowContext.getWindowTypeOverride()).isEqualTo(windowType);
        mWindowContext.setFallbackWindowType(windowType);
        assertThat(mWindowContext.getFallbackWindowType()).isEqualTo(windowType);

        windowType = TYPE_APPLICATION_ATTACHED_DIALOG;
        mWindowContext.setWindowTypeOverride(windowType);
        assertThat(mWindowContext.getWindowTypeOverride()).isEqualTo(windowType);
        mWindowContext.setFallbackWindowType(windowType);
        assertThat(mWindowContext.getFallbackWindowType()).isEqualTo(windowType);

        final int invalidType = TYPE_APPLICATION;
        assertThrows(IllegalArgumentException.class,
                () -> mWindowContext.setWindowTypeOverride(invalidType));
                () -> mWindowContext.setFallbackWindowType(invalidType));
    }

    @Test
    public void testSetWindowTypeOverrideAndAddView_invalidWindowType_noOverride() {
    public void testSetFallbackWindowTypeAndAddView_invalidFallbackWindowType_noOverride() {
        WindowManager.LayoutParams params =
                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        mWindowContext.setWindowTypeOverride(INVALID_WINDOW_TYPE);
        mWindowContext.setFallbackWindowType(INVALID_WINDOW_TYPE);
        final WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
        mInstrumentation.runOnMainSync(() -> wm.addView(new View(mWindowContext), params));

@@ -471,10 +472,10 @@ public class WindowContextTest {
    }

    @Test
    public void testSetWindowTypeOverrideAndAddView_windowContextType_override() {
    public void testSetFallbackWindowTypeAndAddView_Fallback_windowContextType_override() {
        int windowType = mWindowContext.getWindowType();
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        mWindowContext.setWindowTypeOverride(windowType);
        mWindowContext.setFallbackWindowType(windowType);
        final WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
        mInstrumentation.runOnMainSync(() -> wm.addView(new View(mWindowContext), params));

@@ -482,9 +483,9 @@ public class WindowContextTest {
    }

    @Test
    public void testSetWindowTypeOverrideAndAddView_subWindowWithoutParentWindow_throwException() {
    public void testSetFallbackWindowTypeAndAddView_subWindowWithoutParent_throwException() {
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        mWindowContext.setWindowTypeOverride(TYPE_APPLICATION_ATTACHED_DIALOG);
        mWindowContext.setFallbackWindowType(TYPE_APPLICATION_ATTACHED_DIALOG);
        final WindowManager wm = mWindowContext.getSystemService(WindowManager.class);

        assertThrows(IllegalArgumentException.class,
@@ -493,7 +494,7 @@ public class WindowContextTest {
    }

    @Test
    public void testSetWindowTypeOverrideAndAddView_subWindowWithParentWindow_override()
    public void testSetFallbackWindowTypeAndAddView_subWindowWithParent_override()
            throws InterruptedException {
        final View parentWindow = new View(mWindowContext);
        final AttachStateListener listener = new AttachStateListener();
@@ -505,7 +506,7 @@ public class WindowContextTest {

        assertTrue(listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));

        mWindowContext.setWindowTypeOverride(TYPE_APPLICATION_ATTACHED_DIALOG);
        mWindowContext.setFallbackWindowType(TYPE_APPLICATION_ATTACHED_DIALOG);

        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        mInstrumentation.runOnMainSync(() -> wm.addView(new View(mWindowContext), params));
@@ -514,7 +515,51 @@ public class WindowContextTest {
    }

    @Test
    public void testSetWindowTypeOverrideAndUpdateLayout_diffType_noOverride() {
    public void testSetFallbackWindowTypeAndAddView_isSubWindow_noOverride()
            throws InterruptedException {
        final View parentWindow = new View(mWindowContext);
        final AttachStateListener listener = new AttachStateListener();
        parentWindow.addOnAttachStateChangeListener(listener);
        mWindowContext.attachWindow(parentWindow);
        final WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
        mInstrumentation.runOnMainSync(() -> wm.addView(
                parentWindow, new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY)));

        assertTrue(listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));

        mWindowContext.setFallbackWindowType(TYPE_APPLICATION_ATTACHED_DIALOG);

        WindowManager.LayoutParams params =
                new WindowManager.LayoutParams(TYPE_APPLICATION_SUB_PANEL);
        mInstrumentation.runOnMainSync(() -> wm.addView(new View(mWindowContext), params));

        assertThat(params.type).isEqualTo(TYPE_APPLICATION_SUB_PANEL);
    }

    @Test
    public void testSetFallbackWindowTypeAndAddView_typeMatch_noOverride()
            throws InterruptedException {
        final View parentWindow = new View(mWindowContext);
        final AttachStateListener listener = new AttachStateListener();
        parentWindow.addOnAttachStateChangeListener(listener);
        mWindowContext.attachWindow(parentWindow);
        final WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
        mInstrumentation.runOnMainSync(() -> wm.addView(
                parentWindow, new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY)));

        assertTrue(listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));

        mWindowContext.setFallbackWindowType(TYPE_APPLICATION_ATTACHED_DIALOG);

        WindowManager.LayoutParams params =
                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        mInstrumentation.runOnMainSync(() -> wm.addView(new View(mWindowContext), params));

        assertThat(params.type).isEqualTo(TYPE_APPLICATION_OVERLAY);
    }

    @Test
    public void testSetFallbackWindowTypeAndUpdateLayout_diffType_noOverride() {
        final View view = new View(mWindowContext);
        mWindowContext.attachWindow(view);
        final WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
@@ -523,7 +568,7 @@ public class WindowContextTest {

        mInstrumentation.runOnMainSync(() -> wm.addView(view, params));

        mWindowContext.setWindowTypeOverride(TYPE_APPLICATION_ATTACHED_DIALOG);
        mWindowContext.setFallbackWindowType(TYPE_APPLICATION_ATTACHED_DIALOG);

        mInstrumentation.runOnMainSync(() -> wm.updateViewLayout(view, params));

Loading