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

Commit eaeb23fc authored by Ebru Kurnaz's avatar Ebru Kurnaz
Browse files

Use wm callbacks for sys decor in navigation bar instead of command queue.

Bug: 412613590
Test: DisplayRepositoryTest
Flag: com.android.window.flags.enable_sys_decors_callbacks_via_wm
Change-Id: I3175c2cb64850800681e113c94c5d385d7ff93ec
parent b3eb540d
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -29,17 +29,21 @@ import android.view.IDisplayWindowListener
import android.view.mockIWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.displaylib.DisplayDecorationListener
import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.mockCommandQueue
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.window.flags.Flags
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -73,6 +77,7 @@ class DisplayRepositoryTest : SysuiTestCase() {

    private val testHandler = kosmos.fakeHandler
    private val testScope = kosmos.testScope
    private val testDispacher = kosmos.testDispatcher
    private val defaultDisplay =
        display(type = TYPE_INTERNAL, id = DEFAULT_DISPLAY, state = Display.STATE_ON)

@@ -87,6 +92,10 @@ class DisplayRepositoryTest : SysuiTestCase() {
        }
    }

    private val displayRepositoryCompat: DisplaysWithDecorationsRepositoryCompat by lazy {
        kosmos.displaysWithDecorationsRepositoryCompat
    }

    @Before
    fun setup() {
        setDisplays(listOf(defaultDisplay))
@@ -734,6 +743,67 @@ class DisplayRepositoryTest : SysuiTestCase() {
            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyAddSystemDecor() =
        testScope.runTest {
            val listener = mock<DisplayDecorationListener>()
            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()

            wmListener.onDisplayAddSystemDecorations(1)

            verify(listener).onDisplayAddSystemDecorations(1)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyInitialDisplaysWithSysDecor() =
        testScope.runTest {
            setDisplays(0)
            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
            val listener = mock<DisplayDecorationListener>()

            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()

            verify(listener).onDisplayAddSystemDecorations(0)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyRemoveSystemDecor() =
        testScope.runTest {
            setDisplays(0)
            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
            val listener = mock<DisplayDecorationListener>()

            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()
            wmListener.onDisplayRemoveSystemDecorations(0)

            verify(listener).onDisplayAddSystemDecorations(0)
            verify(listener).onDisplayRemoveSystemDecorations(0)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyAllDisplaysWithSysDecor() =
        testScope.runTest {
            setDisplays(0, 2)
            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
            whenever(windowManager.shouldShowSystemDecors(2)).thenReturn(true)
            val listener = mock<DisplayDecorationListener>()

            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()
            wmListener.onDisplayAddSystemDecorations(3)

            verify(listener).onDisplayAddSystemDecorations(0)
            verify(listener).onDisplayAddSystemDecorations(2)
            verify(listener).onDisplayAddSystemDecorations(3)
        }

    @Test
    fun getDisplay_slowMappingToDisplay_returnsRegardless() =
        testScope.runTest {
+9 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.view.IWindowManager
import com.android.app.displaylib.DisplayLibBackground
import com.android.app.displaylib.DisplayLibComponent
import com.android.app.displaylib.DisplaysWithDecorationsRepository
import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat
import com.android.app.displaylib.PerDisplayRepository
import com.android.app.displaylib.createDisplayLibComponent
import com.android.systemui.CoreStartable
@@ -155,4 +156,12 @@ object DisplayLibModule {
    ): DisplaysWithDecorationsRepository {
        return displayLibComponent.displaysWithDecorationsRepository
    }

    @Provides
    @SysUISingleton
    fun providesDisplaysWithDecorationsRepositoryCompatFromLib(
        displayLibComponent: DisplayLibComponent
    ): DisplaysWithDecorationsRepositoryCompat {
        return displayLibComponent.displaysWithDecorationsRepositoryCompat
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ interface SystemUIDisplaySubcomponent {

    /**
     * Annotates the displaylib implementation of a class.
     *
     * TODO(b/408503553): Remove this annotation once the flag is cleaned up.
     */
    @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayLib
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.systemui.display.flags

import android.window.DesktopExperienceFlags
import com.android.systemui.flags.RefactorFlagUtils
import com.android.window.flags.Flags

/** Helper for reading or using the enable wm callback for system decor changes flag state. */
object WmCallbackForSysDecorFlag {
    /** The aconfig flag name */
    const val FLAG_NAME = Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM

    /**
     * When true, this is defined as [DesktopExperienceFlags] to make it possible to enable it
     * together with all the other desktop experience flags from the dev settings.
     *
     * Alternatively, using adb:
     * ```bash
     * adb shell setprop persist.wm.debug.desktop_experience_devopts 1
     * ```
     */
    private const val ENABLED_BY_DESKTOP_EXPERIENCE_DEV_OPTION = false

    val FLAG =
        DesktopExperienceFlags.DesktopExperienceFlag(
            Flags::enableSysDecorsCallbacksViaWm,
            /* shouldOverrideByDevOption= */ ENABLED_BY_DESKTOP_EXPERIENCE_DEV_OPTION,
            Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM,
        )

    /** Is the refactor enabled */
    @JvmStatic
    inline val isEnabled: Boolean
        get() = FLAG.isTrue

    /**
     * Called to ensure code is only run when the flag is enabled. This protects users from the
     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
     * build to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun isUnexpectedlyInLegacyMode() =
        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)

    /**
     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
     * the flag is enabled to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
}
+42 −10
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
import static com.android.wm.shell.Flags.enableTaskbarOnPhones;
import static com.android.window.flags.Flags.enableSysDecorsCallbacksViaWm;

import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -41,6 +42,8 @@ import android.window.DesktopExperienceFlags;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.app.displaylib.DisplayDecorationListener;
import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
@@ -49,6 +52,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.LauncherProxyService;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.display.flags.WmCallbackForSysDecorFlag;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.views.NavigationBar;
@@ -67,6 +71,8 @@ import com.android.wm.shell.pip.Pip;

import dalvik.annotation.optimization.NeverCompile;

import kotlinx.coroutines.CoroutineDispatcher;

import java.io.PrintWriter;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -132,7 +138,9 @@ public class NavigationBarControllerImpl implements
            Optional<BackAnimation> backAnimation,
            SecureSettings secureSettings,
            DisplayTracker displayTracker,
            DeviceStateManager deviceStateManager) {
            DeviceStateManager deviceStateManager,
            DisplaysWithDecorationsRepositoryCompat displaysWithDecorationsRepositoryCompat,
            @Main CoroutineDispatcher mainCoroutineDispatcher) {
        mContext = context;
        mExecutor = mainExecutor;
        mNavigationBarComponentFactory = navigationBarComponentFactory;
@@ -140,6 +148,10 @@ public class NavigationBarControllerImpl implements
        mDisplayTracker = displayTracker;
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        commandQueue.addCallback(mCommandQueueCallbacks);
        if (enableSysDecorsCallbacksViaWm()) {
            displaysWithDecorationsRepositoryCompat.registerDisplayDecorationListener(
                    mDisplayDecorationListener, mainCoroutineDispatcher);
        }
        configurationController.addCallback(this);
        mConfigChanges.applyNewConfig(mContext.getResources());
        mNavMode = navigationModeController.addListener(this);
@@ -271,26 +283,23 @@ public class NavigationBarControllerImpl implements
        return mIsLargeScreen || (foldedOrPhone && enableTaskbarNavbarUnification());
    }

    // TODO: b/408503553 - Remove system decor callbacks once the flag is cleaned up.
    private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
        @Override
        public void onDisplayRemoved(int displayId) {
            onDisplayRemoveSystemDecorations(displayId);
            mDisplayDecorationListener.onDisplayRemoveSystemDecorations(displayId);
        }

        @Override
        public void onDisplayRemoveSystemDecorations(int displayId) {
            removeNavigationBar(displayId);
            mHasNavBar.delete(displayId);
            WmCallbackForSysDecorFlag.assertInLegacyMode();
            mDisplayDecorationListener.onDisplayRemoveSystemDecorations(displayId);
        }

        @Override
        public void onDisplayAddSystemDecorations(int displayId) {
            if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
                mHasNavBar.put(displayId, true);
            }
            Display display = mDisplayManager.getDisplay(displayId);
            mIsLargeScreen = isLargeScreen(mContext);
            createNavigationBar(display, null /* savedState */, null /* result */);
            WmCallbackForSysDecorFlag.assertInLegacyMode();
            mDisplayDecorationListener.onDisplayAddSystemDecorations(displayId);
        }

        @Override
@@ -320,6 +329,29 @@ public class NavigationBarControllerImpl implements
        }
    };

    private final DisplayDecorationListener mDisplayDecorationListener =
            new DisplayDecorationListener() {
                @Override
                public void onDisplayRemoved(int displayId) {
                    onDisplayRemoveSystemDecorations(displayId);
                }

                @Override
                public void onDisplayRemoveSystemDecorations(int displayId) {
                    removeNavigationBar(displayId);
                    mHasNavBar.delete(displayId);
                }

                @Override
                public void onDisplayAddSystemDecorations(int displayId) {
                    if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
                        mHasNavBar.put(displayId, true);
                    }
                    Display display = mDisplayManager.getDisplay(displayId);
                    mIsLargeScreen = isLargeScreen(mContext);
                    createNavigationBar(display, null /* savedState */, null /* result */);
                }};

    /**
     * Recreates the navigation bar for the given display.
     */
Loading