diff options
12 files changed, 957 insertions, 482 deletions
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 48b05b7d9e15..2d58520a942e 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -67,12 +67,6 @@ public abstract class DisplayManagerInternal { public abstract boolean isProximitySensorAvailable(); /** - * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided - * display belongs. - */ - public abstract int getDisplayGroupId(int displayId); - - /** * Registers a display group listener which will be informed of the addition, removal, or change * of display groups. * @@ -469,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(); diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 225da7ad87b3..a2b9b966dd46 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -354,6 +354,10 @@ class AutomaticBrightnessController { } } + public void stop() { + setLightSensorEnabled(false); + } + public boolean hasUserDataPoints() { return mBrightnessMapper.hasUserDataPoints(); } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index a186e334c5c0..06010f51e231 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -243,7 +243,6 @@ public class BrightnessTracker { } /** Stop listening for events */ - @VisibleForTesting void stop() { if (DEBUG) { Slog.d(TAG, "Stop"); diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 198ea3d3f66a..f2126987ff19 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -817,6 +817,12 @@ final class ColorFade { } DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (displayInfo == null) { + // displayInfo can be null if the associated display has been removed. There + // is a delay between the display being removed and ColorFade being dismissed. + return; + } + switch (displayInfo.rotation) { case Surface.ROTATION_0: t.setPosition(mSurfaceControl, 0, 0); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6a229417316d..c62dd72ada70 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -249,30 +249,48 @@ public final class DisplayManagerService extends SystemService { /** {@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); + if (index > -1) { + final int currentState = mDisplayStates.valueAt(index); + stateChanged = state != currentState; + if (stateChanged) { + final int size = mDisplayStates.size(); + for (int i = 0; i < size; i++) { + final int displayState = i == index ? state : mDisplayStates.valueAt(i); + if (displayState != Display.STATE_OFF) { + allOff = false; + } + if (Display.isActiveState(displayState)) { + allInactive = false; + } + if (!allOff && !allInactive) { + break; + } + } + } + } else { + stateChanged = false; + } } // 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); } } }; @@ -1160,7 +1178,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(); @@ -2758,8 +2776,22 @@ public final class DisplayManagerService extends SystemService { 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; } } @@ -2772,13 +2804,6 @@ public final class DisplayManagerService extends SystemService { } @Override - public int getDisplayGroupId(int displayId) { - synchronized (mSyncRoot) { - return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId); - } - } - - @Override public void registerDisplayGroupListener(DisplayGroupListener listener) { mDisplayGroupListeners.add(listener); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9320f5027ce0..62cf86b31180 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -121,6 +121,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6; private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7; private static final int MSG_IGNORE_PROXIMITY = 8; + private static final int MSG_STOP = 9; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -351,6 +352,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Nullable private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; + @Nullable private final ColorDisplayServiceInternal mCdsi; private final float[] mNitsRange; @@ -409,6 +411,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private ObjectAnimator mColorFadeOffAnimator; private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; + // True if this DisplayPowerController has been stopped and should no longer be running. + private boolean mStopped; + /** * Creates the display power controller. */ @@ -583,14 +588,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; - try { - displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, - mSensorManager, resources); - displayWhiteBalanceSettings.setCallbacks(this); - displayWhiteBalanceController.setCallbacks(this); - } catch (Exception e) { - Slog.e(TAG, "failed to set up display white-balance: " + e); + if (mDisplayId == Display.DEFAULT_DISPLAY) { + try { + displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); + displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, + mSensorManager, resources); + displayWhiteBalanceSettings.setCallbacks(this); + displayWhiteBalanceController.setCallbacks(this); + } catch (Exception e) { + Slog.e(TAG, "failed to set up display white-balance: " + e); + } } mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings; mDisplayWhiteBalanceController = displayWhiteBalanceController; @@ -602,20 +609,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources() .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits)); } - mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated) { - applyReduceBrightColorsSplineAdjustment(); - } + if (mDisplayId == Display.DEFAULT_DISPLAY) { + mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); + boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { + @Override + public void onReduceBrightColorsActivationChanged(boolean activated) { + applyReduceBrightColorsSplineAdjustment(); + } - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { + @Override + public void onReduceBrightColorsStrengthChanged(int strength) { + applyReduceBrightColorsSplineAdjustment(); + } + }); + if (active) { applyReduceBrightColorsSplineAdjustment(); } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); + } else { + mCdsi = null; } } @@ -713,6 +724,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } synchronized (mLock) { + if (mStopped) { + return true; + } + boolean changed = false; if (waitForNegativeProximity @@ -731,11 +746,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (changed) { mDisplayReadyLocked = false; - } - - if (changed && !mPendingRequestChangedLocked) { - mPendingRequestChangedLocked = true; - sendUpdatePowerStateLocked(); + if (!mPendingRequestChangedLocked) { + mPendingRequestChangedLocked = true; + sendUpdatePowerStateLocked(); + } } return mDisplayReadyLocked; @@ -758,6 +772,34 @@ 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() { + synchronized (mLock) { + mStopped = true; + Message msg = mHandler.obtainMessage(MSG_STOP); + mHandler.sendMessage(msg); + + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setEnabled(false); + } + + if (mAutomaticBrightnessController != null) { + mAutomaticBrightnessController.stop(); + } + + if (mBrightnessTracker != null) { + mBrightnessTracker.stop(); + } + + mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); + } + } + private void sendUpdatePowerState() { synchronized (mLock) { sendUpdatePowerStateLocked(); @@ -765,7 +807,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void sendUpdatePowerStateLocked() { - if (!mPendingUpdatePowerStateLocked) { + if (!mStopped && !mPendingUpdatePowerStateLocked) { mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); mHandler.sendMessage(msg); @@ -788,7 +830,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mColorFadeOffAnimator.addListener(mAnimatorListener); } - mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>( + mScreenBrightnessRampAnimator = new RampAnimator<>( mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); @@ -829,12 +871,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } }; - private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() { - @Override - public void onAnimationEnd() { - sendUpdatePowerState(); - } - }; + private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState; + + /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ + private void cleanupHandlerThreadAfterStop() { + setProximitySensorEnabled(false); + mHandler.removeCallbacksAndMessages(null); + mPowerState.stop(); + mPowerState = null; + } private void updatePowerState() { // Update the power state request. @@ -844,6 +889,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call int brightnessAdjustmentFlags = 0; mBrightnessReasonTemp.set(null); synchronized (mLock) { + if (mStopped) { + return; + } mPendingUpdatePowerStateLocked = false; if (mPendingRequestLocked == null) { return; // wait until first actual power request @@ -2144,6 +2192,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case MSG_IGNORE_PROXIMITY: ignoreProximitySensorUntilChangedInternal(); break; + + case MSG_STOP: + cleanupHandlerThreadAfterStop(); + break; } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 1d20d878fb81..77aff5b03dd2 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; + DisplayPowerState( DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { mHandler = new Handler(true /*async*/); @@ -264,9 +266,24 @@ 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() { + mStopped = true; + mPhotonicModulator.interrupt(); + dismissColorFade(); + mCleanListener = null; + mHandler.removeCallbacksAndMessages(null); + } + public void dump(PrintWriter pw) { pw.println(); pw.println("Display Power State:"); + pw.println(" mStopped=" + mStopped); pw.println(" mScreenState=" + Display.stateToString(mScreenState)); pw.println(" mScreenBrightness=" + mScreenBrightness); pw.println(" mScreenReady=" + mScreenReady); @@ -428,7 +445,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 a054db533e3f..a3ff534e336e 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; @@ -143,10 +142,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]; @@ -171,15 +166,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } - public int getDisplayGroupIdLocked(int displayId) { - final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId); - if (displayGroup != null) { - return displayGroup.getGroupId(); - } - - return -1; - } - public DisplayGroup getDisplayGroupLocked(int groupId) { final int size = mDisplayIdToGroupMap.size(); for (int i = 0; i < size; i++) { 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..2fcd178c3d15 --- /dev/null +++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2021 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.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.PowerManagerInternal; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; + +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 (dream/AOD). + final boolean supportsSandman = groupId == Display.DEFAULT_DISPLAY_GROUP; + 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(Display.DEFAULT_DISPLAY_GROUP, displayGroupInfo); + mDisplayGroupIds = new int[]{Display.DEFAULT_DISPLAY_GROUP}; + } + + 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 { + public final DisplayPowerRequest displayPowerRequest; + public int wakefulness; + public boolean ready; + public long lastPowerOnTime; + public boolean sandmanSummoned; + + /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */ + public 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 2fc3e40acd4d..000000000000 --- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java +++ /dev/null @@ -1,121 +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; - -/** - * 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(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest()); - mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP); - } - - 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 a13570529197..8c46445fac9b 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -16,8 +16,13 @@ package com.android.server.power; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString; 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_GROUPS_TURNED_OFF; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED; +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 +47,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; @@ -178,6 +182,8 @@ public final class PowerManagerService extends SystemService private static final int DIRTY_ATTENTIVE = 1 << 14; // Dirty bit: phone flipped to face down private static final int DIRTY_FACE_DOWN = 1 << 15; + // Dirty bit: display group power state has changed + private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -300,10 +306,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; @@ -354,11 +356,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. @@ -632,6 +630,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 { @@ -877,6 +908,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; @@ -1068,7 +1105,8 @@ public final class PowerManagerService extends SystemService updatePowerStateLocked(); if (sQuiescent) { - goToSleepNoUpdateLocked(mClock.uptimeMillis(), + sleepDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, + mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } @@ -1085,8 +1123,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()); @@ -1404,9 +1442,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); + } } } @@ -1695,26 +1735,29 @@ 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 || mForceSuspendActive || !mSystemReady) { return false; } - if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { + final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (currentState == WAKEFULNESS_AWAKE) { if (!mBootCompleted && sQuiescent) { mDirty |= DIRTY_QUIESCENT; return true; @@ -1722,113 +1765,90 @@ public final class PowerManagerService extends SystemService return false; } - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - - 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, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags + + ", uid=" + uid); } if (eventTime < mLastWakeTime - || getWakefulnessLocked() == WAKEFULNESS_ASLEEP - || getWakefulnessLocked() == WAKEFULNESS_DOZING + || !PowerManagerInternal.isInteractive(getWakefulnessLocked()) || !mSystemReady || !mBootCompleted) { 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); - - // 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); + Slog.i(TAG, "Powering off display group due to " + + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId + + ", uid= " + uid + ")..."); - // 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 @@ -1836,36 +1856,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); } @@ -1873,8 +1899,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; @@ -1888,6 +1968,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); } } @@ -1910,7 +2021,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 @@ -1922,13 +2033,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(); } @@ -2027,7 +2131,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(); @@ -2056,7 +2159,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(Display.DEFAULT_DISPLAY_GROUP, now, + PowerManager.WAKE_REASON_PLUGGED_IN, "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); } @@ -2340,7 +2444,8 @@ public final class PowerManagerService extends SystemService nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; if (now < nextTimeout) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked( + Display.DEFAULT_DISPLAY_GROUP); if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) { mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; @@ -2605,13 +2710,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); + } } } } @@ -2686,6 +2801,7 @@ public final class PowerManagerService extends SystemService private void updateDreamLocked(int dirty, boolean displayBecameReady) { if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY + | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_ATTENTIVE | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED @@ -2694,7 +2810,7 @@ public final class PowerManagerService extends SystemService | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { scheduleSandmanLocked(); } } @@ -2709,6 +2825,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. * @@ -2716,16 +2840,19 @@ 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 ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && + mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + && mDisplayGroupPowerStateMapper.isReady(groupId)) { + startDreaming = canDreamLocked(groupId) || canDozeLocked(); + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false); } else { startDreaming = false; } @@ -2764,14 +2891,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 @@ -2791,16 +2919,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(); @@ -2811,7 +2936,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(); } } @@ -2823,11 +2948,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 @@ -2865,95 +2990,117 @@ 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) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { sQuiescent = false; } else { mDirty |= DIRTY_QUIESCENT; } } - 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.dozeScreenState == Display.STATE_ON_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_ON; + 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; + } } + displayPowerRequest.dozeScreenBrightness = + mDozeScreenBrightnessOverrideFromDreamManagerFloat; + } else { + displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; + displayPowerRequest.dozeScreenBrightness = + PowerManager.BRIGHTNESS_INVALID_FLOAT; } - displayPowerRequest.dozeScreenBrightness = - mDozeScreenBrightnessOverrideFromDreamManagerFloat; - } else { - displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; - displayPowerRequest.dozeScreenBrightness = - PowerManager.BRIGHTNESS_INVALID_FLOAT; - } - mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP, - displayPowerRequest, mRequestWaitForNegativeProximity); - mRequestWaitForNegativeProximity = false; + final boolean ready = mDisplayManagerInternal.requestPowerState(groupId, + displayPowerRequest, mRequestWaitForNegativeProximity); - 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); + if (DEBUG_SPEW) { + Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready + + ", groupId=" + groupId + + ", policy=" + policyToString(displayPowerRequest.policy) + + ", mWakefulness=" + + PowerManagerInternal.wakefulnessToString( + 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"); + } + } } + mRequestWaitForNegativeProximity = false; } - return mDisplayReady && !oldDisplayReady; + + return mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked() && !oldDisplayReady; } private void updateScreenBrightnessBoostLocked(int dirty) { @@ -2987,12 +3134,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; } @@ -3022,7 +3168,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() { @@ -3053,29 +3198,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); } } } @@ -3090,13 +3231,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() { @@ -3112,9 +3246,11 @@ 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 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 @@ -3144,7 +3280,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); } } @@ -3170,29 +3306,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; } @@ -3206,6 +3323,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; } @@ -3739,9 +3880,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(); @@ -3881,7 +4028,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); @@ -3899,7 +4045,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); pw.println(" mLastFlipTime=" + mLastFlipTime); @@ -4138,7 +4283,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); @@ -4165,7 +4309,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); @@ -4979,7 +5122,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(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid, + opPackageName, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -4997,7 +5141,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - goToSleepInternal(eventTime, reason, flags, uid); + sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -5015,7 +5159,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - napInternal(eventTime, uid); + dreamDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -5677,7 +5821,8 @@ 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( + Display.DEFAULT_DISPLAY_GROUP); 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 6b95cfc934ac..ea27331ac4ca 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -98,9 +98,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +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}. @@ -402,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(Display.DEFAULT_DISPLAY_GROUP, 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(Display.DEFAULT_DISPLAY_GROUP, 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(Display.DEFAULT_DISPLAY_GROUP, 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); } @@ -676,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(); } @@ -841,7 +847,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -860,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); } @@ -874,7 +877,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); forceAwake(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -890,7 +893,7 @@ public class PowerManagerServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -1164,6 +1167,87 @@ public class PowerManagerServiceTest { } @Test + public void testMultiDisplay_wakefulnessUpdates() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 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(Display.DEFAULT_DISPLAY_GROUP, 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(Display.DEFAULT_DISPLAY_GROUP, 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 = Display.DEFAULT_DISPLAY_GROUP + 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(Display.DEFAULT_DISPLAY_GROUP, 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 = Display.DEFAULT_DISPLAY_GROUP + 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(Display.DEFAULT_DISPLAY_GROUP, 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(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test public void testGetFullPowerSavePolicy_returnsStateMachineResult() { createService(); mService.systemReady(null); |