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

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

Merge "System decoration-based extended mode check" into main

parents ecc36b44 16befe4e
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -1474,6 +1474,7 @@ public abstract class WMShellModule {
            ShellTaskOrganizer shellTaskOrganizer,
            ShellTaskOrganizer shellTaskOrganizer,
            DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
            DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
            InputManager inputManager,
            InputManager inputManager,
            DisplayController displayController,
            @ShellMainThread Handler mainHandler
            @ShellMainThread Handler mainHandler
    ) {
    ) {
        if (!DesktopModeStatus.canEnterDesktopMode(context)) {
        if (!DesktopModeStatus.canEnterDesktopMode(context)) {
@@ -1488,6 +1489,7 @@ public abstract class WMShellModule {
                        shellTaskOrganizer,
                        shellTaskOrganizer,
                        desktopWallpaperActivityTokenProvider,
                        desktopWallpaperActivityTokenProvider,
                        inputManager,
                        inputManager,
                        displayController,
                        mainHandler));
                        mainHandler));
    }
    }


+17 −3
Original line number Original line Diff line number Diff line
@@ -35,9 +35,11 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions


/** Controls the display windowing mode in desktop mode */
/** Controls the display windowing mode in desktop mode */
@@ -49,6 +51,7 @@ class DesktopDisplayModeController(
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
    private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
    private val inputManager: InputManager,
    private val inputManager: InputManager,
    private val displayController: DisplayController,
    @ShellMainThread private val mainHandler: Handler,
    @ShellMainThread private val mainHandler: Handler,
) {
) {


@@ -128,14 +131,25 @@ class DesktopDisplayModeController(
        return windowManager.getWindowingMode(DEFAULT_DISPLAY)
        return windowManager.getWindowingMode(DEFAULT_DISPLAY)
    }
    }


    // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
    private fun isExtendedDisplayEnabled(): Boolean {
    private fun isExtendedDisplayEnabled() =
        if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) {
        0 !=
            return rootTaskDisplayAreaOrganizer
                .getDisplayIds()
                .filter { it != DEFAULT_DISPLAY }
                .any { displayId ->
                    displayController.getDisplay(displayId)?.let { display ->
                        DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
                    } ?: false
                }
        }

        return 0 !=
            Settings.Global.getInt(
            Settings.Global.getInt(
                context.contentResolver,
                context.contentResolver,
                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                0,
                0,
            )
            )
    }


    private fun hasExternalDisplay() =
    private fun hasExternalDisplay() =
        rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
        rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
+98 −69
Original line number Original line Diff line number Diff line
@@ -26,28 +26,37 @@ import android.os.Binder
import android.os.Handler
import android.os.Handler
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.UsesFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.server.display.feature.flags.Flags as DisplayFlags
import com.android.window.flags.Flags
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
import org.junit.After
import org.junit.Before
import org.junit.Before
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
@@ -60,6 +69,7 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness


/**
/**
 * Test class for [DesktopDisplayModeController]
 * Test class for [DesktopDisplayModeController]
@@ -68,6 +78,7 @@ import org.mockito.kotlin.whenever
 */
 */
