Merge "Request power state updates for individual DisplayGroups" into sc-dev

This commit is contained in:
Sean Stout
2021-02-23 11:21:15 +00:00
committed by Android (Google) Code Review
12 changed files with 956 additions and 481 deletions

View File

@@ -66,12 +66,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();

View File

@@ -354,6 +354,10 @@ class AutomaticBrightnessController {
}
}
public void stop() {
setLightSensorEnabled(false);
}
public boolean hasUserDataPoints() {
return mBrightnessMapper.hasUserDataPoints();
}

View File

@@ -243,7 +243,6 @@ public class BrightnessTracker {
}
/** Stop listening for events */
@VisibleForTesting
void stop() {
if (DEBUG) {
Slog.d(TAG, "Stop");

View File

@@ -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);

View File

@@ -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;
}
}
@@ -2771,13 +2803,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);

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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++) {

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
@@ -1163,6 +1166,87 @@ public class PowerManagerServiceTest {
mService.getBinderServiceInstance().setPowerModeChecked(Mode.INTERACTIVE, false));
}
@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();