Disable Desktop Windowing for devices with smaller screen sizes
For devices with screen size smaller than
`Configuration.SCREENLAYOUT_SIZE_XLARGE`, hide the desktop windowing app
handle and ignore `DesktopTaskController.moveToDesktop` calls. This
hides all the on screen entry points and disables the adb commands and
shortcuts to enter Desktop Windowing.
Fixes: 326957646
Test: atest WMShellUnitTests:DesktopModeWindowDecorViewModelTests
Test: atest WMShellUnitTests:DesktopTasksControllerTest
Change-Id: I6f3224052c7fd88beefb9e58b0009569376d3a62
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 22ba708..7b84868 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.desktopmode;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager.RunningTaskInfo;
import android.os.SystemProperties;
import com.android.window.flags.Flags;
@@ -66,6 +70,9 @@
private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean(
"persist.wm.debug.desktop_use_rounded_corners", true);
+ private static final boolean ENFORCE_DISPLAY_RESTRICTIONS = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode_enforce_display_restrictions", true);
+
/**
* Return {@code true} if desktop windowing is enabled
*/
@@ -104,4 +111,21 @@
public static boolean useRoundedCorners() {
return USE_ROUNDED_CORNERS;
}
+
+ /**
+ * Return whether the display size restrictions should be enforced.
+ */
+ public static boolean enforceDisplayRestrictions() {
+ return ENFORCE_DISPLAY_RESTRICTIONS;
+ }
+
+ /**
+ * Return {@code true} if the display associated with the task is at least of size
+ * {@link android.content.res.Configuration#SCREENLAYOUT_SIZE_XLARGE} or has been overridden to
+ * ignore the size constraint.
+ */
+ public static boolean meetsMinimumDisplayRequirements(@NonNull RunningTaskInfo taskInfo) {
+ return !enforceDisplayRestrictions()
+ || taskInfo.configuration.isLayoutSizeAtLeast(SCREENLAYOUT_SIZE_XLARGE);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 654409f..2c66fd6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -305,6 +305,12 @@
task: RunningTaskInfo,
wct: WindowContainerTransaction = WindowContainerTransaction()
) {
+ if (!DesktopModeStatus.meetsMinimumDisplayRequirements(task)) {
+ KtProtoLog.w(
+ WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " +
+ "display does not meet minimum size requirements")
+ return
+ }
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: moveToDesktop taskId=%d",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index f4ccd68..dc9b532 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -1058,8 +1058,7 @@
&& taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
&& taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
- && mDisplayController.getDisplayContext(taskInfo.displayId)
- .getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ && DesktopModeStatus.meetsMinimumDisplayRequirements(taskInfo);
}
private void createWindowDecoration(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 35c803b..4c8a308 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -23,6 +23,8 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE
import android.os.Binder
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
@@ -123,7 +125,7 @@
@Before
fun setUp() {
- mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking()
+ mockitoSession = mockitoSession().spyStatic(DesktopModeStatus::class.java).startMocking()
whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
shellInit = Mockito.spy(ShellInit(testExecutor))
@@ -332,6 +334,45 @@
}
@Test
+ fun moveToDesktop_screenSizeBelowXLarge_doesNothing() {
+ val task = setUpFullscreenTask()
+
+ // Update screen layout to be below minimum size
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+
+ controller.moveToDesktop(task)
+ verifyWCTNotExecuted()
+ }
+
+ @Test
+ fun moveToDesktop_screenSizeBelowXLarge_displayRestrictionsOverridden_taskIsMovedToDesktop() {
+ val task = setUpFullscreenTask()
+
+ // Update screen layout to be below minimum size
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+
+ // Simulate enforce display restrictions system property overridden to false
+ whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false)
+
+ controller.moveToDesktop(task)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun moveToDesktop_screenSizeXLarge_taskIsMovedToDesktop() {
+ val task = setUpFullscreenTask()
+
+ controller.moveToDesktop(task)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
fun moveToDesktop_otherFreeformTasksBroughtToFront() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
@@ -816,6 +857,7 @@
private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFullscreenTask(displayId)
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
@@ -823,6 +865,7 @@
private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createSplitScreenTask(displayId)
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 9bb5482..83519bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -23,10 +23,14 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.content.Context
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE
import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.os.Handler
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.SparseArray
@@ -42,6 +46,9 @@
import android.view.WindowInsets.Type.navigationBars
import android.view.WindowInsets.Type.statusBars
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
@@ -51,6 +58,7 @@
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeStatus
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellCommandHandler
@@ -59,6 +67,7 @@
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -71,6 +80,7 @@
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
import java.util.Optional
import java.util.function.Supplier
import org.mockito.Mockito
@@ -82,6 +92,10 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule()
+
@Mock private lateinit var mockDesktopModeWindowDecorFactory:
DesktopModeWindowDecoration.Factory
@Mock private lateinit var mockMainHandler: Handler
@@ -351,6 +365,54 @@
inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun testWindowDecor_screenSizeBelowXLarge_decorNotCreated() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+ // Update screen layout to be below minimum size
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+
+ onTaskOpening(task)
+ verify(mockDesktopModeWindowDecorFactory, never())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun testWindowDecor_screenSizeBelowXLarge_displayRestrictionsOverridden_decorCreated() {
+ val mockitoSession: StaticMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ try {
+ // Simulate enforce display restrictions system property overridden to false
+ whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false)
+
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+ // Update screen layout to be below minimum size
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+ setUpMockDecorationsForTasks(task)
+
+ onTaskOpening(task)
+ verify(mockDesktopModeWindowDecorFactory)
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ } finally {
+ mockitoSession.finishMocking()
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun testWindowDecor_screenSizeXLarge_decorCreated() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+ task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
+ setUpMockDecorationsForTasks(task)
+
+ onTaskOpening(task)
+ verify(mockDesktopModeWindowDecorFactory)
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ }
+
private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
desktopModeWindowDecorViewModel.onTaskOpening(
task,