@SmallTest
@SmallTest
@RunWith(TestParameterInjector::class)
@RunWith(TestParameterInjector::class)
@UsesFlags(com.android.server.display.feature.flags.Flags::class)
class DesktopDisplayModeControllerTest(
class DesktopDisplayModeControllerTest(
    @TestParameter(valuesProvider = FlagsParameterizationProvider::class)
    @TestParameter(valuesProvider = FlagsParameterizationProvider::class)
    flags: FlagsParameterization
    flags: FlagsParameterization
@@ -79,6 +90,7 @@ class DesktopDisplayModeControllerTest(
    private val desktopWallpaperActivityTokenProvider =
    private val desktopWallpaperActivityTokenProvider =
        mock<DesktopWallpaperActivityTokenProvider>()
        mock<DesktopWallpaperActivityTokenProvider>()
    private val inputManager = mock<InputManager>()
    private val inputManager = mock<InputManager>()
    private val displayController = mock<DisplayController>()
    private val mainHandler = mock<Handler>()
    private val mainHandler = mock<Handler>()


    private lateinit var controller: DesktopDisplayModeController
    private lateinit var controller: DesktopDisplayModeController
@@ -90,6 +102,12 @@ class DesktopDisplayModeControllerTest(
        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
    private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
    private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
    private val wallpaperToken = MockToken().token()
    private val wallpaperToken = MockToken().token()
    private val externalDisplay = mock<Display>()

    private lateinit var extendedDisplaySettingsRestoreSession:
        ExtendedDisplaySettingsRestoreSession

    private lateinit var mockitoSession: StaticMockitoSession


    init {
    init {
        mSetFlagsRule.setFlagsParameterization(flags)
        mSetFlagsRule.setFlagsParameterization(flags)
@@ -97,6 +115,13 @@ class DesktopDisplayModeControllerTest(


    @Before
    @Before
    fun setUp() {
    fun setUp() {
        mockitoSession =
            mockitoSession()
                .strictness(Strictness.LENIENT)
                .spyStatic(DesktopModeStatus::class.java)
                .startMocking()
        extendedDisplaySettingsRestoreSession =
            ExtendedDisplaySettingsRestoreSession(context.contentResolver)
        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
            .thenReturn(defaultTDA)
            .thenReturn(defaultTDA)
@@ -109,19 +134,28 @@ class DesktopDisplayModeControllerTest(
                shellTaskOrganizer,
                shellTaskOrganizer,
                desktopWallpaperActivityTokenProvider,
                desktopWallpaperActivityTokenProvider,
                inputManager,
                inputManager,
                displayController,
                mainHandler,
                mainHandler,
            )
            )
        runningTasks.add(freeformTask)
        runningTasks.add(freeformTask)
        runningTasks.add(fullscreenTask)
        runningTasks.add(fullscreenTask)
        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
        whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
        setTabletModeStatus(SwitchState.UNKNOWN)
        setTabletModeStatus(SwitchState.UNKNOWN)
    }
    }


    @After
    fun tearDown() {
        extendedDisplaySettingsRestoreSession.restore()
        mockitoSession.finishMocking()
    }

    private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
    private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
        setExtendedMode(true)

        connectExternalDisplay()
        connectExternalDisplay()
        if (expectToSwitch) {
        if (expectToSwitch) {
            // Assumes [connectExternalDisplay] properly triggered the switching transition.
            // Assumes [connectExternalDisplay] properly triggered the switching transition.
@@ -146,7 +180,6 @@ class DesktopDisplayModeControllerTest(
            verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
            verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
        }
        }
    }
    }
    }


    @Test
    @Test
    @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
    @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
@@ -176,16 +209,11 @@ class DesktopDisplayModeControllerTest(
            disconnectExternalDisplay()
            disconnectExternalDisplay()
        }
        }
        setTabletModeStatus(tabletModeStatus)
        setTabletModeStatus(tabletModeStatus)
        setExtendedMode(param.extendedDisplayEnabled)


        ExtendedDisplaySettingsSession(
                context.contentResolver,
                if (param.extendedDisplayEnabled) 1 else 0,
            )
            .use {
        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
            .isEqualTo(param.expectedWindowingMode)
            .isEqualTo(param.expectedWindowingMode)
    }
    }
    }


    @Test
    @Test
    @EnableFlags(
    @EnableFlags(
@@ -199,35 +227,28 @@ class DesktopDisplayModeControllerTest(
            disconnectExternalDisplay()
            disconnectExternalDisplay()
        }
        }
        setTabletModeStatus(param.tabletModeStatus)
        setTabletModeStatus(param.tabletModeStatus)
        setExtendedMode(param.extendedDisplayEnabled)


        ExtendedDisplaySettingsSession(
                context.contentResolver,
                if (param.extendedDisplayEnabled) 1 else 0,
            )
            .use {
        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
            .isEqualTo(param.expectedWindowingMode)
            .isEqualTo(param.expectedWindowingMode)
    }
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
    fun displayWindowingModeSwitch_existingTasksOnConnected() {
    fun displayWindowingModeSwitch_existingTasksOnConnected() {
        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
        setExtendedMode(true)


        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
        connectExternalDisplay()
        connectExternalDisplay()


        val arg = argumentCaptor<WindowContainerTransaction>()
        val arg = argumentCaptor<WindowContainerTransaction>()
            verify(transitions, times(1))
        verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_UNDEFINED)
            .isEqualTo(WINDOWING_MODE_UNDEFINED)
        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
    }
    }
    }


    @Test
    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
@@ -236,19 +257,17 @@ class DesktopDisplayModeControllerTest(
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
            WINDOWING_MODE_FULLSCREEN
            WINDOWING_MODE_FULLSCREEN
        }
        }
        setExtendedMode(true)


        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
        disconnectExternalDisplay()
        disconnectExternalDisplay()


        val arg = argumentCaptor<WindowContainerTransaction>()
        val arg = argumentCaptor<WindowContainerTransaction>()
            verify(transitions, times(1))
        verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
            .isEqualTo(WINDOWING_MODE_UNDEFINED)
            .isEqualTo(WINDOWING_MODE_UNDEFINED)
    }
    }
    }


    private fun connectExternalDisplay() {
    private fun connectExternalDisplay() {
        whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
        whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
@@ -266,18 +285,27 @@ class DesktopDisplayModeControllerTest(
        whenever(inputManager.isInTabletMode()).thenReturn(status.value)
        whenever(inputManager.isInTabletMode()).thenReturn(status.value)
    }
    }


    private class ExtendedDisplaySettingsSession(
    private fun setExtendedMode(enabled: Boolean) {
        private val contentResolver: ContentResolver,
        if (DisplayFlags.enableDisplayContentModeManagement()) {
        private val overrideValue: Int,
            doReturn(enabled).`when` {
    ) : AutoCloseable {
                DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay)
            }
        } else {
            Settings.Global.putInt(
                context.contentResolver,
                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                if (enabled) 1 else 0,
            )
        }
    }

    private class ExtendedDisplaySettingsRestoreSession(
        private val contentResolver: ContentResolver
    ) {
        private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
        private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
        private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
        private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)


        init {
        fun restore() {
            Settings.Global.putInt(contentResolver, settingName, overrideValue)
        }

        override fun close() {
            Settings.Global.putInt(contentResolver, settingName, initialValue)
            Settings.Global.putInt(contentResolver, settingName, initialValue)
        }
        }
    }
    }
@@ -287,7 +315,8 @@ class DesktopDisplayModeControllerTest(
            context: TestParameterValuesProvider.Context
            context: TestParameterValuesProvider.Context
        ): List<FlagsParameterization> {
        ): List<FlagsParameterization> {
            return FlagsParameterization.allCombinationsOf(
            return FlagsParameterization.allCombinationsOf(
                Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH
                Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
                DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
            )
            )
        }
        }
    }
    }