diff options
| author | 2021-01-21 21:04:58 +0000 | |
|---|---|---|
| committer | 2021-01-21 21:04:58 +0000 | |
| commit | 99170bffd3bcb3856754c79bf3dd5ae6bcf50158 (patch) | |
| tree | 4a480561bad92f2019c15eca5cfd0bd1c259fd1a | |
| parent | 3cc6d0b3741d91612532c6811e817531552283f0 (diff) | |
| parent | e2f209d2600864c447362f7c71341c8e5564b41f (diff) | |
Merge "PM#goToSleep and PM#wakeUp only affects default display group"
12 files changed, 1186 insertions, 493 deletions
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 5a03adee4eab..421a07ba93e1 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -47,28 +47,40 @@ public abstract class DisplayManagerInternal { * begins adjusting the power state to match what was requested. * </p> * + * @param groupId The identifier for the display group being requested to change power state * @param request The requested power state. - * @param waitForNegativeProximity If true, issues a request to wait for + * @param waitForNegativeProximity If {@code true}, issues a request to wait for * negative proximity before turning the screen back on, assuming the screen * was turned off by the proximity sensor. - * @return True if display is ready, false if there are important changes that must - * be made asynchronously (such as turning the screen on), in which case the caller - * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()} - * then try the request again later until the state converges. + * @return {@code true} if display group is ready, {@code false} if there are important + * changes that must be made asynchronously (such as turning the screen on), in which case + * the caller should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged} + * then try the request again later until the state converges. If the provided {@code groupId} + * cannot be found then {@code true} will be returned. */ - public abstract boolean requestPowerState(DisplayPowerRequest request, + public abstract boolean requestPowerState(int groupId, DisplayPowerRequest request, boolean waitForNegativeProximity); /** - * Returns true if the proximity sensor screen-off function is available. + * Returns {@code true} if the proximity sensor screen-off function is available. */ public abstract boolean isProximitySensorAvailable(); /** - * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided - * display belongs. + * Registers a display group listener which will be informed of the addition, removal, or change + * of display groups. + * + * @param listener The listener to register. */ - public abstract int getDisplayGroupId(int displayId); + public abstract void registerDisplayGroupListener(DisplayGroupListener listener); + + /** + * Unregisters a display group listener which will be informed of the addition, removal, or + * change of display groups. + * + * @param listener The listener to unregister. + */ + public abstract void unregisterDisplayGroupListener(DisplayGroupListener listener); /** * Screenshot for internal system-only use such as rotation, etc. This method includes @@ -451,7 +463,7 @@ public abstract class DisplayManagerInternal { void onStateChanged(); void onProximityPositive(); void onProximityNegative(); - void onDisplayStateChange(int state); // one of the Display state constants + void onDisplayStateChange(boolean allInactive, boolean allOff); void acquireSuspendBlocker(); void releaseSuspendBlocker(); @@ -465,4 +477,33 @@ public abstract class DisplayManagerInternal { public interface DisplayTransactionListener { void onDisplayTransaction(Transaction t); } + + /** + * Called when there are changes to {@link com.android.server.display.DisplayGroup + * DisplayGroups}. + */ + public interface DisplayGroupListener { + /** + * A new display group with the provided {@code groupId} was added. + * This is guaranteed to be called <i>before</i> any corresponding calls to + * {@link android.hardware.display.DisplayManager.DisplayListener} are made. + */ + void onDisplayGroupAdded(int groupId); + + /** + * The display group with the provided {@code groupId} was removed. + * + * This is guaranteed to be called <i>after</i> any corresponding calls to + * {@link android.hardware.display.DisplayManager.DisplayListener} are made. + */ + void onDisplayGroupRemoved(int groupId); + + /** + * The display group with the provided {@code groupId} has changed. + * + * This is guaranteed to be called <i>after</i> any corresponding calls to + * {@link android.hardware.display.DisplayManager.DisplayListener} are made. + */ + void onDisplayGroupChanged(int groupId); + } } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index d11f3ce0d112..cbb3ba90fb03 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -416,9 +416,21 @@ public final class PowerManager { public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10; /** + * Go to sleep reason code: The last powered on display group has been removed. * @hide */ - public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT; + public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED = 11; + + /** + * Go to sleep reason code: Every display group has been turned off. + * @hide + */ + public static final int GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF = 12; + + /** + * @hide + */ + public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; /** * @hide @@ -435,6 +447,8 @@ public final class PowerManager { case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility"; case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend"; 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"; default: return Integer.toString(sleepReason); } } @@ -521,6 +535,8 @@ public final class PowerManager { WAKE_REASON_WAKE_KEY, WAKE_REASON_WAKE_MOTION, WAKE_REASON_HDMI, + WAKE_REASON_DISPLAY_GROUP_ADDED, + WAKE_REASON_DISPLAY_GROUP_TURNED_ON, }) @Retention(RetentionPolicy.SOURCE) public @interface WakeReason{} @@ -608,6 +624,18 @@ public final class PowerManager { public static final int WAKE_REASON_LID = 9; /** + * Wake up reason code: Waking due to display group being added. + * @hide + */ + public static final int WAKE_REASON_DISPLAY_GROUP_ADDED = 10; + + /** + * Wake up reason code: Waking due to display group being powered on. + * @hide + */ + public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11; + + /** * Convert the wake reason to a string for debugging purposes. * @hide */ @@ -623,6 +651,8 @@ public final class PowerManager { case WAKE_REASON_WAKE_MOTION: return "WAKE_REASON_WAKE_MOTION"; case WAKE_REASON_HDMI: return "WAKE_REASON_HDMI"; case WAKE_REASON_LID: return "WAKE_REASON_LID"; + case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED"; + case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON"; default: return Integer.toString(wakeReason); } } @@ -1214,8 +1244,15 @@ public final class PowerManager { } } - /** - * Forces the device to go to sleep. + /** + * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} + * to turn off. + * + * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is + * turned on it will be turned off. If all displays are off as a result of this action the + * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT + * default display group} is already off then nothing will happen. + * * <p> * Overrides all the wake locks that are held. * This is what happens when the power key is pressed to turn off the screen. @@ -1238,7 +1275,14 @@ public final class PowerManager { } /** - * Forces the device to go to sleep. + * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} + * to turn off. + * + * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is + * turned on it will be turned off. If all displays are off as a result of this action the + * device will be put to sleep. If the {@link com.android.server.display.DisplayGroup#DEFAULT + * default display group} is already off then nothing will happen. + * * <p> * Overrides all the wake locks that are held. * This is what happens when the power key is pressed to turn off the screen. @@ -1268,9 +1312,15 @@ public final class PowerManager { } /** - * Forces the device to wake up from sleep. + * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} + * to turn on. + * + * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is + * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If + * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already + * on then nothing will happen. + * * <p> - * If the device is currently asleep, wakes it up, otherwise does nothing. * This is what happens when the power key is pressed to turn on the screen. * </p><p> * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. @@ -1293,9 +1343,15 @@ public final class PowerManager { } /** - * Forces the device to wake up from sleep. + * Forces the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} + * to turn on. + * + * <p>If the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is + * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If + * the {@link com.android.server.display.DisplayGroup#DEFAULT default display group} is already + * on then nothing will happen. + * * <p> - * If the device is currently asleep, wakes it up, otherwise does nothing. * This is what happens when the power key is pressed to turn on the screen. * </p><p> * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. @@ -1322,9 +1378,13 @@ public final class PowerManager { } /** - * Forces the device to wake up from sleep. + * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on. + * + * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will + * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link + * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen. + * * <p> - * If the device is currently asleep, wakes it up, otherwise does nothing. * This is what happens when the power key is pressed to turn on the screen. * </p><p> * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java index 2ba875813734..1b5065ad89e4 100644 --- a/services/core/java/com/android/server/display/DisplayGroup.java +++ b/services/core/java/com/android/server/display/DisplayGroup.java @@ -21,7 +21,8 @@ import java.util.List; /** * Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and - * operations. + * operations; particularly display-state. + * * @hide */ public class DisplayGroup { @@ -35,17 +36,44 @@ public class DisplayGroup { mGroupId = groupId; } + /** Returns the identifier for the Group. */ int getGroupId() { return mGroupId; } - void addDisplay(LogicalDisplay display) { + /** + * Adds the provided {@code display} to the Group + * + * @param display the {@link LogicalDisplay} to add to the Group + */ + void addDisplayLocked(LogicalDisplay display) { if (!mDisplays.contains(display)) { mDisplays.add(display); } } - boolean removeDisplay(LogicalDisplay display) { + /** + * Removes the provided {@code display} from the Group. + * + * @param display The {@link LogicalDisplay} to remove from the Group. + * @return {@code true} if the {@code display} was removed; otherwise {@code false} + */ + boolean removeDisplayLocked(LogicalDisplay display) { return mDisplays.remove(display); } + + /** Returns {@code true} if there are no {@link LogicalDisplay LogicalDisplays} in the Group. */ + boolean isEmptyLocked() { + return mDisplays.isEmpty(); + } + + /** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */ + int getSizeLocked() { + return mDisplays.size(); + } + + /** Returns the ID of the {@link LogicalDisplay} at the provided {@code index}. */ + int getIdLocked(int index) { + return mDisplays.get(index).getDisplayIdLocked(); + } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 55103ca6cd1c..bb4c9dda2195 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -57,6 +57,7 @@ import android.hardware.display.Curve; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; @@ -190,6 +191,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_UPDATE_VIEWPORT = 5; private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6; private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; + private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8; private final Context mContext; private final DisplayManagerHandler mHandler; @@ -237,36 +239,54 @@ public final class DisplayManagerService extends SystemService { private final CopyOnWriteArrayList<DisplayTransactionListener> mDisplayTransactionListeners = new CopyOnWriteArrayList<DisplayTransactionListener>(); + /** List of all display group listeners. */ + private final CopyOnWriteArrayList<DisplayGroupListener> mDisplayGroupListeners = + new CopyOnWriteArrayList<>(); + /** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */ private final SparseArray<DisplayPowerController> mDisplayPowerControllers = new SparseArray<>(); /** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */ private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() { + // Synchronized to avoid race conditions when updating multiple display states. @Override - public void requestDisplayState(int displayId, int state, float brightness) { - // TODO (b/168210494): Stop applying default display state to all displays. - if (displayId != Display.DEFAULT_DISPLAY) { - return; - } - final int[] displayIds; + public synchronized void requestDisplayState(int displayId, int state, float brightness) { + boolean allInactive = true; + boolean allOff = true; + final boolean stateChanged; synchronized (mSyncRoot) { - displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(); + final int index = mDisplayStates.indexOfKey(displayId); + final int newState = mDisplayStates.valueAt(index); + stateChanged = index == -1 || state != newState; + if (stateChanged) { + final int size = mDisplayStates.size(); + for (int i = 0; i < size; i++) { + final int displayState = i == index ? newState : state; + if (displayState != Display.STATE_OFF) { + allOff = false; + } + if (Display.isActiveState(displayState)) { + allInactive = false; + } + if (!allOff && !allInactive) { + break; + } + } + } } // The order of operations is important for legacy reasons. if (state == Display.STATE_OFF) { - for (int id : displayIds) { - requestDisplayStateInternal(id, state, brightness); - } + requestDisplayStateInternal(displayId, state, brightness); } - mDisplayPowerCallbacks.onDisplayStateChange(state); + if (stateChanged) { + mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff); + } if (state != Display.STATE_OFF) { - for (int id : displayIds) { - requestDisplayStateInternal(id, state, brightness); - } + requestDisplayStateInternal(displayId, state, brightness); } } }; @@ -1152,7 +1172,7 @@ public final class DisplayManagerService extends SystemService { private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) { final int displayId = display.getDisplayIdLocked(); - mDisplayPowerControllers.delete(displayId); + mDisplayPowerControllers.removeReturnOld(displayId).stop(); mDisplayStates.delete(displayId); mDisplayBrightnesses.delete(displayId); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); @@ -1669,6 +1689,11 @@ public final class DisplayManagerService extends SystemService { mHandler.sendMessage(msg); } + private void sendDisplayGroupEvent(int groupId, int event) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event); + mHandler.sendMessage(msg); + } + private void sendDisplayEventFrameRateOverrideLocked(int displayId) { Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE, displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); @@ -1713,6 +1738,35 @@ public final class DisplayManagerService extends SystemService { mTempCallbacks.clear(); } + // Runs on Handler thread. + // Delivers display group event notifications to callbacks. + private void deliverDisplayGroupEvent(int groupId, int event) { + if (DEBUG) { + Slog.d(TAG, "Delivering display group event: groupId=" + groupId + ", event=" + + event); + } + + switch (event) { + case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED: + for (DisplayGroupListener listener : mDisplayGroupListeners) { + listener.onDisplayGroupAdded(groupId); + } + break; + + case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED: + for (DisplayGroupListener listener : mDisplayGroupListeners) { + listener.onDisplayGroupChanged(groupId); + } + break; + + case LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED: + for (DisplayGroupListener listener : mDisplayGroupListeners) { + listener.onDisplayGroupRemoved(groupId); + } + break; + } + } + private IMediaProjectionManager getProjectionService() { if (mProjectionService == null) { IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE); @@ -1935,6 +1989,11 @@ public final class DisplayManagerService extends SystemService { } deliverDisplayEvent(msg.arg1, uids, msg.arg2); break; + + case MSG_DELIVER_DISPLAY_GROUP_EVENT: + deliverDisplayGroupEvent(msg.arg1, msg.arg2); + break; + } } } @@ -1966,6 +2025,11 @@ public final class DisplayManagerService extends SystemService { } @Override + public void onDisplayGroupEventLocked(int groupId, int event) { + sendDisplayGroupEvent(groupId, event); + } + + @Override public void onTraversalRequested() { synchronized (mSyncRoot) { scheduleTraversalLocked(false); @@ -2700,11 +2764,25 @@ public final class DisplayManagerService extends SystemService { } @Override - public boolean requestPowerState(DisplayPowerRequest request, + public boolean requestPowerState(int groupId, DisplayPowerRequest request, boolean waitForNegativeProximity) { synchronized (mSyncRoot) { - return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY) - .requestPowerState(request, waitForNegativeProximity); + final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked( + groupId); + if (displayGroup == null) { + return true; + } + + final int size = displayGroup.getSizeLocked(); + boolean ready = true; + for (int i = 0; i < size; i++) { + final DisplayPowerController displayPowerController = + mDisplayPowerControllers.get(displayGroup.getIdLocked(i)); + ready &= displayPowerController.requestPowerState(request, + waitForNegativeProximity); + } + + return ready; } } @@ -2717,10 +2795,13 @@ public final class DisplayManagerService extends SystemService { } @Override - public int getDisplayGroupId(int displayId) { - synchronized (mSyncRoot) { - return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId); - } + public void registerDisplayGroupListener(DisplayGroupListener listener) { + mDisplayGroupListeners.add(listener); + } + + @Override + public void unregisterDisplayGroupListener(DisplayGroupListener listener) { + mDisplayGroupListeners.remove(listener); } @Override diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 811625b06cf0..5c795cdbddc6 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -683,6 +683,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // TODO: b/175821789 - Support high brightness on multiple (folding) displays } + /** + * Unregisters all listeners and interrupts all running threads; halting future work. + * + * This method should be called when the DisplayPowerController is no longer in use; i.e. when + * the {@link #mDisplayId display} has been removed. + */ + public void stop() { + mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); + mPowerState.stop(); + } + private void sendUpdatePowerState() { synchronized (mLock) { sendUpdatePowerStateLocked(); diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 54f30a954c33..51ba065f601c 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -72,6 +72,8 @@ final class DisplayPowerState { private Runnable mCleanListener; + private volatile boolean mStopped; + public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) { mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); @@ -263,6 +265,18 @@ final class DisplayPowerState { } } + /** + * Interrupts all running threads; halting future work. + * + * This method should be called when the DisplayPowerState is no longer in use; i.e. when + * the {@link #mDisplayId display} has been removed. + */ + public void stop() { + mHandler.removeCallbacksAndMessages(null); + mStopped = true; + mPhotonicModulator.interrupt(); + } + public void dump(PrintWriter pw) { pw.println(); pw.println("Display Power State:"); @@ -427,7 +441,11 @@ final class DisplayPowerState { if (!stateChanged && !backlightChanged) { try { mLock.wait(); - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + if (mStopped) { + return; + } + } continue; } mActualState = state; diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index bb2fbed354aa..ecb837da458b 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -17,7 +17,6 @@ package com.android.server.display; import android.content.Context; -import android.os.Process; import android.os.SystemProperties; import android.text.TextUtils; import android.util.IndentingPrintWriter; @@ -55,6 +54,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4; public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5; + public static final int DISPLAY_GROUP_EVENT_ADDED = 1; + public static final int DISPLAY_GROUP_EVENT_CHANGED = 2; + public static final int DISPLAY_GROUP_EVENT_REMOVED = 3; + /** * Temporary display info, used for comparing display configurations. */ @@ -99,7 +102,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1; /** A mapping from logical display id to display group. */ - private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>(); + private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>(); private final DisplayDeviceRepository mDisplayDeviceRepo; private final Listener mListener; @@ -154,10 +157,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return null; } - public int[] getDisplayIdsLocked() { - return getDisplayIdsLocked(Process.SYSTEM_UID); - } - public int[] getDisplayIdsLocked(int callingUid) { final int count = mLogicalDisplays.size(); int[] displayIds = new int[count]; @@ -182,13 +181,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } - public int getDisplayGroupIdLocked(int displayId) { - final DisplayGroup displayGroup = mDisplayGroups.get(displayId); - if (displayGroup != null) { - return displayGroup.getGroupId(); + public DisplayGroup getDisplayGroupLocked(int groupId) { + final int size = mDisplayIdToGroupMap.size(); + for (int i = 0; i < size; i++) { + final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i); + if (displayGroup.getGroupId() == groupId) { + return displayGroup; + } } - return -1; + return null; } public void dumpLocked(PrintWriter pw) { @@ -325,17 +327,31 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mLogicalDisplays.put(displayId, display); final DisplayGroup displayGroup; - if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) { + final boolean addNewDisplayGroup = + isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0; + if (addNewDisplayGroup) { final int groupId = assignDisplayGroupIdLocked(isDefault); displayGroup = new DisplayGroup(groupId); } else { - displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY); + displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY); + } + displayGroup.addDisplayLocked(display); + mDisplayIdToGroupMap.append(displayId, displayGroup); + + if (addNewDisplayGroup) { + // Group added events happen before Logical Display added events. + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED); } - displayGroup.addDisplay(display); - mDisplayGroups.append(displayId, displayGroup); mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED); + + if (!addNewDisplayGroup) { + // Group changed events happen after Logical Display added events. + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED); + } } /** @@ -352,31 +368,45 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides = display.getFrameRateOverrides(); display.updateLocked(mDisplayDeviceRepo); + final DisplayGroup changedDisplayGroup; if (!display.isValidLocked()) { mLogicalDisplays.removeAt(i); - mDisplayGroups.removeReturnOld(displayId).removeDisplay(display); + final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId); + displayGroup.removeDisplayLocked(display); mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED); + + changedDisplayGroup = displayGroup; } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) { final int flags = display.getDisplayInfoLocked().flags; - final DisplayGroup defaultDisplayGroup = mDisplayGroups.get( + final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get( Display.DEFAULT_DISPLAY); if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) { // The display should have its own DisplayGroup. - if (defaultDisplayGroup.removeDisplay(display)) { + if (defaultDisplayGroup.removeDisplayLocked(display)) { final int groupId = assignDisplayGroupIdLocked(false); final DisplayGroup displayGroup = new DisplayGroup(groupId); - displayGroup.addDisplay(display); - mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup); + displayGroup.addDisplayLocked(display); + mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup); + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED); + changedDisplayGroup = defaultDisplayGroup; + } else { + changedDisplayGroup = null; } } else { // The display should be a part of the default DisplayGroup. - final DisplayGroup displayGroup = mDisplayGroups.get(displayId); + final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId); if (displayGroup != defaultDisplayGroup) { - displayGroup.removeDisplay(display); - defaultDisplayGroup.addDisplay(display); - mDisplayGroups.put(displayId, defaultDisplayGroup); + displayGroup.removeDisplayLocked(display); + defaultDisplayGroup.addDisplayLocked(display); + mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), + LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED); + mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup); + changedDisplayGroup = displayGroup; + } else { + changedDisplayGroup = null; } } @@ -388,6 +418,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) { mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED); + changedDisplayGroup = null; } else { // While applications shouldn't know nor care about the non-overridden info, we // still need to let WindowManager know so it can update its own internal state for @@ -397,6 +428,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED); } + changedDisplayGroup = null; + } + + // CHANGED and REMOVED DisplayGroup events should always fire after Display events. + if (changedDisplayGroup != null) { + final int event = changedDisplayGroup.isEmptyLocked() + ? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED + : LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED; + mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event); } } } @@ -432,6 +472,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); + void onDisplayGroupEventLocked(int groupId, int event); void onTraversalRequested(); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4c344c519089..69484b1e961d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -447,8 +447,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile int mPowerKeyPressCounter; volatile boolean mEndCallKeyHandled; volatile boolean mCameraGestureTriggeredDuringGoingToSleep; - volatile boolean mGoingToSleep; - volatile boolean mRequestedOrGoingToSleep; + + /** + * {@code true} if the device is entering a low-power state; {@code false otherwise}. + * + * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state + * of the {@link #mDefaultDisplay default display} versus the power state of the entire device. + */ + volatile boolean mDeviceGoingToSleep; + + /** + * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to + * enter a low-power state; {@code false otherwise}. + * + * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire + * device versus the power state of the {@link #mDefaultDisplay default display}. + */ + // TODO(b/178103325): Track sleep/requested sleep for every display. + volatile boolean mRequestedOrSleepingDefaultDisplay; + volatile boolean mRecentsVisible; volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true; volatile boolean mPictureInPictureVisible; @@ -916,7 +933,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (gestureService != null) { gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, mTmpBoolean); - if (mTmpBoolean.value && mRequestedOrGoingToSleep) { + if (mTmpBoolean.value && mRequestedOrSleepingDefaultDisplay) { mCameraGestureTriggeredDuringGoingToSleep = true; } } @@ -1063,13 +1080,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { case SHORT_PRESS_POWER_NOTHING: break; case SHORT_PRESS_POWER_GO_TO_SLEEP: - goToSleepFromPowerButton(eventTime, 0); + sleepDefaultDisplayFromPowerButton(eventTime, 0); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: - goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); + sleepDefaultDisplayFromPowerButton(eventTime, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: - if (goToSleepFromPowerButton(eventTime, + if (sleepDefaultDisplayFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) { launchHomeFromHotKey(DEFAULT_DISPLAY); } @@ -1097,11 +1115,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * Sends the device to sleep as a result of a power button press. + * Sends the default display to sleep as a result of a power button press. * - * @return True if the was device was sent to sleep, false if sleep was suppressed. + * @return True if the device was sent to sleep, false if the device did not sleep. */ - private boolean goToSleepFromPowerButton(long eventTime, int flags) { + private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) { // Before we actually go to sleep, we check the last wakeup reason. // If the device very recently woke up from a gesture (like user lifting their device) // then ignore the sleep instruction. This is because users have developed @@ -1121,12 +1139,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags); + sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags); return true; } - private void goToSleep(long eventTime, int reason, int flags) { - mRequestedOrGoingToSleep = true; + private void sleepDefaultDisplay(long eventTime, int reason, int flags) { + mRequestedOrSleepingDefaultDisplay = true; mPowerManager.goToSleep(eventTime, reason, flags); } @@ -1163,7 +1181,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.THEATER_MODE_ON, 1); if (mGoToSleepOnButtonPressTheaterMode && interactive) { - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); + sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, + 0); } } break; @@ -1271,7 +1290,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case SHORT_PRESS_SLEEP_GO_TO_SLEEP: case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME: Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)"); - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0); + sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0); break; } } @@ -3686,7 +3705,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if ((mEndcallBehavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { - goToSleep(event.getEventTime(), + sleepDefaultDisplay(event.getEventTime(), PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); isWakeKey = false; } @@ -3729,10 +3748,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Any activity on the power button stops the accessibility shortcut result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately + final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState()); + final boolean interactiveAndOn = interactive && isDefaultDisplayOn; if (down) { - interceptPowerKeyDown(event, interactive); + interceptPowerKeyDown(event, interactiveAndOn); } else { - interceptPowerKeyUp(event, interactive, canceled); + interceptPowerKeyUp(event, interactiveAndOn, canceled); } break; } @@ -4242,8 +4263,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pmSleepReason)) + ")"); } - mGoingToSleep = true; - mRequestedOrGoingToSleep = true; + mDeviceGoingToSleep = true; if (mKeyguardDelegate != null) { mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason); @@ -4262,8 +4282,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000); - mGoingToSleep = false; - mRequestedOrGoingToSleep = false; + mDeviceGoingToSleep = false; mDefaultDisplayPolicy.setAwake(false); // We must get this work done here because the power manager will drop @@ -4413,6 +4432,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off..."); + mRequestedOrSleepingDefaultDisplay = false; updateScreenOffSleepToken(true); mDefaultDisplayPolicy.screenTurnedOff(); synchronized (mLock) { @@ -4479,6 +4499,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } + mRequestedOrSleepingDefaultDisplay = true; mWindowManagerFuncs.screenTurningOff(screenOffListener); synchronized (mLock) { if (mKeyguardDelegate != null) { @@ -4564,7 +4585,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean okToAnimate() { - return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep; + return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep; } /** {@inheritDoc} */ @@ -4940,7 +4961,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerFuncs.lockDeviceNow(); break; case LID_BEHAVIOR_SLEEP: - goToSleep(SystemClock.uptimeMillis(), + sleepDefaultDisplay(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java new file mode 100644 index 000000000000..8ebeea3be48f --- /dev/null +++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; +import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; +import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; +import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; + +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED; +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_CHANGED; +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED; + +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.Handler; +import android.os.PowerManagerInternal; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.server.display.DisplayGroup; + +/** + * Responsible for creating {@link DisplayPowerRequest}s and associating them with + * {@link com.android.server.display.DisplayGroup}s. + * + * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} + * which is used to request power state changes to every display in the group. + */ +public class DisplayGroupPowerStateMapper { + + private static final String TAG = "DisplayPowerRequestMapper"; + + /** Lock obtained from {@link PowerManagerService}. */ + private final Object mLock; + + /** Listener to inform of changes to display groups. */ + private final DisplayGroupPowerChangeListener mListener; + + /** A mapping from DisplayGroup Id to DisplayGroup information. */ + @GuardedBy("mLock") + private final SparseArray<DisplayGroupInfo> mDisplayGroupInfos = new SparseArray<>(); + + /** A cached array of DisplayGroup Ids. */ + @GuardedBy("mLock") + private int[] mDisplayGroupIds; + + private final DisplayManagerInternal.DisplayGroupListener mDisplayGroupListener = + new DisplayManagerInternal.DisplayGroupListener() { + @Override + public void onDisplayGroupAdded(int groupId) { + synchronized (mLock) { + if (mDisplayGroupInfos.contains(groupId)) { + Slog.e(TAG, "Tried to add already existing group:" + groupId); + return; + } + // For now, only the default group supports sandman. + final boolean supportsSandman = groupId == DisplayGroup.DEFAULT; + final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo( + new DisplayPowerRequest(), + getGlobalWakefulnessLocked(), /* ready= */ false, + supportsSandman); + mDisplayGroupInfos.append(groupId, displayGroupInfo); + mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId); + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId); + } + } + + @Override + public void onDisplayGroupRemoved(int groupId) { + synchronized (mLock) { + if (!mDisplayGroupInfos.contains(groupId)) { + Slog.e(TAG, "Tried to remove non-existent group:" + groupId); + return; + } + mDisplayGroupInfos.delete(groupId); + mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId); + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId); + } + } + + @Override + public void onDisplayGroupChanged(int groupId) { + synchronized (mLock) { + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId); + } + } + }; + + DisplayGroupPowerStateMapper(Object lock, DisplayManagerInternal displayManagerInternal, + DisplayGroupPowerChangeListener listener) { + mLock = lock; + mListener = listener; + displayManagerInternal.registerDisplayGroupListener(mDisplayGroupListener); + + final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo( + new DisplayPowerRequest(), WAKEFULNESS_AWAKE, /* ready= */ + false, /* supportsSandman= */ true); + mDisplayGroupInfos.append(DisplayGroup.DEFAULT, displayGroupInfo); + mDisplayGroupIds = new int[]{DisplayGroup.DEFAULT}; + } + + DisplayPowerRequest getPowerRequestLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).displayPowerRequest; + } + + int[] getDisplayGroupIdsLocked() { + return mDisplayGroupIds; + } + + int getWakefulnessLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).wakefulness; + } + + void setLastPowerOnTimeLocked(int groupId, long eventTime) { + mDisplayGroupInfos.get(groupId).lastPowerOnTime = eventTime; + } + + long getLastPowerOnTimeLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).lastPowerOnTime; + } + + /** + * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}. + * + * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered + * from highest to lowest: + * <ol> + * <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE} + * <li>{@link PowerManagerInternal#WAKEFULNESS_DREAMING} + * <li>{@link PowerManagerInternal#WAKEFULNESS_DOZING} + * <li>{@link PowerManagerInternal#WAKEFULNESS_ASLEEP} + * </ol> + */ + int getGlobalWakefulnessLocked() { + final int size = mDisplayGroupInfos.size(); + int deviceWakefulness = WAKEFULNESS_ASLEEP; + for (int i = 0; i < size; i++) { + final int wakefulness = mDisplayGroupInfos.valueAt(i).wakefulness; + if (wakefulness == WAKEFULNESS_AWAKE) { + return WAKEFULNESS_AWAKE; + } else if (wakefulness == WAKEFULNESS_DREAMING + && (deviceWakefulness == WAKEFULNESS_ASLEEP + || deviceWakefulness == WAKEFULNESS_DOZING)) { + deviceWakefulness = WAKEFULNESS_DREAMING; + } else if (wakefulness == WAKEFULNESS_DOZING + && deviceWakefulness == WAKEFULNESS_ASLEEP) { + deviceWakefulness = WAKEFULNESS_DOZING; + } + } + + return deviceWakefulness; + } + + /** + * Sets the {@code wakefulness} value for the {@link DisplayGroup} specified by the provided + * {@code groupId}. + * + * @return {@code true} if the wakefulness value was changed; {@code false} otherwise. + */ + boolean setWakefulnessLocked(int groupId, int wakefulness) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + if (displayGroupInfo.wakefulness != wakefulness) { + displayGroupInfo.wakefulness = wakefulness; + return true; + } + + return false; + } + + boolean isSandmanSummoned(int groupId) { + return mDisplayGroupInfos.get(groupId).sandmanSummoned; + } + + boolean isSandmanSupported(int groupId) { + return mDisplayGroupInfos.get(groupId).supportsSandman; + } + + /** + * Sets whether or not the sandman is summoned for the given {@code groupId}. + * + * @param groupId Signifies the DisplayGroup for which to summon or unsummon the + * sandman. + * @param sandmanSummoned {@code true} to summon the sandman; {@code false} to unsummon. + */ + void setSandmanSummoned(int groupId, boolean sandmanSummoned) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + displayGroupInfo.sandmanSummoned = displayGroupInfo.supportsSandman && sandmanSummoned; + } + + /** + * Returns {@code true} if every display in the specified group has its requested state matching + * its actual state. + * + * @param groupId The identifier for the display group to check for readiness. + */ + boolean isReady(int groupId) { + return mDisplayGroupInfos.get(groupId).ready; + } + + /** Returns {@code true} if every display has its requested state matching its actual state. */ + boolean areAllDisplaysReadyLocked() { + final int size = mDisplayGroupInfos.size(); + for (int i = 0; i < size; i++) { + if (!mDisplayGroupInfos.valueAt(i).ready) { + return false; + } + } + + return true; + } + + /** + * Sets whether the displays specified by the provided {@code groupId} are all ready. + * + * <p>A display is ready if its reported + * {@link DisplayManagerInternal.DisplayPowerCallbacks#onStateChanged() actual state} matches + * its {@link DisplayManagerInternal#requestPowerState requested state}. + * + * @param groupId The identifier for the display group. + * @param ready {@code true} if every display in the group is ready; otherwise {@code false}. + * @return {@code true} if the ready state changed; otherwise {@code false}. + */ + boolean setDisplayGroupReadyLocked(int groupId, boolean ready) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + if (displayGroupInfo.ready != ready) { + displayGroupInfo.ready = ready; + return true; + } + + return false; + } + + /** + * Interface through which an interested party may be informed of {@link DisplayGroup} events. + */ + interface DisplayGroupPowerChangeListener { + int DISPLAY_GROUP_ADDED = 0; + int DISPLAY_GROUP_REMOVED = 1; + int DISPLAY_GROUP_CHANGED = 2; + + void onDisplayGroupEventLocked(int event, int groupId); + } + + private static final class DisplayGroupInfo { + final DisplayPowerRequest displayPowerRequest; + int wakefulness; + boolean ready; + long lastPowerOnTime; + boolean sandmanSummoned; + + /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */ + boolean supportsSandman; + + DisplayGroupInfo(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready, + boolean supportsSandman) { + this.displayPowerRequest = displayPowerRequest; + this.wakefulness = wakefulness; + this.ready = ready; + this.supportsSandman = supportsSandman; + } + } +} diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java deleted file mode 100644 index 6477552eb550..000000000000 --- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.power; - -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.os.Handler; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.Display; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.display.DisplayGroup; - -/** - * Responsible for creating {@link DisplayPowerRequest}s and associating them with - * {@link com.android.server.display.DisplayGroup}s. - * - * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} - * which is used to request power state changes to every display in the group. - */ -class DisplayPowerRequestMapper { - - private final Object mLock = new Object(); - - /** A mapping from LogicalDisplay Id to DisplayGroup Id. */ - @GuardedBy("mLock") - private final SparseIntArray mDisplayGroupIds = new SparseIntArray(); - - /** A mapping from DisplayGroup Id to DisplayPowerRequest. */ - @GuardedBy("mLock") - private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>(); - - private final DisplayManagerInternal mDisplayManagerInternal; - - private final DisplayManager.DisplayListener mDisplayListener = - new DisplayManager.DisplayListener() { - - @Override - public void onDisplayAdded(int displayId) { - synchronized (mLock) { - if (mDisplayGroupIds.indexOfKey(displayId) >= 0) { - return; - } - final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId( - displayId); - if (!mDisplayPowerRequests.contains(displayGroupId)) { - // A new DisplayGroup was created; create a new DisplayPowerRequest. - mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest()); - } - mDisplayGroupIds.append(displayId, displayGroupId); - } - } - - @Override - public void onDisplayRemoved(int displayId) { - synchronized (mLock) { - final int index = mDisplayGroupIds.indexOfKey(displayId); - if (index < 0) { - return; - } - final int displayGroupId = mDisplayGroupIds.valueAt(index); - mDisplayGroupIds.removeAt(index); - - if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) { - // The DisplayGroup no longer exists; delete the DisplayPowerRequest. - mDisplayPowerRequests.delete(displayGroupId); - } - } - } - - @Override - public void onDisplayChanged(int displayId) { - synchronized (mLock) { - final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId( - displayId); - final int oldDisplayGroupId = mDisplayGroupIds.get(displayId); - - if (!mDisplayPowerRequests.contains(newDisplayGroupId)) { - // A new DisplayGroup was created; create a new DisplayPowerRequest. - mDisplayPowerRequests.append(newDisplayGroupId, - new DisplayPowerRequest()); - } - mDisplayGroupIds.put(displayId, newDisplayGroupId); - - if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) { - // The DisplayGroup no longer exists; delete the DisplayPowerRequest. - mDisplayPowerRequests.delete(oldDisplayGroupId); - } - } - } - }; - - DisplayPowerRequestMapper(DisplayManager displayManager, - DisplayManagerInternal displayManagerInternal, Handler handler) { - mDisplayManagerInternal = displayManagerInternal; - displayManager.registerDisplayListener(mDisplayListener, handler); - mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest()); - mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT); - } - - DisplayPowerRequest get(int displayId) { - synchronized (mLock) { - return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId)); - } - } -} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 084dc32e8ad7..f14ff3cc3268 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -18,6 +18,10 @@ package com.android.server.power; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; +import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED; +import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON; import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE; import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; @@ -42,7 +46,6 @@ import android.database.ContentObserver; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.Boost; @@ -107,6 +110,7 @@ import com.android.server.UiThread; import com.android.server.UserspaceRebootLogger; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; +import com.android.server.display.DisplayGroup; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; import com.android.server.policy.WindowManagerPolicy; @@ -176,6 +180,8 @@ public final class PowerManagerService extends SystemService private static final int DIRTY_VR_MODE_CHANGED = 1 << 13; // Dirty bit: attentive timer may have timed out private static final int DIRTY_ATTENTIVE = 1 << 14; + // Dirty bit: display group power state has changed + private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 15; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -297,10 +303,6 @@ public final class PowerManagerService extends SystemService private int mWakefulnessRaw; private boolean mWakefulnessChanging; - // True if the sandman has just been summoned for the first time since entering the - // dreaming or dozing state. Indicates whether a new dream should begin. - private boolean mSandmanSummoned; - // True if MSG_SANDMAN has been scheduled. private boolean mSandmanScheduled; @@ -351,11 +353,7 @@ public final class PowerManagerService extends SystemService // Manages the desired power state of displays. The actual state may lag behind the // requested because it is updated asynchronously by the display power controller. - private DisplayPowerRequestMapper mDisplayPowerRequestMapper; - - // True if the display power state has been fully applied, which means the display - // is actually on or actually off or whatever was requested. - private boolean mDisplayReady; + private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper; // The suspend blocker used to keep the CPU alive when an application has acquired // a wake lock. @@ -625,6 +623,39 @@ public final class PowerManagerService extends SystemService // but the DreamService has not yet been told to start (it's an async process). private boolean mDozeStartInProgress; + private final class DisplayGroupPowerChangeListener implements + DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener { + @Override + public void onDisplayGroupEventLocked(int event, int groupId) { + final int oldWakefulness = getWakefulnessLocked(); + final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(); + if (oldWakefulness != newWakefulness) { + final int reason; + switch (newWakefulness) { + case WAKEFULNESS_AWAKE: + reason = event == DISPLAY_GROUP_ADDED ? WAKE_REASON_DISPLAY_GROUP_ADDED + : WAKE_REASON_DISPLAY_GROUP_TURNED_ON; + break; + case WAKEFULNESS_DOZING: + reason = event == DISPLAY_GROUP_REMOVED + ? GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED + : GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; + break; + default: + reason = 0; + } + + setGlobalWakefulnessLocked( + mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(), + mClock.uptimeMillis(), reason, Process.SYSTEM_UID, Process.SYSTEM_UID, + mContext.getOpPackageName(), "groupId: " + groupId); + } + + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; + updatePowerStateLocked(); + } + } + private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver { @Override public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException { @@ -868,6 +899,12 @@ public final class PowerManagerService extends SystemService void invalidateIsInteractiveCaches() { PowerManager.invalidateIsInteractiveCaches(); } + + DisplayGroupPowerStateMapper createDisplayPowerRequestMapper(Object lock, + DisplayManagerInternal displayManagerInternal, + DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener listener) { + return new DisplayGroupPowerStateMapper(lock, displayManagerInternal, listener); + } } final Constants mConstants; @@ -1037,7 +1074,7 @@ public final class PowerManagerService extends SystemService now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); if (sQuiescent) { - goToSleepNoUpdateLocked(mClock.uptimeMillis(), + sleepDisplayGroupNoUpdateLocked(DisplayGroup.DEFAULT, mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } @@ -1055,8 +1092,8 @@ public final class PowerManagerService extends SystemService mPolicy = getLocalService(WindowManagerPolicy.class); mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class); mAttentionDetector.systemReady(mContext); - mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService( - DisplayManager.class), mDisplayManagerInternal, mHandler); + mDisplayGroupPowerStateMapper = mInjector.createDisplayPowerRequestMapper(mLock, + mDisplayManagerInternal, new DisplayGroupPowerChangeListener()); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -1373,9 +1410,11 @@ public final class PowerManagerService extends SystemService opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; } - wakeUpNoUpdateLocked(mClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, - opUid, opPackageName, opUid); + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), + PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, + opUid, opPackageName, opUid); + } } } @@ -1664,74 +1703,69 @@ public final class PowerManagerService extends SystemService } } - private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid, - String opPackageName, int opUid) { + private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason, + String details, int uid, String opPackageName, int opUid) { synchronized (mLock) { - if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) { + if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid, + opPackageName, opUid)) { updatePowerStateLocked(); } } } - private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details, - int reasonUid, String opPackageName, int opUid) { + private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime, + @WakeReason int reason, String details, int uid, String opPackageName, int opUid) { if (DEBUG_SPEW) { - Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); + Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime + +", groupId=" + groupId + ", uid=" + uid); } - if (eventTime < mLastSleepTime || getWakefulnessLocked() == WAKEFULNESS_AWAKE - || mForceSuspendActive || !mSystemReady) { + if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) { return false; } - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); + final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (currentState == WAKEFULNESS_AWAKE) { + return false; + } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay"); try { - Slog.i(TAG, "Waking up from " - + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked()) - + " (uid=" + reasonUid + Slog.i(TAG, "Powering on display group from" + + PowerManagerInternal.wakefulnessToString(currentState) + + " (groupId=" + groupId + + ", uid=" + uid + ", reason=" + PowerManager.wakeReasonToString(reason) + ", details=" + details + ")..."); + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); - mLastWakeTime = eventTime; - mLastWakeReason = reason; - setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime); - - mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid); - userActivityNoUpdateLocked( - eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); - - if (sQuiescent) { - mDirty |= DIRTY_QUIESCENT; - } + setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid, + opPackageName, details); + mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } + return true; } - private void goToSleepInternal(long eventTime, int reason, int flags, int uid) { + private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags, + int uid) { synchronized (mLock) { - if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) { + if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) { updatePowerStateLocked(); } } } - /** - * Puts the system in doze. - * - * This method is called goToSleep for historical reasons but actually attempts to DOZE, - * and only tucks itself in to SLEEP if requested with the flag - * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}. - */ - @SuppressWarnings("deprecation") - private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) { + private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason, + int flags, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime - + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid); + Slog.d(TAG, "powerOffDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags + + ", uid=" + uid); } if (eventTime < mLastWakeTime @@ -1742,55 +1776,44 @@ public final class PowerManagerService extends SystemService return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep"); + final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (!PowerManagerInternal.isInteractive(wakefulness)) { + return false; + } + + Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay"); try { reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX, Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN)); - Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) - + " (uid " + uid + ")..."); - - mLastSleepTime = eventTime; - mLastSleepReason = reason; - mSandmanSummoned = true; - mDozeStartInProgress = true; - setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime); + Slog.i(TAG, "Powering off display group due to " + + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId + + ", uid= " + uid + ")..."); - // Report the number of wake locks that will be cleared by going to sleep. - int numWakeLocksCleared = 0; - final int numWakeLocks = mWakeLocks.size(); - for (int i = 0; i < numWakeLocks; i++) { - final WakeLock wakeLock = mWakeLocks.get(i); - switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { - case PowerManager.FULL_WAKE_LOCK: - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - case PowerManager.SCREEN_DIM_WAKE_LOCK: - numWakeLocksCleared += 1; - break; - } - } - EventLogTags.writePowerSleepRequested(numWakeLocksCleared); - - // Skip dozing if requested. + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); + setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason, + /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { - reallyGoToSleepNoUpdateLocked(eventTime, uid); + reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid); } + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } - private void napInternal(long eventTime, int uid) { + private void dreamDisplayGroup(int groupId, long eventTime, int uid) { synchronized (mLock) { - if (napNoUpdateLocked(eventTime, uid)) { + if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) { updatePowerStateLocked(); } } } - private boolean napNoUpdateLocked(long eventTime, int uid) { + private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid); + Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", uid=" + uid); } if (eventTime < mLastWakeTime || getWakefulnessLocked() != WAKEFULNESS_AWAKE @@ -1798,36 +1821,42 @@ public final class PowerManagerService extends SystemService return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup"); try { - Slog.i(TAG, "Nap time (uid " + uid +")..."); + Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")..."); + + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); + setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */ + 0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; - mSandmanSummoned = true; - setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } - // Done dozing, drop everything and go to sleep. - private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) { + private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "reallyGoToSleepNoUpdateLocked: eventTime=" + eventTime + Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid); } if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP - || !mBootCompleted || !mSystemReady) { + || !mBootCompleted || !mSystemReady + || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) + == WAKEFULNESS_ASLEEP) { return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup"); try { - Slog.i(TAG, "Sleeping (uid " + uid +")..."); + Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid +")..."); - setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - eventTime); + setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, /* opUid= */ 0, + /* opPackageName= */ null, /* details= */ null); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -1835,8 +1864,62 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - void setWakefulnessLocked(int wakefulness, int reason, long eventTime) { - if (getWakefulnessLocked() != wakefulness) { + void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason, + int opUid, String opPackageName, String details) { + if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) { + setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(), + eventTime, reason, uid, opUid, opPackageName, details); + } + } + + private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid, + int opUid, String opPackageName, String details) { + if (getWakefulnessLocked() == wakefulness) { + return; + } + + // Phase 1: Handle pre-wakefulness change bookkeeping. + final String traceMethodName; + switch (wakefulness) { + case WAKEFULNESS_ASLEEP: + traceMethodName = "reallyGoToSleep"; + Slog.i(TAG, "Sleeping (uid " + uid + ")..."); + break; + + case WAKEFULNESS_AWAKE: + traceMethodName = "wakeUp"; + Slog.i(TAG, "Waking up from " + + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked()) + + " (uid=" + uid + + ", reason=" + PowerManager.wakeReasonToString(reason) + + ", details=" + details + + ")..."); + mLastWakeTime = eventTime; + mLastWakeReason = reason; + break; + + case WAKEFULNESS_DREAMING: + traceMethodName = "nap"; + Slog.i(TAG, "Nap time (uid " + uid + ")..."); + break; + + case WAKEFULNESS_DOZING: + traceMethodName = "goToSleep"; + Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) + + " (uid " + uid + ")..."); + + mLastSleepTime = eventTime; + mLastSleepReason = reason; + mDozeStartInProgress = true; + break; + + default: + throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness); + } + + Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName); + try { + // Phase 2: Handle wakefulness change and bookkeeping. // Under lock, invalidate before set ensures caches won't return stale values. mInjector.invalidateIsInteractiveCaches(); mWakefulnessRaw = wakefulness; @@ -1850,6 +1933,37 @@ public final class PowerManagerService extends SystemService mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime); } mAttentionDetector.onWakefulnessChangeStarted(wakefulness); + + // Phase 3: Handle post-wakefulness change bookkeeping. + switch (wakefulness) { + case WAKEFULNESS_AWAKE: + mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid); + userActivityNoUpdateLocked( + eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); + if (sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + } + break; + + case WAKEFULNESS_DOZING: + // Report the number of wake locks that will be cleared by going to sleep. + int numWakeLocksCleared = 0; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + numWakeLocksCleared += 1; + break; + } + } + EventLogTags.writePowerSleepRequested(numWakeLocksCleared); + break; + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } } @@ -1872,7 +1986,7 @@ public final class PowerManagerService extends SystemService } private void finishWakefulnessChangeIfNeededLocked() { - if (mWakefulnessChanging && mDisplayReady) { + if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { if (getWakefulnessLocked() == WAKEFULNESS_DOZING && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) { return; // wait until dream has enabled dozing @@ -1884,13 +1998,6 @@ public final class PowerManagerService extends SystemService || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) { logSleepTimeoutRecapturedLocked(); } - if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime); - if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { - Slog.w(TAG, "Screen on took " + latencyMs + " ms"); - } - } mWakefulnessChanging = false; mNotifier.onWakefulnessChangeFinished(); } @@ -1989,7 +2096,6 @@ public final class PowerManagerService extends SystemService if ((dirty & DIRTY_BATTERY_STATE) != 0) { final boolean wasPowered = mIsPowered; final int oldPlugType = mPlugType; - final boolean oldLevelLow = mBatteryLevelLow; mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mPlugType = mBatteryManagerInternal.getPlugType(); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); @@ -2018,7 +2124,8 @@ public final class PowerManagerService extends SystemService final long now = mClock.uptimeMillis(); if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType, dockedOnWirelessCharger)) { - wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN, + wakeDisplayGroupNoUpdateLocked(DisplayGroup.DEFAULT, now, + PowerManager.WAKE_REASON_PLUGGED_IN, "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); } @@ -2300,7 +2407,8 @@ public final class PowerManagerService extends SystemService nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; if (now < nextTimeout) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked( + DisplayGroup.DEFAULT); if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) { mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; @@ -2555,13 +2663,23 @@ public final class PowerManagerService extends SystemService } final long time = mClock.uptimeMillis(); if (isAttentiveTimeoutExpired(time)) { - changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = sleepDisplayGroupNoUpdateLocked(id, time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + } } else if (shouldNapAtBedTimeLocked()) { - changed = napNoUpdateLocked(time, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID); + } } else { - changed = goToSleepNoUpdateLocked(time, - PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = sleepDisplayGroupNoUpdateLocked(id, time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + } } } } @@ -2644,7 +2762,7 @@ public final class PowerManagerService extends SystemService | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { scheduleSandmanLocked(); } } @@ -2659,6 +2777,14 @@ public final class PowerManagerService extends SystemService } } + private void handleSandman() { + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + if (mDisplayGroupPowerStateMapper.isSandmanSupported(id)) { + handleSandman(id); + } + } + } + /** * Called when the device enters or exits a dreaming or dozing state. * @@ -2666,16 +2792,18 @@ public final class PowerManagerService extends SystemService * the dream and we don't want to hold our lock while doing so. There is a risk that * the device will wake or go to sleep in the meantime so we have to handle that case. */ - private void handleSandman() { // runs on handler thread + private void handleSandman(int groupId) { // runs on handler thread // Handle preconditions. final boolean startDreaming; final int wakefulness; synchronized (mLock) { mSandmanScheduled = false; + // TODO (b/175764708): Support per-display doze. wakefulness = getWakefulnessLocked(); - if (mSandmanSummoned && mDisplayReady) { - startDreaming = canDreamLocked() || canDozeLocked(); - mSandmanSummoned = false; + if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + && mDisplayGroupPowerStateMapper.isReady(groupId)) { + startDreaming = canDreamLocked(groupId) || canDozeLocked(); + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false); } else { startDreaming = false; } @@ -2714,14 +2842,15 @@ public final class PowerManagerService extends SystemService // If preconditions changed, wait for the next iteration to determine // whether the dream should continue (or be restarted). - if (mSandmanSummoned || getWakefulnessLocked() != wakefulness) { + if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != wakefulness) { return; // wait for next cycle } // Determine whether the dream should continue. long now = mClock.uptimeMillis(); if (wakefulness == WAKEFULNESS_DREAMING) { - if (isDreaming && canDreamLocked()) { + if (isDreaming && canDreamLocked(groupId)) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 && mBatteryLevel < mBatteryLevelWhenDreamStarted - mDreamsBatteryLevelDrainCutoffConfig @@ -2741,16 +2870,13 @@ public final class PowerManagerService extends SystemService // Dream has ended or will be stopped. Update the power state. if (isItBedTimeYetLocked()) { - int flags = 0; - if (isAttentiveTimeoutExpired(now)) { - flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE; - } - goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, - Process.SYSTEM_UID); + final int flags = isAttentiveTimeoutExpired(now) + ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0; + sleepDisplayGroupNoUpdateLocked(groupId, now, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID); updatePowerStateLocked(); } else { - wakeUpNoUpdateLocked(now, - PowerManager.WAKE_REASON_UNKNOWN, + wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN, "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); updatePowerStateLocked(); @@ -2761,7 +2887,7 @@ public final class PowerManagerService extends SystemService } // Doze has ended or will be stopped. Update the power state. - reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID); + reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID); updatePowerStateLocked(); } } @@ -2773,11 +2899,11 @@ public final class PowerManagerService extends SystemService } /** - * Returns true if the device is allowed to dream in its current state. + * Returns true if the {@code groupId} is allowed to dream in its current state. */ - private boolean canDreamLocked() { + private boolean canDreamLocked(int groupId) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId); if (getWakefulnessLocked() != WAKEFULNESS_DREAMING || !mDreamsSupportedConfig || !mDreamsEnabledSetting @@ -2815,91 +2941,113 @@ public final class PowerManagerService extends SystemService /** * Updates the display power state asynchronously. - * When the update is finished, mDisplayReady will be set to true. The display - * controller posts a message to tell us when the actual display power state + * When the update is finished, the ready state of the displays will be updated. The display + * controllers post a message to tell us when the actual display power state * has been updated so we come back here to double-check and finish up. * * This function recalculates the display power state each time. * - * @return True if the display became ready. + * @return {@code true} if all displays became ready; {@code false} otherwise */ private boolean updateDisplayPowerStateLocked(int dirty) { - final boolean oldDisplayReady = mDisplayReady; + final boolean oldDisplayReady = mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked(); if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | - DIRTY_QUIESCENT)) != 0) { + DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) { if ((dirty & DIRTY_QUIESCENT) != 0) { sQuiescent = false; } - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - displayPowerRequest.policy = getDesiredScreenPolicyLocked(); - - // Determine appropriate screen brightness and auto-brightness adjustments. - final boolean autoBrightness; - final float screenBrightnessOverride; - if (!mBootCompleted) { - // Keep the brightness steady during boot. This requires the - // bootloader brightness and the default brightness to be identical. - autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessDefault; - } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { - autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; - } else { - autoBrightness = (mScreenBrightnessModeSetting == - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; - } + for (final int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId); + displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId); + + // Determine appropriate screen brightness and auto-brightness adjustments. + final boolean autoBrightness; + final float screenBrightnessOverride; + if (!mBootCompleted) { + // Keep the brightness steady during boot. This requires the + // bootloader brightness and the default brightness to be identical. + autoBrightness = false; + screenBrightnessOverride = mScreenBrightnessDefault; + } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { + autoBrightness = false; + screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; + } else { + autoBrightness = (mScreenBrightnessModeSetting == + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; + } - // Update display power request. - displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; - displayPowerRequest.useAutoBrightness = autoBrightness; - displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); - displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); + // Update display power request. + displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; + displayPowerRequest.useAutoBrightness = autoBrightness; + displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); + displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); - updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); + updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); - if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { - displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; - if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 - && !mDrawWakeLockOverrideFromSidekick) { - if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_DOZE; + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { + displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; + if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 + && !mDrawWakeLockOverrideFromSidekick) { + if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_DOZE; + } + if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_ON; + } } - if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_ON; + displayPowerRequest.dozeScreenBrightness = + mDozeScreenBrightnessOverrideFromDreamManagerFloat; + } else { + displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; + displayPowerRequest.dozeScreenBrightness = + PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + + final boolean ready = mDisplayManagerInternal.requestPowerState(groupId, + displayPowerRequest, mRequestWaitForNegativeProximity); + + if (DEBUG_SPEW) { + Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready + + ", groupId=" + groupId + + ", policy=" + displayPowerRequest.policy + + ", mWakefulness=" + + mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + + ", mUserActivitySummary=0x" + Integer.toHexString( + mUserActivitySummary) + + ", mBootCompleted=" + mBootCompleted + + ", screenBrightnessOverride=" + + displayPowerRequest.screenBrightnessOverride + + ", useAutoBrightness=" + displayPowerRequest.useAutoBrightness + + ", mScreenBrightnessBoostInProgress=" + + mScreenBrightnessBoostInProgress + + ", mIsVrModeEnabled= " + mIsVrModeEnabled + + ", sQuiescent=" + sQuiescent); + } + + final boolean displayReadyStateChanged = + mDisplayGroupPowerStateMapper.setDisplayGroupReadyLocked(groupId, ready); + if (ready && displayReadyStateChanged + && mDisplayGroupPowerStateMapper.getWakefulnessLocked( + groupId) == WAKEFULNESS_AWAKE) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); + final int latencyMs = (int) (mClock.uptimeMillis() + - mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId)); + if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { + Slog.w(TAG, "Screen on took " + latencyMs + " ms"); } } - displayPowerRequest.dozeScreenBrightness = - mDozeScreenBrightnessOverrideFromDreamManagerFloat; - } else { - displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; - displayPowerRequest.dozeScreenBrightness = - PowerManager.BRIGHTNESS_INVALID_FLOAT; } - mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest, - mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; - if (DEBUG_SPEW) { - Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady - + ", policy=" + displayPowerRequest.policy - + ", mWakefulness=" + getWakefulnessLocked() - + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) - + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) - + ", mBootCompleted=" + mBootCompleted - + ", screenBrightnessOverride=" + screenBrightnessOverride - + ", useAutoBrightness=" + autoBrightness - + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress - + ", mIsVrModeEnabled= " + mIsVrModeEnabled - + ", sQuiescent=" + sQuiescent); - } } - return mDisplayReady && !oldDisplayReady; + return mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked() && !oldDisplayReady; } private void updateScreenBrightnessBoostLocked(int dirty) { @@ -2933,12 +3081,11 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - int getDesiredScreenPolicyLocked() { - if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP || sQuiescent) { + int getDesiredScreenPolicyLocked(int groupId) { + final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; - } - - if (getWakefulnessLocked() == WAKEFULNESS_DOZING) { + } else if (wakefulness == WAKEFULNESS_DOZING) { if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } @@ -2968,7 +3115,6 @@ public final class PowerManagerService extends SystemService private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks = new DisplayManagerInternal.DisplayPowerCallbacks() { - private int mDisplayState = Display.STATE_UNKNOWN; @Override public void onStateChanged() { @@ -2999,29 +3145,25 @@ public final class PowerManagerService extends SystemService } @Override - public void onDisplayStateChange(int state) { + public void onDisplayStateChange(boolean allInactive, boolean allOff) { // This method is only needed to support legacy display blanking behavior // where the display's power state is coupled to suspend or to the power HAL. // The order of operations matters here. synchronized (mLock) { - if (mDisplayState != state) { - mDisplayState = state; - setPowerModeInternal(MODE_DISPLAY_INACTIVE, - !Display.isActiveState(state)); - if (state == Display.STATE_OFF) { - if (!mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(false); - } - if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(true); - } - } else { - if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(false); - } - if (!mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(true); - } + setPowerModeInternal(MODE_DISPLAY_INACTIVE, allInactive); + if (allOff) { + if (!mDecoupleHalInteractiveModeFromDisplayConfig) { + setHalInteractiveModeLocked(false); + } + if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(true); + } + } else { + if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(false); + } + if (!mDecoupleHalInteractiveModeFromDisplayConfig) { + setHalInteractiveModeLocked(true); } } } @@ -3036,13 +3178,6 @@ public final class PowerManagerService extends SystemService public void releaseSuspendBlocker() { mDisplaySuspendBlocker.release(); } - - @Override - public String toString() { - synchronized (this) { - return "state=" + Display.stateToString(mDisplayState); - } - } }; private boolean shouldUseProximitySensorLocked() { @@ -3058,9 +3193,13 @@ public final class PowerManagerService extends SystemService final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); final boolean autoSuspend = !needDisplaySuspendBlocker; - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - final boolean interactive = displayPowerRequest.isBrightOrDim(); + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(DisplayGroup.DEFAULT); + final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); + boolean interactive = false; + for (int id : groupIds) { + interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim(); + } // Disable auto-suspend if needed. // FIXME We should consider just leaving auto-suspend enabled forever since @@ -3090,7 +3229,7 @@ public final class PowerManagerService extends SystemService // until the display is actually ready so that all transitions have // completed. This is probably a good sign that things have gotten // too tangled over here... - if (interactive || mDisplayReady) { + if (interactive || mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { setHalInteractiveModeLocked(interactive); } } @@ -3116,29 +3255,10 @@ public final class PowerManagerService extends SystemService * We do so if the screen is on or is in transition between states. */ private boolean needDisplaySuspendBlockerLocked() { - if (!mDisplayReady) { + if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { return true; } - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - if (displayPowerRequest.isBrightOrDim()) { - // If we asked for the screen to be on but it is off due to the proximity - // sensor then we may suspend but only if the configuration allows it. - // On some hardware it may not be safe to suspend because the proximity - // sensor may not be correctly configured as a wake-up source. - if (!displayPowerRequest.useProximitySensor || !mProximityPositive - || !mSuspendWhenScreenOffDueToProximityConfig) { - return true; - } - } - if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE - && displayPowerRequest.dozeScreenState == Display.STATE_ON) { - // Although we are in DOZE and would normally allow the device to suspend, - // the doze service has explicitly requested the display to remain in the ON - // state which means we should hold the display suspend blocker. - return true; - } if (mScreenBrightnessBoostInProgress) { return true; } @@ -3152,6 +3272,30 @@ public final class PowerManagerService extends SystemService return true; } + final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); + for (int id : groupIds) { + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(id); + if (displayPowerRequest.isBrightOrDim()) { + // If we asked for the screen to be on but it is off due to the proximity + // sensor then we may suspend but only if the configuration allows it. + // On some hardware it may not be safe to suspend because the proximity + // sensor may not be correctly configured as a wake-up source. + if (!displayPowerRequest.useProximitySensor || !mProximityPositive + || !mSuspendWhenScreenOffDueToProximityConfig) { + return true; + } + } + + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE + && displayPowerRequest.dozeScreenState == Display.STATE_ON) { + // Although we are in DOZE and would normally allow the device to suspend, + // the doze service has explicitly requested the display to remain in the ON + // state which means we should hold the display suspend blocker. + return true; + } + } + // Let the system suspend if the screen is off or dozing. return false; } @@ -3685,9 +3829,15 @@ public final class PowerManagerService extends SystemService synchronized (mLock) { mForceSuspendActive = true; // Place the system in an non-interactive state - goToSleepInternal(mClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + boolean updatePowerState = false; + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + } + if (updatePowerState) { + updatePowerStateLocked(); + } // Disable all the partial wake locks as well updateWakeLockDisabledStatesLocked(); @@ -3827,7 +3977,6 @@ public final class PowerManagerService extends SystemService pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); - pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode); pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); @@ -3845,7 +3994,6 @@ public final class PowerManagerService extends SystemService + TimeUtils.formatUptime(mLastScreenBrightnessBoostTime)); pw.println(" mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress); - pw.println(" mDisplayReady=" + mDisplayReady); pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker); pw.println(" mHoldingDisplaySuspendBlocker=" + mHoldingDisplaySuspendBlocker); @@ -4080,7 +4228,6 @@ public final class PowerManagerService extends SystemService PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY, mRequestWaitForNegativeProximity); proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled); - proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned); proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow); proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode); proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode); @@ -4107,7 +4254,6 @@ public final class PowerManagerService extends SystemService proto.write( PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS, mScreenBrightnessBoostInProgress); - proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady); proto.write( PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER, mHoldingWakeLockSuspendBlocker); @@ -4921,7 +5067,8 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid); + wakeDisplayGroup(DisplayGroup.DEFAULT, eventTime, reason, details, uid, + opPackageName, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -4939,7 +5086,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - goToSleepInternal(eventTime, reason, flags, uid); + sleepDisplayGroup(DisplayGroup.DEFAULT, eventTime, reason, flags, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -4957,7 +5104,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - napInternal(eventTime, uid); + dreamDisplayGroup(DisplayGroup.DEFAULT, eventTime, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -5590,7 +5737,7 @@ public final class PowerManagerService extends SystemService // also tells us that we're not already ignoring the proximity sensor. final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked(DisplayGroup.DEFAULT); if (displayPowerRequest.useProximitySensor && mProximityPositive) { mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); return true; diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 7f35511236f7..f07197cf77f6 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -77,6 +77,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.display.DisplayGroup; import com.android.server.lights.LightsManager; import com.android.server.policy.WindowManagerPolicy; import com.android.server.power.PowerManagerService.BatteryReceiver; @@ -97,9 +98,12 @@ import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** * Tests for {@link com.android.server.power.PowerManagerService}. @@ -178,7 +182,8 @@ public class PowerManagerServiceTest { .thenReturn(mPowerSaveState); when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false); when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false); - when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true); + when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), + anyBoolean())).thenReturn(true); when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true); @@ -399,30 +404,33 @@ public class PowerManagerServiceTest { @Test public void testGetDesiredScreenPolicy_WithVR() throws Exception { createService(); + mService.systemReady(null); // Brighten up the screen - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null, + null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); // Move to VR mService.setVrModeEnabled(true); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_VR); // Then take a nap - mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null, + null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_OFF); // Wake up to VR - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null, + null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_VR); // And back to normal mService.setVrModeEnabled(false); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -673,8 +681,9 @@ public class PowerManagerServiceTest { } @Test - public void testForceSuspend_forceSuspendFailurePropogated() { + public void testForceSuspend_forceSuspendFailurePropagated() throws Exception { createService(); + startSystem(); when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false); assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse(); } @@ -838,7 +847,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -857,11 +866,8 @@ public class PowerManagerServiceTest { public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception { when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); createService(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( - DisplayPowerRequest.POLICY_OFF); - startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_OFF); } @@ -871,7 +877,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); forceAwake(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -1143,4 +1149,85 @@ public class PowerManagerServiceTest { assertFalse( mService.getBinderServiceInstance().setPowerModeChecked(Mode.INTERACTIVE, false)); } + + @Test + public void testMultiDisplay_wakefulnessUpdates() throws Exception { + final int nonDefaultDisplayGroupId = DisplayGroup.DEFAULT + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null, + null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null, + null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test + public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception { + final int nonDefaultDisplayGroupId = DisplayGroup.DEFAULT + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null, + null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + } + + @Test + public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception { + final int nonDefaultDisplayGroupId = DisplayGroup.DEFAULT + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, null, + null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + listener.get().onDisplayGroupRemoved(nonDefaultDisplayGroupId); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + mService.setWakefulnessLocked(DisplayGroup.DEFAULT, WAKEFULNESS_AWAKE, 0, 0, 0, 0, null, + null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } } |