Loading core/java/android/hardware/camera2/CameraManager.java +14 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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); } } Loading core/java/android/hardware/devicestate/DeviceStateManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -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 * Loading services/core/java/com/android/server/display/DisplayManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -5240,7 +5240,7 @@ public final class DisplayManagerService extends SystemService { mHandler.sendMessage(msg); mLogicalDisplayMapper .setDeviceStateLocked(deviceState.getIdentifier()); .setDeviceStateLocked(deviceState); } } }; Loading services/core/java/com/android/server/display/LogicalDisplayMapper.java +65 −43 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -245,6 +250,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mDeviceStateToLayoutMap = deviceStateToLayoutMap; mFlags = flags; mSyntheticModeManager = syntheticModeManager; mDeviceStateManagerFlags = new FeatureFlagsImpl(); } @Override Loading Loading @@ -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); Loading @@ -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(); Loading Loading @@ -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. Loading @@ -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, Loading @@ -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. Loading Loading @@ -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); } } Loading Loading @@ -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. Loading @@ -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(); Loading @@ -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 Loading Loading @@ -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. Loading @@ -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; } Loading services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java +32 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) Loading @@ -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; Loading Loading @@ -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)); } Loading Loading @@ -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()); Loading @@ -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()); Loading @@ -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()); Loading Loading @@ -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( Loading Loading @@ -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( Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading
core/java/android/hardware/camera2/CameraManager.java +14 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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); } } Loading
core/java/android/hardware/devicestate/DeviceStateManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -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 * Loading
services/core/java/com/android/server/display/DisplayManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -5240,7 +5240,7 @@ public final class DisplayManagerService extends SystemService { mHandler.sendMessage(msg); mLogicalDisplayMapper .setDeviceStateLocked(deviceState.getIdentifier()); .setDeviceStateLocked(deviceState); } } }; Loading
services/core/java/com/android/server/display/LogicalDisplayMapper.java +65 −43 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -245,6 +250,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mDeviceStateToLayoutMap = deviceStateToLayoutMap; mFlags = flags; mSyntheticModeManager = syntheticModeManager; mDeviceStateManagerFlags = new FeatureFlagsImpl(); } @Override Loading Loading @@ -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); Loading @@ -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(); Loading Loading @@ -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. Loading @@ -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, Loading @@ -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. Loading Loading @@ -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); } } Loading Loading @@ -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. Loading @@ -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(); Loading @@ -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 Loading Loading @@ -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. Loading @@ -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; } Loading
services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java +32 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) Loading @@ -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; Loading Loading @@ -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)); } Loading Loading @@ -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()); Loading @@ -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()); Loading @@ -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()); Loading Loading @@ -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( Loading Loading @@ -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( Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; Loading