summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeHelper.java8
-rw-r--r--services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java173
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java459
4 files changed, 638 insertions, 6 deletions
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index 1f341147deb1..e0c0c2c60123 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -22,7 +22,7 @@ import android.os.SystemProperties;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
+import com.android.server.wm.utils.DesktopModeFlagsUtil;
/**
* Constants for desktop mode feature
@@ -35,8 +35,8 @@ public final class DesktopModeHelper {
"persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
/** Whether desktop mode is enabled. */
- static boolean isDesktopModeEnabled() {
- return Flags.enableDesktopWindowingMode();
+ static boolean isDesktopModeEnabled(@NonNull Context context) {
+ return DesktopModeFlagsUtil.DESKTOP_WINDOWING_MODE.isEnabled(context);
}
/**
@@ -60,7 +60,7 @@ public final class DesktopModeHelper {
* Return {@code true} if desktop mode can be entered on the current device.
*/
static boolean canEnterDesktopMode(@NonNull Context context) {
- return isDesktopModeEnabled()
+ return isDesktopModeEnabled(context)
&& (!shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context));
}
}
diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
new file mode 100644
index 000000000000..4211764085b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 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.server.wm.utils;
+
+import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.window.flags.Flags;
+
+import java.util.function.Supplier;
+
+/**
+ * Util to check desktop mode flags state.
+ *
+ * This utility is used to allow developer option toggles to override flags related to desktop
+ * windowing.
+ *
+ * Computes whether Desktop Windowing related flags should be enabled by using the aconfig flag
+ * value and the developer option override state (if applicable).
+ *
+ * This is a partial copy of {@link com.android.wm.shell.shared.desktopmode.DesktopModeFlags} which
+ * is to be used in WM core.
+ */
+public enum DesktopModeFlagsUtil {
+ // All desktop mode related flags to be overridden by developer option toggle will be added here
+ DESKTOP_WINDOWING_MODE(
+ Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true),
+ WALLPAPER_ACTIVITY(
+ Flags::enableDesktopWindowingWallpaperActivity, /* shouldOverrideByDevOption= */ true);
+
+ private static final String TAG = "DesktopModeFlagsUtil";
+ private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
+ "sys.wmshell.desktopmode.dev_toggle_override";
+
+ // Function called to obtain aconfig flag value.
+ private final Supplier<Boolean> mFlagFunction;
+ // Whether the flag state should be affected by developer option.
+ private final boolean mShouldOverrideByDevOption;
+
+ // Local cache for toggle override, which is initialized once on its first access. It needs to
+ // be refreshed only on reboots as overridden state takes effect on reboots.
+ private static ToggleOverride sCachedToggleOverride;
+
+ DesktopModeFlagsUtil(Supplier<Boolean> flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop mode developer option
+ * overrides.
+ *
+ * Note: this method makes sure that a constant developer toggle overrides is read until
+ * reboot.
+ */
+ public boolean isEnabled(Context context) {
+ if (!Flags.showDesktopWindowingDevOption()
+ || !mShouldOverrideByDevOption
+ || context.getContentResolver() == null) {
+ return mFlagFunction.get();
+ } else {
+ boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode();
+ return switch (getToggleOverride(context)) {
+ case OVERRIDE_UNSET -> mFlagFunction.get();
+ // When toggle override matches its default state, don't override flags. This
+ // helps users reset their feature overrides.
+ case OVERRIDE_OFF -> !shouldToggleBeEnabledByDefault && mFlagFunction.get();
+ case OVERRIDE_ON -> shouldToggleBeEnabledByDefault ? mFlagFunction.get() : true;
+ };
+ }
+ }
+
+ private ToggleOverride getToggleOverride(Context context) {
+ // If cached, return it
+ if (sCachedToggleOverride != null) {
+ return sCachedToggleOverride;
+ }
+
+ // Otherwise, fetch and cache it
+ ToggleOverride override = getToggleOverrideFromSystem(context);
+ sCachedToggleOverride = override;
+ Log.d(TAG, "Toggle override initialized to: " + override);
+ return override;
+ }
+
+ /**
+ * Returns {@link ToggleOverride} from a non-persistent system property if present. Otherwise
+ * initializes the system property by reading Settings.Global.
+ */
+ private ToggleOverride getToggleOverrideFromSystem(Context context) {
+ // A non-persistent System Property is used to store override to ensure it remains
+ // constant till reboot.
+ String overrideProperty = System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null);
+ ToggleOverride overrideFromSystemProperties = convertToToggleOverride(overrideProperty);
+
+ // If valid system property, return it
+ if (overrideFromSystemProperties != null) {
+ return overrideFromSystemProperties;
+ }
+
+ // Fallback when System Property is not present (just after reboot) or not valid (user
+ // manually changed the value): Read from Settings.Global
+ int settingValue = Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ OVERRIDE_UNSET.getSetting()
+ );
+ ToggleOverride overrideFromSettingsGlobal =
+ ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
+ // Initialize System Property
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(settingValue));
+ return overrideFromSettingsGlobal;
+ }
+
+ /**
+ * Converts {@code intString} into {@link ToggleOverride}. Return {@code null} if
+ * {@code intString} does not correspond to a {@link ToggleOverride}.
+ */
+ private static @Nullable ToggleOverride convertToToggleOverride(
+ @Nullable String intString
+ ) {
+ if (intString == null) return null;
+ try {
+ int intValue = Integer.parseInt(intString);
+ return ToggleOverride.fromSetting(intValue, null);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Unknown toggleOverride int " + intString);
+ return null;
+ }
+ }
+
+ /** Override state of desktop mode developer option toggle. */
+ enum ToggleOverride {
+ OVERRIDE_UNSET,
+ OVERRIDE_OFF,
+ OVERRIDE_ON;
+
+ int getSetting() {
+ return switch (this) {
+ case OVERRIDE_ON -> 1;
+ case OVERRIDE_OFF -> 0;
+ case OVERRIDE_UNSET -> -1;
+ };
+ }
+
+ static ToggleOverride fromSetting(int setting, @Nullable ToggleOverride fallback) {
+ return switch (setting) {
+ case 1 -> OVERRIDE_ON;
+ case 0 -> OVERRIDE_OFF;
+ case -1 -> OVERRIDE_UNSET;
+ default -> fallback;
+ };
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 41f1ac72c56d..ea2abf7ddcb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -141,8 +141,8 @@ import java.util.HashMap;
import java.util.List;
/** Common base class for window manager unit test classes. */
-class WindowTestsBase extends SystemServiceTestsBase {
- final Context mContext = getInstrumentation().getTargetContext();
+public class WindowTestsBase extends SystemServiceTestsBase {
+ protected final Context mContext = getInstrumentation().getTargetContext();
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
new file mode 100644
index 000000000000..e5f2f89ccead
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2024 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.server.wm.utils;
+
+import static com.android.server.wm.utils.DesktopModeFlagsUtil.DESKTOP_WINDOWING_MODE;
+import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_OFF;
+import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_ON;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.WindowTestRunner;
+import com.android.server.wm.WindowTestsBase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+/**
+ * Test class for [DesktopModeFlagsUtil]
+ *
+ * Build/Install/Run:
+ * atest WmTests:DesktopModeFlagsUtilTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DesktopModeFlagsUtilTest extends WindowTestsBase {
+
+ @Rule
+ public SetFlagsRule setFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setUp() throws Exception {
+ resetCache();
+ }
+
+ private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
+ "sys.wmshell.desktopmode.dev_toggle_override";
+
+ @Test
+ @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
+ setOverride(OVERRIDE_OFF.getSetting());
+ // In absence of dev options, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ }
+
+
+ @Test
+ @DisableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_overrideUnset_featureFlagOn_returnsTrue() {
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ // For overridableFlag, for unset overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_overrideUnset_featureFlagOff_returnsFalse() {
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ // For overridableFlag, for unset overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_noOverride_featureFlagOn_returnsTrue() {
+ setOverride(null);
+
+ // For overridableFlag, in absence of overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_noOverride_featureFlagOff_returnsFalse() {
+ setOverride(null);
+
+ // For overridableFlag, in absence of overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() {
+ setOverride(-2);
+
+ // For overridableFlag, for unrecognized overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() {
+ setOverride(-2);
+
+ // For overridableFlag, for unrecognizable overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_overrideOff_featureFlagOn_returnsFalse() {
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_overrideOn_featureFlagOff_returnsTrue() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // Keep overrides constant through the process
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // Keep overrides constant through the process
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_noProperty_overrideOn_featureFlagOff_returnsTrueAndPropertyOn() {
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
+ setOverride(OVERRIDE_ON.getSetting());
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ // Store System Property if not present
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_noProperty_overrideUnset_featureFlagOn_returnsTrueAndPropertyUnset() {
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ // Store System Property if not present
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(
+ DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_noProperty_overrideUnset_featureFlagOff_returnsFalseAndPropertyUnset() {
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ // Store System Property if not present
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(
+ DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_propertyNotInt_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc");
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ // Store System Property if currently invalid
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_propertyInvalid_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2");
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ // Store System Property if currently invalid
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_propertyOff_overrideOn_featureFlagOn_returnsFalseAndnoPropertyUpdate() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(
+ OVERRIDE_OFF.getSetting()));
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // Have a consistent override until reboot
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_propertyOn_overrideOff_featureFlagOff_returnsTrueAndnoPropertyUpdate() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(OVERRIDE_ON.getSetting()));
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // Have a consistent override until reboot
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ public void isEnabled_propertyUnset_overrideOff_featureFlagOn_returnsTrueAndnoPropertyUpdate() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY,
+ String.valueOf(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // Have a consistent override until reboot
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(String.valueOf(
+ DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY})
+ public void isEnabled_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ // For unset overrides, follow flag
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ public void isEnabled_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+ // For unset overrides, follow flag
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({
+ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ public void isEnabled_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // When toggle override matches its default state (dw flag), don't override flags
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ public void isEnabled_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // When toggle override matches its default state (dw flag), don't override flags
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({
+ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsFalse() {
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ public void isEnabled_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() {
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({
+ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() {
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ // For unset overrides, follow flag
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags({
+ FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ public void isEnabled_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() {
+ setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
+
+ // For unset overrides, follow flag
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ @EnableFlags({
+ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags({
+ FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnTrue() {
+ setOverride(OVERRIDE_ON.getSetting());
+
+ // Follow override if they exist, and is not equal to default toggle state (dw flag)
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags({
+ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ public void isEnabled_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() {
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // When toggle override matches its default state (dw flag), don't override flags
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags({
+ FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+ })
+ public void isEnabled_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() {
+ setOverride(OVERRIDE_OFF.getSetting());
+
+ // When toggle override matches its default state (dw flag), don't override flags
+ assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse();
+ }
+
+ private void setOverride(Integer setting) {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ String key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
+
+ if (setting == null) {
+ Settings.Global.putString(contentResolver, key, null);
+ } else {
+ Settings.Global.putInt(contentResolver, key, setting);
+ }
+ }
+
+ private void resetCache() throws Exception {
+ Field cachedToggleOverride = DesktopModeFlagsUtil.class.getDeclaredField(
+ "sCachedToggleOverride");
+ cachedToggleOverride.setAccessible(true);
+ cachedToggleOverride.set(null, null);
+
+ // Clear override cache stored in System property
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
+ }
+}