Go to sleep on fold for foldable devices.
Allow configuration of foldable states to indicate the device should sleep when entering certain states. Test: Check that device still wakes up on unfold, and sleeps on fold. Bug: 180154690 Change-Id: I179d93ec506deee7e9da7d9190934c52a0bc188f
This commit is contained in:
@@ -371,6 +371,13 @@ public abstract class DisplayManagerInternal {
|
||||
*/
|
||||
public abstract Point getDisplaySurfaceDefaultSize(int displayId);
|
||||
|
||||
/**
|
||||
* Receives early interactivity changes from power manager.
|
||||
*
|
||||
* @param interactive The interactive state that the device is moving into.
|
||||
*/
|
||||
public abstract void onEarlyInteractivityChange(boolean interactive);
|
||||
|
||||
/**
|
||||
* Describes the requested power state of the display.
|
||||
*
|
||||
|
||||
@@ -440,9 +440,15 @@ public final class PowerManager {
|
||||
public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF = 12;
|
||||
|
||||
/**
|
||||
* Go to sleep reason code: A foldable device has been folded.
|
||||
* @hide
|
||||
*/
|
||||
public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF;
|
||||
public static final int GO_TO_SLEEP_REASON_DEVICE_FOLD = 13;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_DEVICE_FOLD;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -461,6 +467,7 @@ public final class PowerManager {
|
||||
case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive";
|
||||
case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed";
|
||||
case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off";
|
||||
case GO_TO_SLEEP_REASON_DEVICE_FOLD: return "device_folded";
|
||||
default: return Integer.toString(sleepReason);
|
||||
}
|
||||
}
|
||||
@@ -568,7 +575,8 @@ public final class PowerManager {
|
||||
GO_TO_SLEEP_REASON_ACCESSIBILITY,
|
||||
GO_TO_SLEEP_REASON_FORCE_SUSPEND,
|
||||
GO_TO_SLEEP_REASON_INATTENTIVE,
|
||||
GO_TO_SLEEP_REASON_QUIESCENT
|
||||
GO_TO_SLEEP_REASON_QUIESCENT,
|
||||
GO_TO_SLEEP_REASON_DEVICE_FOLD
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface GoToSleepReason{}
|
||||
|
||||
@@ -654,10 +654,23 @@
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- When entering this device state (defined in device_state_configuration.xml),
|
||||
we should wake the device. -1 to disable the feature (do not wake on any device-state
|
||||
transition). -->
|
||||
<integer name="config_deviceStateOnWhichToWakeUp">-1</integer>
|
||||
<!-- When a device enters any of these states, it should be woken up. States are defined in
|
||||
device_state_configuration.xml. -->
|
||||
<integer-array name="config_deviceStatesOnWhichToWakeUp">
|
||||
<!-- Example:
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- When a device enters any of these states, it should go to sleep. States are defined in
|
||||
device_state_configuration.xml. -->
|
||||
<integer-array name="config_deviceStatesOnWhichToSleep">
|
||||
<!-- Example:
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- Indicate the display area rect for foldable devices in folded state. -->
|
||||
<string name="config_foldedArea"></string>
|
||||
|
||||
@@ -3856,7 +3856,8 @@
|
||||
|
||||
<!-- For Foldables -->
|
||||
<java-symbol type="array" name="config_foldedDeviceStates" />
|
||||
<java-symbol type="integer" name="config_deviceStateOnWhichToWakeUp" />
|
||||
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
|
||||
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
|
||||
<java-symbol type="string" name="config_foldedArea" />
|
||||
<java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
|
||||
<java-symbol type="bool" name="config_unfoldTransitionEnabled" />
|
||||
|
||||
@@ -524,6 +524,7 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
} else if (phase == PHASE_BOOT_COMPLETED) {
|
||||
mDisplayModeDirector.onBootCompleted();
|
||||
mLogicalDisplayMapper.onBootCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3601,6 +3602,11 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
return device.getDisplaySurfaceDefaultSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEarlyInteractivityChange(boolean interactive) {
|
||||
mLogicalDisplayMapper.onEarlyInteractivityChange(interactive);
|
||||
}
|
||||
}
|
||||
|
||||
class DesiredDisplayModeSpecsObserver
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.util.ArraySet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAddress;
|
||||
@@ -71,7 +72,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
|
||||
public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
|
||||
|
||||
private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 300;
|
||||
private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500;
|
||||
|
||||
private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
|
||||
|
||||
@@ -100,9 +101,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
private final boolean mSupportsConcurrentInternalDisplays;
|
||||
|
||||
/**
|
||||
* Wake the device when transitioning into this device state.
|
||||
* Wake the device when transitioning into these device state.
|
||||
*/
|
||||
private final int mDeviceStateOnWhichToWakeUp;
|
||||
private final SparseBooleanArray mDeviceStatesOnWhichToWakeUp;
|
||||
|
||||
/**
|
||||
* Sleep the device when transitioning into these device state.
|
||||
*/
|
||||
private final SparseBooleanArray mDeviceStatesOnWhichToSleep;
|
||||
|
||||
/**
|
||||
* Map of all logical displays indexed by logical display id.
|
||||
@@ -153,20 +159,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
private Layout mCurrentLayout = null;
|
||||
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
|
||||
private boolean mBootCompleted = false;
|
||||
private boolean mInteractive;
|
||||
|
||||
LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
|
||||
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
|
||||
@NonNull Handler handler) {
|
||||
mSyncRoot = syncRoot;
|
||||
mPowerManager = context.getSystemService(PowerManager.class);
|
||||
mInteractive = mPowerManager.isInteractive();
|
||||
mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
|
||||
mDisplayDeviceRepo = repo;
|
||||
mListener = listener;
|
||||
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
|
||||
mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
|
||||
mDeviceStateOnWhichToWakeUp = context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_deviceStateOnWhichToWakeUp);
|
||||
mDeviceStatesOnWhichToWakeUp = toSparseBooleanArray(context.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_deviceStatesOnWhichToWakeUp));
|
||||
mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
|
||||
mDisplayDeviceRepo.addListener(this);
|
||||
mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
|
||||
}
|
||||
@@ -329,7 +340,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
|
||||
ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
|
||||
ipw.println("mCurrentLayout=" + mCurrentLayout);
|
||||
ipw.println("mDeviceStateOnWhichToWakeUp=" + mDeviceStateOnWhichToWakeUp);
|
||||
ipw.println("mDeviceStatesOnWhichToWakeUp=" + mDeviceStatesOnWhichToWakeUp);
|
||||
ipw.println("mDeviceStatesOnWhichToSleep=" + mDeviceStatesOnWhichToSleep);
|
||||
ipw.println("mInteractive=" + mInteractive);
|
||||
|
||||
final int logicalDisplayCount = mLogicalDisplays.size();
|
||||
ipw.println();
|
||||
@@ -347,9 +360,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
}
|
||||
|
||||
void setDeviceStateLocked(int state) {
|
||||
final boolean isInteractive = mPowerManager.isInteractive();
|
||||
Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
|
||||
+ ", interactive=" + isInteractive);
|
||||
+ ", interactive=" + mInteractive);
|
||||
// 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 DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be
|
||||
@@ -358,13 +370,17 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
|
||||
}
|
||||
mPendingDeviceState = state;
|
||||
final boolean wakeDevice = mPendingDeviceState == mDeviceStateOnWhichToWakeUp
|
||||
&& !isInteractive;
|
||||
final boolean wakeDevice = mDeviceStatesOnWhichToWakeUp.get(mPendingDeviceState)
|
||||
&& !mDeviceStatesOnWhichToWakeUp.get(mDeviceState)
|
||||
&& !mInteractive && mBootCompleted;
|
||||
final boolean sleepDevice = mDeviceStatesOnWhichToSleep.get(mPendingDeviceState)
|
||||
&& !mDeviceStatesOnWhichToSleep.get(mDeviceState)
|
||||
&& mInteractive && mBootCompleted;
|
||||
|
||||
// If all displays are off already, we can just transition here, unless the device is asleep
|
||||
// and we plan on waking it up. In that case, fall through to the call to wakeUp, and defer
|
||||
// the final transition until later once the device is awake.
|
||||
if (areAllTransitioningDisplaysOffLocked() && !wakeDevice) {
|
||||
// If all displays are off already, we can just transition here, unless we are trying to
|
||||
// wake or sleep the device as part of this transition. In that case defer the final
|
||||
// transition until later once the device is awake/asleep.
|
||||
if (areAllTransitioningDisplaysOffLocked() && !wakeDevice && !sleepDevice) {
|
||||
transitionToPendingStateLocked();
|
||||
return;
|
||||
}
|
||||
@@ -376,17 +392,39 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
// start turning OFF in preparation for the new layout.
|
||||
updateLogicalDisplaysLocked();
|
||||
|
||||
if (wakeDevice) {
|
||||
// We already told the displays to turn off, now we need to wake the device as
|
||||
// we transition to this new state. We do it here so that the waking happens between the
|
||||
// transition from one layout to another.
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNFOLD_DEVICE,
|
||||
"server.display:unfold");
|
||||
if (wakeDevice || sleepDevice) {
|
||||
if (wakeDevice) {
|
||||
// We already told the displays to turn off, now we need to wake the device as
|
||||
// we transition to this new state. We do it here so that the waking happens
|
||||
// between the transition from one layout to another.
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis(),
|
||||
PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
|
||||
} else if (sleepDevice) {
|
||||
// Send the device to sleep when required.
|
||||
mPowerManager.goToSleep(SystemClock.uptimeMillis(),
|
||||
PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, 0);
|
||||
}
|
||||
}
|
||||
|
||||
mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE,
|
||||
TIMEOUT_STATE_TRANSITION_MILLIS);
|
||||
}
|
||||
|
||||
void onBootCompleted() {
|
||||
synchronized (mSyncRoot) {
|
||||
mBootCompleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void onEarlyInteractivityChange(boolean interactive) {
|
||||
synchronized (mSyncRoot) {
|
||||
if (mInteractive != interactive) {
|
||||
mInteractive = interactive;
|
||||
finishStateTransitionLocked(false /*force*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areAllTransitioningDisplaysOffLocked() {
|
||||
final int count = mLogicalDisplays.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -419,13 +457,24 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean waitingToWakeDevice = mDeviceStatesOnWhichToWakeUp.get(mPendingDeviceState)
|
||||
&& !mDeviceStatesOnWhichToWakeUp.get(mDeviceState)
|
||||
&& !mInteractive && mBootCompleted;
|
||||
final boolean waitingToSleepDevice = mDeviceStatesOnWhichToSleep.get(mPendingDeviceState)
|
||||
&& !mDeviceStatesOnWhichToSleep.get(mDeviceState)
|
||||
&& mInteractive && mBootCompleted;
|
||||
|
||||
final boolean displaysOff = areAllTransitioningDisplaysOffLocked();
|
||||
if (displaysOff || force) {
|
||||
final boolean isReadyToTransition = displaysOff && !waitingToWakeDevice
|
||||
&& !waitingToSleepDevice;
|
||||
|
||||
if (isReadyToTransition || force) {
|
||||
transitionToPendingStateLocked();
|
||||
mHandler.removeMessages(MSG_TRANSITION_TO_PENDING_DEVICE_STATE);
|
||||
} else if (DEBUG) {
|
||||
Slog.d(TAG, "Not yet ready to transition to state=" + mPendingDeviceState
|
||||
+ " with displays-off=" + displaysOff + " and force=" + force);
|
||||
+ " with displays-off=" + displaysOff + ", force=" + force
|
||||
+ ", mInteractive=" + mInteractive + ", isReady=" + isReadyToTransition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,6 +870,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
return displayId;
|
||||
}
|
||||
|
||||
private SparseBooleanArray toSparseBooleanArray(int[] input) {
|
||||
final SparseBooleanArray retval = new SparseBooleanArray(2);
|
||||
for (int i = 0; input != null && i < input.length; i++) {
|
||||
retval.put(input[i], true);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
private String displayEventToString(int msg) {
|
||||
switch(msg) {
|
||||
case LOGICAL_DISPLAY_EVENT_ADDED:
|
||||
@@ -861,5 +918,4 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.app.trust.TrustManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.input.InputManagerInternal;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
@@ -129,6 +130,7 @@ public class Notifier {
|
||||
private final TrustManager mTrustManager;
|
||||
private final Vibrator mVibrator;
|
||||
private final WakeLockLog mWakeLockLog;
|
||||
private final DisplayManagerInternal mDisplayManagerInternal;
|
||||
|
||||
private final NotifierHandler mHandler;
|
||||
private final Intent mScreenOnIntent;
|
||||
@@ -181,6 +183,7 @@ public class Notifier {
|
||||
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
|
||||
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
|
||||
mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
|
||||
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
|
||||
mTrustManager = mContext.getSystemService(TrustManager.class);
|
||||
mVibrator = mContext.getSystemService(Vibrator.class);
|
||||
|
||||
@@ -460,7 +463,10 @@ public class Notifier {
|
||||
synchronized (mLock) {
|
||||
if (mInteractive) {
|
||||
// Waking up...
|
||||
mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));
|
||||
mHandler.post(() -> {
|
||||
mPolicy.startedWakingUp(mInteractiveChangeReason);
|
||||
mDisplayManagerInternal.onEarlyInteractivityChange(true /*isInteractive*/);
|
||||
});
|
||||
|
||||
// Send interactive broadcast.
|
||||
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
|
||||
@@ -469,7 +475,10 @@ public class Notifier {
|
||||
} else {
|
||||
// Going to sleep...
|
||||
// Tell the policy that we started going to sleep.
|
||||
mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));
|
||||
mHandler.post(() -> {
|
||||
mPolicy.startedGoingToSleep(mInteractiveChangeReason);
|
||||
mDisplayManagerInternal.onEarlyInteractivityChange(false /*isInteractive*/);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ import android.app.PropertyInvalidatedCache;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Handler;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.IThermalService;
|
||||
import android.os.Parcel;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.test.TestLooper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -72,10 +75,13 @@ public class LogicalDisplayMapperTest {
|
||||
private LogicalDisplayMapper mLogicalDisplayMapper;
|
||||
private TestLooper mLooper;
|
||||
private Handler mHandler;
|
||||
private PowerManager mPowerManager;
|
||||
|
||||
@Mock LogicalDisplayMapper.Listener mListenerMock;
|
||||
@Mock Context mContextMock;
|
||||
@Mock Resources mResourcesMock;
|
||||
@Mock IPowerManager mIPowerManagerMock;
|
||||
@Mock IThermalService mIThermalServiceMock;
|
||||
|
||||
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
|
||||
|
||||
@@ -105,6 +111,11 @@ public class LogicalDisplayMapperTest {
|
||||
// Disable binder caches in this process.
|
||||
PropertyInvalidatedCache.disableForTestMode();
|
||||
|
||||
mPowerManager = new PowerManager(mContextMock, mIPowerManagerMock, mIThermalServiceMock,
|
||||
null);
|
||||
when(mContextMock.getSystemServiceName(PowerManager.class))
|
||||
.thenReturn(Context.POWER_SERVICE);
|
||||
when(mContextMock.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
|
||||
when(mContextMock.getResources()).thenReturn(mResourcesMock);
|
||||
when(mResourcesMock.getBoolean(
|
||||
com.android.internal.R.bool.config_supportsConcurrentInternalDisplays))
|
||||
@@ -301,7 +312,6 @@ public class LogicalDisplayMapperTest {
|
||||
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// Helper Methods
|
||||
/////////////////
|
||||
|
||||
Reference in New Issue
Block a user