Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b2596e82 authored by Kenneth Ford's avatar Kenneth Ford Committed by Android (Google) Code Review
Browse files

Merge "Update device state overlay config usage to new API" into main

parents 93547bd6 c67b1835
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.hardware.camera2.utils.ExceptionUtils;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.feature.flags.FeatureFlags;
import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Handler;
@@ -247,14 +249,22 @@ public final class CameraManager {
        private ArrayList<WeakReference<DeviceStateListener>> mDeviceStateListeners =
                new ArrayList<>();
        private boolean mFoldedDeviceState;
        private final FeatureFlags mDeviceStateManagerFlags;

        public FoldStateListener(Context context) {
            mFoldedDeviceStates = context.getResources().getIntArray(
                    com.android.internal.R.array.config_foldedDeviceStates);
            mDeviceStateManagerFlags = new FeatureFlagsImpl();
        }

        private synchronized void handleStateChange(int state) {
            boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
        private synchronized void handleStateChange(DeviceState state) {
            final boolean folded;
            if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) {
                folded = state.hasProperty(
                        DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
            } else {
                folded = ArrayUtils.contains(mFoldedDeviceStates, state.getIdentifier());
            }

            mFoldedDeviceState = folded;
            Iterator<WeakReference<DeviceStateListener>> it = mDeviceStateListeners.iterator();
@@ -276,10 +286,8 @@ public final class CameraManager {

        @SuppressWarnings("FlaggedApi")
        @Override
        public void onDeviceStateChanged(DeviceState state) {
            // Suppressing the FlaggedAPI warning as this specific API isn't new, just moved to
            // system API which requires it to be flagged.
            handleStateChange(state.getIdentifier());
        public void onDeviceStateChanged(@NonNull DeviceState state) {
            handleStateChange(state);
        }
    }

+8 −0
Original line number Diff line number Diff line
@@ -66,6 +66,14 @@ public final class DeviceStateManager {
    @TestApi
    public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000;

    /**
     * {@link DeviceState} to represent an invalid device state.
     * @hide
     */
    public static final DeviceState INVALID_DEVICE_STATE = new DeviceState(
            new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
                    "INVALID").build());

    /**
     * Intent needed to launch the rear display overlay activity from SysUI
     *
+1 −1
Original line number Diff line number Diff line
@@ -5240,7 +5240,7 @@ public final class DisplayManagerService extends SystemService {
                mHandler.sendMessage(msg);

                mLogicalDisplayMapper
                        .setDeviceStateLocked(deviceState.getIdentifier());
                        .setDeviceStateLocked(deviceState);
            }
        }
    };
+65 −43
Original line number Diff line number Diff line
@@ -16,12 +16,17 @@

package com.android.server.display;

import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP;
import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.view.Display.DEFAULT_DISPLAY;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.feature.flags.FeatureFlags;
import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -198,14 +203,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    private final DisplayIdProducer mIdProducer = (isDefault) ->
            isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
    private Layout mCurrentLayout = null;
    private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
    private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
    private int mDeviceStateToBeAppliedAfterBoot =
            DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
    private DeviceState mDeviceState = INVALID_DEVICE_STATE;
    private DeviceState mPendingDeviceState = INVALID_DEVICE_STATE;
    private DeviceState mDeviceStateToBeAppliedAfterBoot = INVALID_DEVICE_STATE;
    private boolean mBootCompleted = false;
    private boolean mInteractive;
    private final DisplayManagerFlags mFlags;
    private final SyntheticModeManager mSyntheticModeManager;
    private final FeatureFlags mDeviceStateManagerFlags;

    LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
            FoldGracePeriodProvider foldGracePeriodProvider,
@@ -245,6 +250,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        mDeviceStateToLayoutMap = deviceStateToLayoutMap;
        mFlags = flags;
        mSyntheticModeManager = syntheticModeManager;
        mDeviceStateManagerFlags = new FeatureFlagsImpl();
    }

    @Override
@@ -403,8 +409,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        // Retrieve the display info for the display that matches the display id.
        final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(display.getAddress());
        if (device == null) {
            Slog.w(TAG, "The display device (" + display.getAddress() + "), is not available"
                    + " for the display state " + mDeviceState);
            Slog.w(TAG, "The display device (" + display.getAddress()
                    + "), is not available for the display state " + mDeviceState.getIdentifier());
            return null;
        }
        LogicalDisplay logicalDisplay = getDisplayLocked(device, /* includeDisabled= */ true);
@@ -431,9 +437,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        ipw.println("mBootCompleted=" + mBootCompleted);

        ipw.println();
        ipw.println("mDeviceState=" + mDeviceState);
        ipw.println("mPendingDeviceState=" + mPendingDeviceState);
        ipw.println("mDeviceStateToBeAppliedAfterBoot=" + mDeviceStateToBeAppliedAfterBoot);

        ipw.println("mDeviceState=" + mDeviceState.getIdentifier());
        ipw.println("mPendingDeviceState=" + mPendingDeviceState.getIdentifier());
        ipw.println("mDeviceStateToBeAppliedAfterBoot="
                + mDeviceStateToBeAppliedAfterBoot.getIdentifier());

        final int logicalDisplayCount = mLogicalDisplays.size();
        ipw.println();
@@ -463,7 +471,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        mVirtualDeviceDisplayMapping.put(displayDevice.getUniqueId(), virtualDeviceUniqueId);
    }

    void setDeviceStateLocked(int state) {
    void setDeviceStateLocked(DeviceState state) {
        if (!mBootCompleted) {
            // The boot animation might still be in progress, we do not want to switch states now
            // as the boot animation would end up with an incorrect size.
@@ -475,15 +483,17 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
            return;
        }

        Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
                + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
        Slog.i(TAG, "Requesting Transition to state: " + state + ", from state="
                + mDeviceState.getIdentifier() + ", interactive=" + mInteractive
                + ", mBootCompleted=" + mBootCompleted);
        // As part of a state transition, we may need to turn off some displays temporarily so that
        // the transition is smooth. Plus, on some devices, only one internal displays can be
        // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
        // temporarily turned off.
        resetLayoutLocked(mDeviceState, state, /* transitionValue= */ true);
        resetLayoutLocked(mDeviceState.getIdentifier(),
                state.getIdentifier(), /* transitionValue= */ true);
        mPendingDeviceState = state;
        mDeviceStateToBeAppliedAfterBoot = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
        mDeviceStateToBeAppliedAfterBoot = INVALID_DEVICE_STATE;
        final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
                mInteractive, mBootCompleted);
        final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
@@ -498,7 +508,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
        }

        if (DEBUG) {
            Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState);
            Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState.getIdentifier());
        }
        // Send the transitioning phase updates to DisplayManager so that the displays can
        // start turning OFF in preparation for the new layout.
