Loading core/java/com/android/internal/policy/DesktopModeCompatPolicy.java +20 −0 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import android.window.DesktopModeFlags; import com.android.internal.R; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; Loading @@ -41,6 +43,8 @@ public class DesktopModeCompatPolicy { private final Context mContext; @NonNull private final String mSystemUiPackage; @NonNull private final List<String> mConfigExemptPackages; private final Map<String, Boolean> mPackageInfoCache = new HashMap<>(); private PackageManager mPackageManager = null; Loading @@ -49,6 +53,8 @@ public class DesktopModeCompatPolicy { public DesktopModeCompatPolicy(@NonNull Context context) { mContext = context; mSystemUiPackage = context.getResources().getString(R.string.config_systemUi); mConfigExemptPackages = Arrays.asList(context.getResources().getStringArray( R.array.config_desktopExemptPackages)); } public void setDefaultHomePackageSupplier( Loading Loading @@ -113,6 +119,11 @@ public class DesktopModeCompatPolicy { if (isTopActivityNoDisplay) { return false; } // TODO: b/434943016 - Replace with permission. // If activity belongs to package exempt via device config, force out of desktop. if (isPackageExemptViaConfig(packageName) && !isActivityStackTransparent) { return true; } // If activity belongs to system ui package, safe to force out of desktop. if (isSystemUiTask(packageName)) { return true; Loading Loading @@ -162,6 +173,11 @@ public class DesktopModeCompatPolicy { if (isPartOfDefaultHomePackageOrNoHomeAvailable(packageName)) { return true; } // TODO: b/434943016 - Replace with permission. // If activity belongs to package exempt via device config, hide desktop entry point. if (isPackageExemptViaConfig(packageName)) { return true; } // If all activities in task stack are transparent AND package has the relevant fullscreen // transparent permission, safe to force out of desktop. return isTransparentTask(isActivityStackTransparent, numActivities); Loading Loading @@ -197,6 +213,10 @@ public class DesktopModeCompatPolicy { return Objects.equals(packageName, mSystemUiPackage); } private boolean isPackageExemptViaConfig(@Nullable String packageName) { return mConfigExemptPackages.contains(packageName); } // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission. private boolean hasFullscreenTransparentPermission(@NonNull String packageName, int userId) { if (!DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue()) { Loading core/res/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -7522,6 +7522,9 @@ <!-- Whether the home screen should be shown behind freeform tasks in desktop mode. --> <bool name="config_showHomeBehindDesktop">false</bool> <!-- List of packages which should be exempt from desktop mode. --> <string-array name="config_desktopExemptPackages" translatable="false"/> <!-- Frame rate compatibility value for Wallpaper FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption --> <integer name="config_wallpaperFrameRateCompatibility">102</integer> Loading core/res/res/values/symbols.xml +3 −0 Original line number Diff line number Diff line Loading @@ -6099,6 +6099,9 @@ <!-- Whether the home screen should be shown behind freeform tasks in desktop mode. --> <java-symbol type="bool" name="config_showHomeBehindDesktop" /> <!-- List of packages which should be exempt from desktop mode. --> <java-symbol type="array" name="config_desktopExemptPackages" /> <!-- Frame rate compatibility value for Wallpaper --> <java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" /> Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt +46 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.os.Process import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableContext import androidx.test.filters.SmallTest import com.android.internal.R import com.android.internal.policy.DesktopModeCompatPolicy Loading @@ -47,8 +48,10 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever Loading @@ -62,16 +65,24 @@ import org.mockito.kotlin.whenever @SmallTest class DesktopModeCompatPolicyTest : ShellTestCase() { @get:Rule val compatRule = PlatformCompatChangeRule() private lateinit var mockContext: TestableContext private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy private val packageManager: PackageManager = mock() private val homeActivities = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "") private val baseActivityTest = ComponentName("com.test.dummypackage", "TestClass") private val configExemptActivity = ComponentName("com.test.configExemptPackage", /* class */ "") private val configExemptPackageList = arrayOf(configExemptActivity.packageName) @Before fun setUp() { desktopModeCompatPolicy = DesktopModeCompatPolicy(mContext) mockContext = spy(mContext) val resources = spy(mockContext.resources) doReturn(configExemptPackageList).`when`(resources) .getStringArray(R.array.config_desktopExemptPackages) doReturn(resources).`when`(mockContext).resources desktopModeCompatPolicy = DesktopModeCompatPolicy(mockContext) whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities) mContext.setMockPackageManager(packageManager) mockContext.setMockPackageManager(packageManager) } @Test Loading Loading @@ -277,7 +288,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { @Test fun testIsTopActivityExemptFromDesktopWindowing_defaultHomePackage_notYetAvailable() { val emptyHomeActivities: ComponentName = mock() mContext.setMockPackageManager(packageManager) mockContext.setMockPackageManager(packageManager) whenever(emptyHomeActivities.packageName).thenReturn(null) whenever(packageManager.getHomeActivities(any())).thenReturn(emptyHomeActivities) Loading @@ -290,6 +301,27 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { })) } @Test fun testIsTopActivityExemptFromDesktopWindowing_packageInConfigExemptionList() { assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing( createFreeformTask(/* displayId */ 0) .apply { baseActivity = configExemptActivity isTopActivityNoDisplay = false })) } @Test fun testIsTopActivityExemptFromDesktopWindowing_packageInConfigExemptionList_transparentTask() { assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing( createFreeformTask(/* displayId */ 0) .apply { baseActivity = configExemptActivity isTopActivityNoDisplay = false isActivityStackTransparent = true })) } @Test fun testShouldDisableDesktopEntryPoints_noDisplayActivity() { assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints( Loading Loading @@ -321,7 +353,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { @Test fun testShouldDisableDesktopEntryPoints_defaultHomePackage_notYetAvailable() { val emptyHomeActivities: ComponentName = mock() mContext.setMockPackageManager(packageManager) mockContext.setMockPackageManager(packageManager) whenever(emptyHomeActivities.packageName).thenReturn(null) whenever(packageManager.getHomeActivities(any())).thenReturn(emptyHomeActivities) Loading @@ -341,6 +373,15 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { })) } @Test fun testShouldDisableDesktopEntryPoints_packageInConfigExemptionList() { assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints( createFreeformTask(/* displayId */ 0) .apply { baseActivity = configExemptActivity })) } @Test @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS) @DisableCompatChanges(ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED) Loading Loading @@ -383,7 +424,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { createFreeformTask().apply { val componentName = ComponentName.createRelative( mContext, mockContext, DesktopModeCompatPolicyTest::class.java.simpleName ) baseActivity = componentName Loading Loading
core/java/com/android/internal/policy/DesktopModeCompatPolicy.java +20 −0 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import android.window.DesktopModeFlags; import com.android.internal.R; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; Loading @@ -41,6 +43,8 @@ public class DesktopModeCompatPolicy { private final Context mContext; @NonNull private final String mSystemUiPackage; @NonNull private final List<String> mConfigExemptPackages; private final Map<String, Boolean> mPackageInfoCache = new HashMap<>(); private PackageManager mPackageManager = null; Loading @@ -49,6 +53,8 @@ public class DesktopModeCompatPolicy { public DesktopModeCompatPolicy(@NonNull Context context) { mContext = context; mSystemUiPackage = context.getResources().getString(R.string.config_systemUi); mConfigExemptPackages = Arrays.asList(context.getResources().getStringArray( R.array.config_desktopExemptPackages)); } public void setDefaultHomePackageSupplier( Loading Loading @@ -113,6 +119,11 @@ public class DesktopModeCompatPolicy { if (isTopActivityNoDisplay) { return false; } // TODO: b/434943016 - Replace with permission. // If activity belongs to package exempt via device config, force out of desktop. if (isPackageExemptViaConfig(packageName) && !isActivityStackTransparent) { return true; } // If activity belongs to system ui package, safe to force out of desktop. if (isSystemUiTask(packageName)) { return true; Loading Loading @@ -162,6 +173,11 @@ public class DesktopModeCompatPolicy { if (isPartOfDefaultHomePackageOrNoHomeAvailable(packageName)) { return true; } // TODO: b/434943016 - Replace with permission. // If activity belongs to package exempt via device config, hide desktop entry point. if (isPackageExemptViaConfig(packageName)) { return true; } // If all activities in task stack are transparent AND package has the relevant fullscreen // transparent permission, safe to force out of desktop. return isTransparentTask(isActivityStackTransparent, numActivities); Loading Loading @@ -197,6 +213,10 @@ public class DesktopModeCompatPolicy { return Objects.equals(packageName, mSystemUiPackage); } private boolean isPackageExemptViaConfig(@Nullable String packageName) { return mConfigExemptPackages.contains(packageName); } // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission. private boolean hasFullscreenTransparentPermission(@NonNull String packageName, int userId) { if (!DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue()) { Loading
core/res/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -7522,6 +7522,9 @@ <!-- Whether the home screen should be shown behind freeform tasks in desktop mode. --> <bool name="config_showHomeBehindDesktop">false</bool> <!-- List of packages which should be exempt from desktop mode. --> <string-array name="config_desktopExemptPackages" translatable="false"/> <!-- Frame rate compatibility value for Wallpaper FRAME_RATE_COMPATIBILITY_MIN (102) is used by default for lower power consumption --> <integer name="config_wallpaperFrameRateCompatibility">102</integer> Loading
core/res/res/values/symbols.xml +3 −0 Original line number Diff line number Diff line Loading @@ -6099,6 +6099,9 @@ <!-- Whether the home screen should be shown behind freeform tasks in desktop mode. --> <java-symbol type="bool" name="config_showHomeBehindDesktop" /> <!-- List of packages which should be exempt from desktop mode. --> <java-symbol type="array" name="config_desktopExemptPackages" /> <!-- Frame rate compatibility value for Wallpaper --> <java-symbol type="integer" name="config_wallpaperFrameRateCompatibility" /> Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt +46 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.os.Process import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableContext import androidx.test.filters.SmallTest import com.android.internal.R import com.android.internal.policy.DesktopModeCompatPolicy Loading @@ -47,8 +48,10 @@ import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever Loading @@ -62,16 +65,24 @@ import org.mockito.kotlin.whenever @SmallTest class DesktopModeCompatPolicyTest : ShellTestCase() { @get:Rule val compatRule = PlatformCompatChangeRule() private lateinit var mockContext: TestableContext private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy private val packageManager: PackageManager = mock() private val homeActivities = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "") private val baseActivityTest = ComponentName("com.test.dummypackage", "TestClass") private val configExemptActivity = ComponentName("com.test.configExemptPackage", /* class */ "") private val configExemptPackageList = arrayOf(configExemptActivity.packageName) @Before fun setUp() { desktopModeCompatPolicy = DesktopModeCompatPolicy(mContext) mockContext = spy(mContext) val resources = spy(mockContext.resources) doReturn(configExemptPackageList).`when`(resources) .getStringArray(R.array.config_desktopExemptPackages) doReturn(resources).`when`(mockContext).resources desktopModeCompatPolicy = DesktopModeCompatPolicy(mockContext) whenever(packageManager.getHomeActivities(any())).thenReturn(homeActivities) mContext.setMockPackageManager(packageManager) mockContext.setMockPackageManager(packageManager) } @Test Loading Loading @@ -277,7 +288,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { @Test fun testIsTopActivityExemptFromDesktopWindowing_defaultHomePackage_notYetAvailable() { val emptyHomeActivities: ComponentName = mock() mContext.setMockPackageManager(packageManager) mockContext.setMockPackageManager(packageManager) whenever(emptyHomeActivities.packageName).thenReturn(null) whenever(packageManager.getHomeActivities(any())).thenReturn(emptyHomeActivities) Loading @@ -290,6 +301,27 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { })) } @Test fun testIsTopActivityExemptFromDesktopWindowing_packageInConfigExemptionList() { assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing( createFreeformTask(/* displayId */ 0) .apply { baseActivity = configExemptActivity isTopActivityNoDisplay = false })) } @Test fun testIsTopActivityExemptFromDesktopWindowing_packageInConfigExemptionList_transparentTask() { assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing( createFreeformTask(/* displayId */ 0) .apply { baseActivity = configExemptActivity isTopActivityNoDisplay = false isActivityStackTransparent = true })) } @Test fun testShouldDisableDesktopEntryPoints_noDisplayActivity() { assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints( Loading Loading @@ -321,7 +353,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { @Test fun testShouldDisableDesktopEntryPoints_defaultHomePackage_notYetAvailable() { val emptyHomeActivities: ComponentName = mock() mContext.setMockPackageManager(packageManager) mockContext.setMockPackageManager(packageManager) whenever(emptyHomeActivities.packageName).thenReturn(null) whenever(packageManager.getHomeActivities(any())).thenReturn(emptyHomeActivities) Loading @@ -341,6 +373,15 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { })) } @Test fun testShouldDisableDesktopEntryPoints_packageInConfigExemptionList() { assertTrue(desktopModeCompatPolicy.shouldDisableDesktopEntryPoints( createFreeformTask(/* displayId */ 0) .apply { baseActivity = configExemptActivity })) } @Test @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS) @DisableCompatChanges(ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED) Loading Loading @@ -383,7 +424,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() { createFreeformTask().apply { val componentName = ComponentName.createRelative( mContext, mockContext, DesktopModeCompatPolicyTest::class.java.simpleName ) baseActivity = componentName Loading