Merge "Add DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT" into main
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index e583627..8b4d0da 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -172,6 +172,23 @@
      */
     public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17;
 
+    /**
+     * Property that indicates that this state corresponds to the device state for rear display
+     * mode, where both the inner and outer displays are on. In this state, the outer display
+     * is the default display where the app is shown, and the inner display is used by the system to
+     * show a UI affordance for exiting the mode.
+     *
+     * Note that this value should generally not be used, and may be removed in the future (e.g.
+     * if or when it becomes the only type of rear display mode when
+     * {@link android.hardware.devicestate.feature.flags.Flags#deviceStateRdmV2} is removed).
+     *
+     * As such, clients should strongly consider relying on {@link #PROPERTY_FEATURE_REAR_DISPLAY}
+     * instead.
+     *
+     * @hide
+     */
+    public static final int PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT = 1001;
+
     /** @hide */
     @IntDef(prefix = {"PROPERTY_"}, flag = false, value = {
             PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
@@ -190,7 +207,8 @@
             PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
             PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY,
             PROPERTY_FEATURE_REAR_DISPLAY,
-            PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+            PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+            PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT
     })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
index 98ba9192..6230f4d 100644
--- a/core/java/android/hardware/devicestate/feature/flags.aconfig
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -29,4 +29,13 @@
     metadata {
       purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "device_state_rdm_v2"
+    is_exported: true
+    namespace: "windowing_sdk"
+    description: "Enables Rear Display Mode V2, where the inner display shows the user a UI affordance for exiting the state"
+    bug: "372486634"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 4d7be39..76eb207 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -19,6 +19,7 @@
 import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
@@ -104,6 +105,30 @@
     @GuardedBy("mLock")
     private int mLastReportedRearDisplayPresentationStatus;
 
+    @VisibleForTesting
+    static int getRdmV1Identifier(List<DeviceState> currentSupportedDeviceStates) {
+        for (int i = 0; i < currentSupportedDeviceStates.size(); i++) {
+            DeviceState state = currentSupportedDeviceStates.get(i);
+            if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)
+                    && !state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) {
+                return state.getIdentifier();
+            }
+        }
+        return INVALID_DEVICE_STATE_IDENTIFIER;
+    }
+
+    @VisibleForTesting
+    static int getRdmV2Identifier(List<DeviceState> currentSupportedDeviceStates) {
+        for (int i = 0; i < currentSupportedDeviceStates.size(); i++) {
+            DeviceState state = currentSupportedDeviceStates.get(i);
+            if (state.hasProperties(PROPERTY_FEATURE_REAR_DISPLAY,
+                    PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) {
+                return state.getIdentifier();
+            }
+        }
+        return INVALID_DEVICE_STATE_IDENTIFIER;
+    }
+
     public WindowAreaComponentImpl(@NonNull Context context) {
         mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
         mDisplayManager = context.getSystemService(DisplayManager.class);
@@ -112,12 +137,10 @@
         mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedDeviceStates();
 
         if (Flags.deviceStatePropertyMigration()) {
-            for (int i = 0; i < mCurrentSupportedDeviceStates.size(); i++) {
-                DeviceState state = mCurrentSupportedDeviceStates.get(i);
-                if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) {
-                    mRearDisplayState = state.getIdentifier();
-                    break;
-                }
+            if (Flags.deviceStateRdmV2()) {
+                mRearDisplayState = getRdmV2Identifier(mCurrentSupportedDeviceStates);
+            } else {
+                mRearDisplayState = getRdmV1Identifier(mCurrentSupportedDeviceStates);
             }
         } else {
             mFoldedDeviceStates = context.getResources().getIntArray(
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java
index ccb4ebe..d677fef 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java
@@ -16,8 +16,13 @@
 
 package androidx.window.extensions.area;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
+
 import static org.junit.Assert.assertEquals;
 
+import android.hardware.devicestate.DeviceState;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.view.Surface;
@@ -29,11 +34,34 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class WindowAreaComponentImplTests {
 
+    private static final DeviceState REAR_DISPLAY_STATE_V1 = new DeviceState(
+            new DeviceState.Configuration.Builder(1, "STATE_0")
+                    .setSystemProperties(
+                            Set.of(PROPERTY_FEATURE_REAR_DISPLAY))
+                    .build());
+    private static final DeviceState REAR_DISPLAY_STATE_V2 = new DeviceState(
+            new DeviceState.Configuration.Builder(2, "STATE_0")
+                    .setSystemProperties(
+                            Set.of(PROPERTY_FEATURE_REAR_DISPLAY,
+                                    PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT))
+                    .build());
+    // The PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT state must be present together with the
+    // PROPERTY_FEATURE_REAR_DISPLAY state in order to be a valid state.
+    private static final DeviceState INVALID_REAR_DISPLAY_STATE = new DeviceState(
+            new DeviceState.Configuration.Builder(2, "STATE_0")
+                    .setSystemProperties(
+                            Set.of(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT))
+                    .build());
+
     private final DisplayMetrics mTestDisplayMetrics = new DisplayMetrics();
 
     @Before
@@ -93,4 +121,37 @@
                 Surface.ROTATION_270, Surface.ROTATION_0, mTestDisplayMetrics);
         assertEquals(expectedMetrics, mTestDisplayMetrics);
     }
+
+    @Test
+    public void testRdmV1Identifier() {
+        final List<DeviceState> supportedStates = new ArrayList<>();
+        supportedStates.add(REAR_DISPLAY_STATE_V2);
+        assertEquals(INVALID_DEVICE_STATE_IDENTIFIER,
+                WindowAreaComponentImpl.getRdmV1Identifier(supportedStates));
+
+        supportedStates.add(REAR_DISPLAY_STATE_V1);
+        assertEquals(REAR_DISPLAY_STATE_V1.getIdentifier(),
+                WindowAreaComponentImpl.getRdmV1Identifier(supportedStates));
+    }
+
+    @Test
+    public void testRdmV2Identifier_whenStateIsImproperlyConfigured() {
+        final List<DeviceState> supportedStates = new ArrayList<>();
+        supportedStates.add(INVALID_REAR_DISPLAY_STATE);
+        assertEquals(INVALID_DEVICE_STATE_IDENTIFIER,
+                WindowAreaComponentImpl.getRdmV2Identifier(supportedStates));
+    }
+
+    @Test
+    public void testRdmV2Identifier_whenStateIsProperlyConfigured() {
+        final List<DeviceState> supportedStates = new ArrayList<>();
+
+        supportedStates.add(REAR_DISPLAY_STATE_V1);
+        assertEquals(INVALID_DEVICE_STATE_IDENTIFIER,
+                WindowAreaComponentImpl.getRdmV2Identifier(supportedStates));
+
+        supportedStates.add(REAR_DISPLAY_STATE_V2);
+        assertEquals(REAR_DISPLAY_STATE_V2.getIdentifier(),
+                WindowAreaComponentImpl.getRdmV2Identifier(supportedStates));
+    }
 }
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
index cc5573b..f34ec72 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
@@ -19,6 +19,7 @@
 import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
 import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
@@ -71,6 +72,7 @@
     private static final int DEVICE_STATE_OPENED = 2;
     private static final int DEVICE_STATE_REAR_DISPLAY = 3;
     private static final int DEVICE_STATE_CONCURRENT_INNER_DEFAULT = 4;
+    private static final int DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT = 5;
     private static final int TENT_MODE_SWITCH_ANGLE_DEGREES = 90;
     private static final int TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES = 125;
     private static final int MIN_CLOSED_ANGLE_DEGREES = 0;
@@ -130,14 +132,17 @@
                             return hingeAngle >= MAX_CLOSED_ANGLE_DEGREES
                                     && hingeAngle <= TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES;
                         }),