@@ -533,8 +543,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    void onBootCompleted() {
        synchronized (mSyncRoot) {
            mBootCompleted = true;
            if (mDeviceStateToBeAppliedAfterBoot
                    != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER) {
            if (!mDeviceStateToBeAppliedAfterBoot.equals(INVALID_DEVICE_STATE)) {
                setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot);
            }
        }
@@ -563,12 +572,19 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
     * @see #setDeviceStateLocked
     */
    @VisibleForTesting
    boolean shouldDeviceBeWoken(int pendingState, int currentState, boolean isInteractive,
            boolean isBootCompleted) {
        return mDeviceStatesOnWhichToWakeUp.get(pendingState)
                && !mDeviceStatesOnWhichToWakeUp.get(currentState)
    boolean shouldDeviceBeWoken(DeviceState pendingState, DeviceState currentState,
            boolean isInteractive, boolean isBootCompleted) {
        if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) {
            return pendingState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)
                    && !currentState.equals(INVALID_DEVICE_STATE)
                    && !currentState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)
                    && !isInteractive && isBootCompleted;
        } else {
            return mDeviceStatesOnWhichToWakeUp.get(pendingState.getIdentifier())
                    && !mDeviceStatesOnWhichToWakeUp.get(currentState.getIdentifier())
                    && !isInteractive && isBootCompleted;
        }
    }

    /**
     * Returns if the device should be put to sleep or not.
@@ -588,15 +604,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
     * @see #setDeviceStateLocked
     */
    @VisibleForTesting
    boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isInteractive,
            boolean isBootCompleted) {
        return currentState != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER
                && mDeviceStatesOnWhichToSelectiveSleep.get(pendingState)
                && !mDeviceStatesOnWhichToSelectiveSleep.get(currentState)
    boolean shouldDeviceBePutToSleep(DeviceState pendingState, DeviceState currentState,
            boolean isInteractive, boolean isBootCompleted) {
        if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) {
            return pendingState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP)
                    && !currentState.equals(INVALID_DEVICE_STATE)
                    && !currentState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP)
                    && isInteractive
                    && isBootCompleted
                    && !mFoldSettingProvider.shouldStayAwakeOnFold();
        } else {
            return mDeviceStatesOnWhichToSelectiveSleep.get(pendingState.getIdentifier())
                    && !mDeviceStatesOnWhichToSelectiveSleep.get(currentState.getIdentifier())
                    && isInteractive
                    && isBootCompleted
                    && !mFoldSettingProvider.shouldStayAwakeOnFold();
        }
    }

    private boolean areAllTransitioningDisplaysOffLocked() {
        final int count = mLogicalDisplays.size();
@@ -618,27 +642,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    }

    private void transitionToPendingStateLocked() {
        resetLayoutLocked(mDeviceState, mPendingDeviceState, /* transitionValue= */ false);
        resetLayoutLocked(mDeviceState.getIdentifier(),
                mPendingDeviceState.getIdentifier(), /* transitionValue= */ false);
        mDeviceState = mPendingDeviceState;
        mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
        mPendingDeviceState = INVALID_DEVICE_STATE;
        applyLayoutLocked();
        updateLogicalDisplaysLocked();
    }

    private void finishStateTransitionLocked(boolean force) {
        if (mPendingDeviceState == DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER) {
        if (mPendingDeviceState.equals(INVALID_DEVICE_STATE)) {
            return;
        }

        final boolean waitingToWakeDevice = mDeviceStatesOnWhichToWakeUp.get(mPendingDeviceState)
                && !mDeviceStatesOnWhichToWakeUp.get(mDeviceState)
                && !mInteractive && mBootCompleted;
        final boolean waitingToWakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
                mInteractive, mBootCompleted);
        // The device should only wait for sleep if #shouldStayAwakeOnFold method returns false.
        // If not, device should be marked ready for transition immediately.
        final boolean waitingToSleepDevice = mDeviceStatesOnWhichToSelectiveSleep.get(
                mPendingDeviceState)
                && !mDeviceStatesOnWhichToSelectiveSleep.get(mDeviceState)
                && mInteractive && mBootCompleted && !shouldStayAwakeOnFold();
        final boolean waitingToSleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState,
                mDeviceState, mInteractive, mBootCompleted) && !shouldStayAwakeOnFold();

        final boolean displaysOff = areAllTransitioningDisplaysOffLocked();
        final boolean isReadyToTransition = displaysOff && !waitingToWakeDevice
