summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/window/DesktopExperienceFlags.java124
-rw-r--r--core/java/android/window/DesktopModeFlags.java6
-rw-r--r--core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java164
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java3
4 files changed, 295 insertions, 2 deletions
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
new file mode 100644
index 000000000000..0d1bb77ae8a2
--- /dev/null
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -0,0 +1,124 @@
+/*
+ * 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 android.window;
+
+import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
+
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.window.flags.Flags;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Checks Desktop Experience flag state.
+ *
+ * <p>This enum provides a centralized way to control the behavior of flags related to desktop
+ * experience features which are aiming for developer preview before their release. It allows
+ * developer option to override the default behavior of these flags.
+ *
+ * <p>The flags here will be controlled by the {@code
+ * persist.wm.debug.desktop_experience_devopts} system property.
+ *
+ * <p>NOTE: Flags should only be added to this enum when they have received Product and UX alignment
+ * that the feature is ready for developer preview, otherwise just do a flag check.
+ *
+ * @hide
+ */
+public enum DesktopExperienceFlags {
+ ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(() -> enableDisplayContentModeManagement(), true);
+
+ /**
+ * Flag class, to be used in case the enum cannot be used because the flag is not accessible.
+ *
+ * <p>This class will still use the process-wide cache.
+ */
+ public static class DesktopExperienceFlag {
+ // Function called to obtain aconfig flag value.
+ private final BooleanSupplier mFlagFunction;
+ // Whether the flag state should be affected by developer option.
+ private final boolean mShouldOverrideByDevOption;
+
+ public DesktopExperienceFlag(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop experience developer option
+ * overrides.
+ */
+ public boolean isTrue() {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+ }
+
+ private static final String TAG = "DesktopExperienceFlags";
+ // Function called to obtain aconfig flag value.
+ private final BooleanSupplier 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 is expected to take effect on reboots.
+ @Nullable private static Boolean sCachedToggleOverride;
+
+ public static final String SYSTEM_PROPERTY_NAME = "persist.wm.debug.desktop_experience_devopts";
+
+ DesktopExperienceFlags(BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ this.mFlagFunction = flagFunction;
+ this.mShouldOverrideByDevOption = shouldOverrideByDevOption;
+ }
+
+ /**
+ * Determines state of flag based on the actual flag and desktop experience developer option
+ * overrides.
+ */
+ public boolean isTrue() {
+ return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
+ }
+
+ private static boolean isFlagTrue(
+ BooleanSupplier flagFunction, boolean shouldOverrideByDevOption) {
+ if (shouldOverrideByDevOption
+ && Flags.showDesktopExperienceDevOption()
+ && getToggleOverride()) {
+ return true;
+ }
+ return flagFunction.getAsBoolean();
+ }
+
+ private static boolean getToggleOverride() {
+ // If cached, return it
+ if (sCachedToggleOverride != null) {
+ return sCachedToggleOverride;
+ }
+
+ // Otherwise, fetch and cache it
+ boolean override = getToggleOverrideFromSystem();
+ sCachedToggleOverride = override;
+ Log.d(TAG, "Toggle override initialized to: " + override);
+ return override;
+ }
+
+ /** Returns the {@link ToggleOverride} from the system property.. */
+ private static boolean getToggleOverrideFromSystem() {
+ return SystemProperties.getBoolean(SYSTEM_PROPERTY_NAME, false);
+ }
+}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index cebfb381211d..e51ef4f6d04c 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -35,6 +35,10 @@ import java.util.function.BooleanSupplier;
* windowing features which are aiming for developer preview before their release. It allows
* developer option to override the default behavior of these flags.
*
+ * <p> The flags here will be controlled by either {@link
+ * Settings.Global#DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES} or the {@code
+ * persyst.wm.debug.desktop_experience_devopts} system property.
+ *
* <p>NOTE: Flags should only be added to this enum when they have received Product and UX
* alignment that the feature is ready for developer preview, otherwise just do a flag check.
*
@@ -110,7 +114,7 @@ public enum DesktopModeFlags {
/**
* Determines state of flag based on the actual flag and desktop mode developer option
- * overrides.
+ * or desktop experience developer option overrides.
*/
public boolean isTrue() {
return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
diff --git a/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
new file mode 100644
index 000000000000..cc06f3d21332
--- /dev/null
+++ b/core/tests/coretests/src/android/window/DesktopExperienceFlagsTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 android.window;
+
+import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
+
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.support.test.uiautomator.UiDevice;
+import android.window.DesktopExperienceFlags.DesktopExperienceFlag;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.window.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ * Test class for {@link android.window.DesktopExperienceFlags}
+ *
+ * <p>Build/Install/Run: atest FrameworksCoreTests:DesktopExperienceFlagsTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(ParameterizedAndroidJunit4.class)
+public class DesktopExperienceFlagsTest {
+
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION);
+ }
+
+ @Rule public SetFlagsRule mSetFlagsRule;
+
+ private UiDevice mUiDevice;
+ private Context mContext;
+ private boolean mLocalFlagValue = false;
+ private final DesktopExperienceFlag mOverriddenLocalFlag =
+ new DesktopExperienceFlag(() -> mLocalFlagValue, true);
+ private final DesktopExperienceFlag mNotOverriddenLocalFlag =
+ new DesktopExperienceFlag(() -> mLocalFlagValue, false);
+
+ private static final String OVERRIDE_OFF_SETTING = "0";
+ private static final String OVERRIDE_ON_SETTING = "1";
+ private static final String OVERRIDE_INVALID_SETTING = "garbage";
+
+ public DesktopExperienceFlagsTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ setSysProp(null);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ resetCache();
+ setSysProp(null);
+ }
+
+ @Test
+ public void isTrue_overrideOff_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
+ setSysProp(OVERRIDE_OFF_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
+ }
+
+ @Test
+ public void isTrue_overrideOn_featureFlagOn_returnsTrue() throws Exception {
+ mLocalFlagValue = true;
+ setSysProp(OVERRIDE_ON_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isTrue();
+ }
+
+ @Test
+ public void isTrue_overrideOff_featureFlagOff_returnsFalse() throws Exception {
+ mLocalFlagValue = false;
+ setSysProp(OVERRIDE_OFF_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+ }
+
+ @Test
+ public void isTrue_devOptionEnabled_overrideOn_featureFlagOff() throws Exception {
+ assumeTrue(Flags.showDesktopExperienceDevOption());
+ mLocalFlagValue = false;
+ setSysProp(OVERRIDE_ON_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isTrue();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+ }
+
+ @Test
+ public void isTrue_devOptionDisabled_overrideOn_featureFlagOff_returnsFalse() throws Exception {
+ assumeFalse(Flags.showDesktopExperienceDevOption());
+ mLocalFlagValue = false;
+ setSysProp(OVERRIDE_ON_SETTING);
+
+ assertThat(mOverriddenLocalFlag.isTrue()).isFalse();
+ assertThat(mNotOverriddenLocalFlag.isTrue()).isFalse();
+ }
+
+ private void setSysProp(String value) throws Exception {
+ if (value == null) {
+ resetSysProp();
+ } else {
+ mUiDevice.executeShellCommand(
+ "setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " " + value);
+ }
+ }
+
+ private void resetSysProp() throws Exception {
+ mUiDevice.executeShellCommand("setprop " + DesktopModeFlags.SYSTEM_PROPERTY_NAME + " ''");
+ }
+
+ private void resetCache() throws Exception {
+ Field cachedToggleOverride =
+ DesktopExperienceFlags.class.getDeclaredField("sCachedToggleOverride");
+ cachedToggleOverride.setAccessible(true);
+ cachedToggleOverride.set(null, null);
+ }
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index d435144b28c6..b9ce8c93dbde 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -21,6 +21,7 @@ import android.os.Build;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
+import android.window.DesktopExperienceFlags;
import com.android.server.display.feature.flags.Flags;
import com.android.server.display.utils.DebugUtils;
@@ -250,7 +251,7 @@ public class DisplayManagerFlags {
);
private final FlagState mEnableDisplayContentModeManagementFlagState = new FlagState(
Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
- Flags::enableDisplayContentModeManagement
+ DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT::isTrue
);
private final FlagState mSubscribeGranularDisplayEvents = new FlagState(