-                createConfig(getOpenedDeviceState(), /* activeStatePredicate= */
-                        ALLOWED),
-                createConfig(getRearDisplayDeviceState(), /* activeStatePredicate= */
-                        NOT_ALLOWED),
-                createConfig(getDualDisplayDeviceState(), /* activeStatePredicate= */
-                        NOT_ALLOWED, /* availabilityPredicate= */
-                        provider -> !mIsDualDisplayBlockingEnabled
-                                || provider.hasNoConnectedExternalDisplay())};
+                createConfig(getOpenedDeviceState(),
+                        /* activeStatePredicate= */ ALLOWED),
+                createConfig(getRearDisplayDeviceState(),
+                        /* activeStatePredicate= */ NOT_ALLOWED),
+                createConfig(getDualDisplayDeviceState(),
+                        /* activeStatePredicate= */ NOT_ALLOWED,
+                        /* availabilityPredicate= */ provider -> !mIsDualDisplayBlockingEnabled
+                                || provider.hasNoConnectedExternalDisplay()),
+                createConfig(getRearDisplayOuterDefaultState(),
+                        /* activeStatePredicate= */ NOT_ALLOWED)
+        };
     }
 
     private DeviceStatePredicateWrapper createClosedConfiguration(
@@ -266,4 +271,24 @@
                 .setSystemProperties(systemProperties)
                 .build());
     }
+
+    /**
+     * Returns the {link DeviceState.Configuration} that represents the new rear display state
+     * where the inner display is also enabled, showing a system affordance to exit the state.
+     */
+    @NonNull
+    private DeviceState getRearDisplayOuterDefaultState() {
+        Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties = new HashSet<>(
+                List.of(PROPERTY_EMULATED_ONLY,
+                        PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+                        PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST,
+                        PROPERTY_FEATURE_REAR_DISPLAY,
+                        PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT));
+
+        return new DeviceState(new DeviceState.Configuration.Builder(
+                DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT,
+                "REAR_DISPLAY_OUTER_DEFAULT")
+                .setSystemProperties(systemProperties)
+                .build());
+    }
 }