@@ -1104,7 +1126,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
     */
    private void applyLayoutLocked() {
        final Layout oldLayout = mCurrentLayout;
        mCurrentLayout = mDeviceStateToLayoutMap.get(mDeviceState);
        mCurrentLayout = mDeviceStateToLayoutMap.get(mDeviceState.getIdentifier());
        Slog.i(TAG, "Applying layout: " + mCurrentLayout + ", Previous layout: " + oldLayout);

        // Go through each of the displays in the current layout set.
@@ -1120,7 +1142,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
            final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
            if (device == null) {
                Slog.w(TAG, "applyLayoutLocked: The display device (" + address + "), is not "
                        + "available for the display state " + mDeviceState);
                        + "available for the display state " + mDeviceState.getIdentifier());
                continue;
            }

+32 −15
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@

package com.android.server.display;

import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.DEFAULT_DISPLAY_GROUP;
import static android.view.Display.FLAG_REAR;
@@ -62,9 +62,11 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.NonNull;
import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.devicestate.DeviceState;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -103,7 +105,9 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -111,9 +115,12 @@ public class LogicalDisplayMapperTest {
    private static int sUniqueTestDisplayId = 0;
    private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500;
    private static final int FOLD_SETTLE_DELAY = 1000;
    private static final int DEVICE_STATE_CLOSED = 0;
    private static final int DEVICE_STATE_HALF_OPEN = 1;
    private static final int DEVICE_STATE_OPEN = 2;
    private static final DeviceState DEVICE_STATE_CLOSED = createDeviceState(0, "Zero",
            Set.of(DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP), Collections.emptySet());
    private static final DeviceState DEVICE_STATE_HALF_OPEN = createDeviceState(1, "One",
            Set.of(DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE), Collections.emptySet());
    private static final DeviceState DEVICE_STATE_OPEN = createDeviceState(2, "Two",
            Set.of(DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE), Collections.emptySet());
    private static final int FLAG_GO_TO_SLEEP_ON_FOLD = 0;
    private static final int FLAG_GO_TO_SLEEP_FLAG_SOFT_SLEEP = 2;
    private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;
@@ -703,8 +710,7 @@ public class LogicalDisplayMapperTest {
                /* isInteractive= */true,
                /* isBootCompleted= */true));
        assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
                INVALID_DEVICE_STATE_IDENTIFIER,
                /* isInteractive= */true,
                INVALID_DEVICE_STATE /* currentState */, /* isInteractive= */true,
                /* isBootCompleted= */true));
    }

@@ -932,7 +938,7 @@ public class LogicalDisplayMapperTest {
        // We can only have one default display
        assertEquals(DEFAULT_DISPLAY, id(display1));

        mLogicalDisplayMapper.setDeviceStateLocked(0);
        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED);
        advanceTime(1000);
        // The new state is not applied until the boot is completed
        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
@@ -953,7 +959,7 @@ public class LogicalDisplayMapperTest {
        assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device2)
                .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);

        mLogicalDisplayMapper.setDeviceStateLocked(1);
        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_HALF_OPEN);
        advanceTime(1000);
        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
@@ -966,7 +972,7 @@ public class LogicalDisplayMapperTest {
                mLogicalDisplayMapper.getDisplayLocked(device2)
                        .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);

        mLogicalDisplayMapper.setDeviceStateLocked(2);
        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_OPEN);
        advanceTime(1000);
        assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
        assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
@@ -1043,7 +1049,7 @@ public class LogicalDisplayMapperTest {
        // 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state
        // 4) Dispatch handler events.
        mLogicalDisplayMapper.onBootCompleted();
        mLogicalDisplayMapper.setDeviceStateLocked(0);
        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED);
        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
        advanceTime(1000);
        final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
@@ -1073,7 +1079,7 @@ public class LogicalDisplayMapperTest {
                /* includeDisabled= */ false));

        // Now do it again to go back to state 1
        mLogicalDisplayMapper.setDeviceStateLocked(1);
        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_HALF_OPEN);
        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
        advanceTime(1000);
        final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
@@ -1127,7 +1133,7 @@ public class LogicalDisplayMapperTest {
        // We can only have one default display
        assertEquals(DEFAULT_DISPLAY, id(display1));

        mLogicalDisplayMapper.setDeviceStateLocked(0);
        mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED);
        advanceTime(1000);
        mLogicalDisplayMapper.onBootCompleted();
        advanceTime(1000);
@@ -1180,13 +1186,15 @@ public class LogicalDisplayMapperTest {
        Layout layout = new Layout();
        createDefaultDisplay(layout, outer);
        createNonDefaultDisplay(layout, inner, /* enabled= */ false, /* group= */ null);
        when(mDeviceStateToLayoutMapSpy.get(DEVICE_STATE_CLOSED)).thenReturn(layout);
        when(mDeviceStateToLayoutMapSpy.get(DEVICE_STATE_CLOSED.getIdentifier())).thenReturn(
                layout);

        layout = new Layout();
        createNonDefaultDisplay(layout, outer, /* enabled= */ false, /* group= */ null);
        createDefaultDisplay(layout, inner);
        when(mDeviceStateToLayoutMapSpy.get(DEVICE_STATE_HALF_OPEN)).thenReturn(layout);
        when(mDeviceStateToLayoutMapSpy.get(DEVICE_STATE_OPEN)).thenReturn(layout);
        when(mDeviceStateToLayoutMapSpy.get(DEVICE_STATE_HALF_OPEN.getIdentifier())).thenReturn(
                layout);
        when(mDeviceStateToLayoutMapSpy.get(DEVICE_STATE_OPEN.getIdentifier())).thenReturn(layout);
        when(mDeviceStateToLayoutMapSpy.size()).thenReturn(4);

        add(outer);
@@ -1317,6 +1325,15 @@ public class LogicalDisplayMapperTest {
        assertNotEquals(DEFAULT_DISPLAY, id(displayRemoved));
    }

    private static DeviceState createDeviceState(int identifier, @NonNull String name,
            @NonNull Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties,
            @NonNull Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) {
        DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder(
                identifier, name).setSystemProperties(systemProperties).setPhysicalProperties(
                physicalProperties).build();
        return new DeviceState(deviceStateConfiguration);
    }

    private final static class FoldableDisplayDevices {
        final TestDisplayDevice mOuter;
        final TestDisplayDevice mInner;