Merge "Replace desktop mode screen size check with config property flag" into main
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index a541c59..c68b0be 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -148,4 +148,7 @@
 
     <!-- Whether pointer pilfer is required to start back animation. -->
     <bool name="config_backAnimationRequiresPointerPilfer">true</bool>
+
+    <!-- Whether desktop mode is supported on the current device  -->
+    <bool name="config_isDesktopModeSupported">false</bool>
 </resources>
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 7b84868..494d893 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,13 +16,13 @@
 
 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.content.Context;
 import android.os.SystemProperties;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.window.flags.Flags;
+import com.android.wm.shell.R;
 
 /**
  * Constants for desktop mode feature
@@ -70,8 +70,11 @@
     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);
+    /**
+     * Flag to indicate whether to restrict desktop mode to supported devices.
+     */
+    private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
 
     /**
      * Return {@code true} if desktop windowing is enabled
@@ -113,19 +116,25 @@
     }
 
     /**
-     * Return whether the display size restrictions should be enforced.
+     * Return {@code true} if desktop mode should be restricted to supported devices.
      */
-    public static boolean enforceDisplayRestrictions() {
-        return ENFORCE_DISPLAY_RESTRICTIONS;
+    @VisibleForTesting
+    public static boolean enforceDeviceRestrictions() {
+        return ENFORCE_DEVICE_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.
+     * Return {@code true} if the current device supports desktop mode.
      */
-    public static boolean meetsMinimumDisplayRequirements(@NonNull RunningTaskInfo taskInfo) {
-        return !enforceDisplayRestrictions()
-                || taskInfo.configuration.isLayoutSizeAtLeast(SCREENLAYOUT_SIZE_XLARGE);
+    @VisibleForTesting
+    public static boolean isDesktopModeSupported(@NonNull Context context) {
+        return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
+    }
+
+    /**
+     * Return {@code true} if desktop mode can be entered on the current device.
+     */
+    public static boolean canEnterDesktopMode(@NonNull Context context) {
+        return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
     }
 }
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 c2c9442..95237c3 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
@@ -306,7 +306,7 @@
             task: RunningTaskInfo,
             wct: WindowContainerTransaction = WindowContainerTransaction()
     ) {
-        if (!DesktopModeStatus.meetsMinimumDisplayRequirements(task)) {
+        if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             KtProtoLog.w(
                 WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " +
                         "display does not meet minimum size requirements")
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 bf22193..9c01442 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
@@ -1052,7 +1052,7 @@
                 && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
                 && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
                 && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
-                && DesktopModeStatus.meetsMinimumDisplayRequirements(taskInfo);
+                && DesktopModeStatus.canEnterDesktopMode(mContext);
     }
 
     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 92b187f..0136751 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,8 +23,6 @@
 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
@@ -38,6 +36,7 @@
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
 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.StaticMockitoSession
@@ -89,6 +88,7 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.quality.Strictness
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -126,7 +126,8 @@
 
     @Before
     fun setUp() {
-        mockitoSession = mockitoSession().spyStatic(DesktopModeStatus::class.java).startMocking()
+        mockitoSession = mockitoSession().strictness(Strictness.LENIENT)
+            .spyStatic(DesktopModeStatus::class.java).startMocking()
         whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
 
         shellInit = Mockito.spy(ShellInit(testExecutor))
@@ -335,25 +336,25 @@
     }
 
     @Test
-    fun moveToDesktop_screenSizeBelowXLarge_doesNothing() {
+    fun moveToDesktop_deviceNotSupported_doesNothing() {
         val task = setUpFullscreenTask()
 
-        // Update screen layout to be below minimum size
-        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+        // Simulate non compatible device
+        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
         controller.moveToDesktop(task)
         verifyWCTNotExecuted()
     }
 
     @Test
-    fun moveToDesktop_screenSizeBelowXLarge_displayRestrictionsOverridden_taskIsMovedToDesktop() {
+    fun moveToDesktop_deviceNotSupported_deviceRestrictionsOverridden_taskIsMovedToDesktop() {
         val task = setUpFullscreenTask()
 
-        // Update screen layout to be below minimum size
-        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+        // Simulate non compatible device
+        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-        // Simulate enforce display restrictions system property overridden to false
-        whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false)
+        // Simulate enforce device restrictions system property overridden to false
+        whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false)
 
         controller.moveToDesktop(task)
 
@@ -363,7 +364,7 @@
     }
 
     @Test
-    fun moveToDesktop_screenSizeXLarge_taskIsMovedToDesktop() {
+    fun moveToDesktop_deviceSupported_taskIsMovedToDesktop() {
         val task = setUpFullscreenTask()
 
         controller.moveToDesktop(task)
@@ -874,7 +875,8 @@
 
     private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createFullscreenTask(displayId)
-        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         runningTasks.add(task)
         return task
@@ -882,7 +884,8 @@
 
     private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createSplitScreenTask(displayId)
-        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
         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 83519bb..6940739 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,8 +23,6 @@
 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
@@ -47,6 +45,7 @@
 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.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -367,30 +366,41 @@
 
     @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() {
+    fun testWindowDecor_desktopModeUnsupportedOnDevice_decorNotCreated() {
         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)
+            // Simulate default enforce device restrictions system property
+            whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
             val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
-            // Update screen layout to be below minimum size
-            task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+            // Simulate device that doesn't support desktop mode
+            doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+            onTaskOpening(task)
+            verify(mockDesktopModeWindowDecorFactory, never())
+                .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+        } finally {
+            mockitoSession.finishMocking()
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    fun testWindowDecor_desktopModeUnsupportedOnDevice_deviceRestrictionsOverridden_decorCreated() {
+        val mockitoSession: StaticMockitoSession = mockitoSession()
+            .strictness(Strictness.LENIENT)
+            .spyStatic(DesktopModeStatus::class.java)
+            .startMocking()
+        try {
+            // Simulate enforce device restrictions system property overridden to false
+            whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false)
+            // Simulate device that doesn't support desktop mode
+            doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
             setUpMockDecorationsForTasks(task)
 
             onTaskOpening(task)
@@ -403,14 +413,25 @@
 
     @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)
+    fun testWindowDecor_deviceSupportsDesktopMode_decorCreated() {
+        val mockitoSession: StaticMockitoSession = mockitoSession()
+            .strictness(Strictness.LENIENT)
+            .spyStatic(DesktopModeStatus::class.java)
+            .startMocking()
+        try {
+            // Simulate default enforce device restrictions system property
+            whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
-        onTaskOpening(task)
-        verify(mockDesktopModeWindowDecorFactory)
-            .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+            doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+            setUpMockDecorationsForTasks(task)
+
+            onTaskOpening(task)
+            verify(mockDesktopModeWindowDecorFactory)
+                .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+        } finally {
+            mockitoSession.finishMocking()
+        }
     }
 
     private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {