From ede3ded5e427cb3c1e755e7050c3bc82ee9d5133 Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Sun, 11 Apr 2021 20:23:55 +0100 Subject: [PATCH] Add HDR-HBM support into framework. Add a listener to know when HDR layers are present on displays which support HBM. Pass both HDR and SDR brightness requests from DPC to SurfaceFlinger. Lots and lots of scaffolding for this. Bug: 175321409 Bug: 175821789 Test: atest com.android.server.display Test: Manually verify that brightness levels are correct when in high lux and HDR, low lux and HDR, and various luxes without HDR. Change-Id: I530299455a7745fd57d44b1e8b20477674894e21 --- .../hardware/display/BrightnessInfo.java | 24 +++- .../AutomaticBrightnessController.java | 8 +- .../server/display/DisplayBlanker.java | 5 +- .../android/server/display/DisplayDevice.java | 4 +- .../server/display/DisplayDeviceConfig.java | 23 ++++ .../server/display/DisplayManagerService.java | 76 +++++++---- .../display/DisplayPowerController.java | 86 +++++++++--- .../server/display/DisplayPowerState.java | 77 +++++++++-- .../display/HighBrightnessModeController.java | 125 ++++++++++++++---- .../server/display/LocalDisplayAdapter.java | 60 +++++++-- .../android/server/display/RampAnimator.java | 50 ++++++- .../server/display/VirtualDisplayAdapter.java | 3 +- .../display/LocalDisplayAdapterTest.java | 12 +- .../HighBrightnessModeControllerTest.java | 11 +- 14 files changed, 461 insertions(+), 103 deletions(-) diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java index 4289860d4e8a9..c5d37c2d0b906 100644 --- a/core/java/android/hardware/display/BrightnessInfo.java +++ b/core/java/android/hardware/display/BrightnessInfo.java @@ -33,7 +33,8 @@ public final class BrightnessInfo implements Parcelable { @IntDef(prefix = {"HIGH_BRIGHTNESS_MODE_"}, value = { HIGH_BRIGHTNESS_MODE_OFF, - HIGH_BRIGHTNESS_MODE_SUNLIGHT + HIGH_BRIGHTNESS_MODE_SUNLIGHT, + HIGH_BRIGHTNESS_MODE_HDR }) @Retention(RetentionPolicy.SOURCE) public @interface HighBrightnessMode {} @@ -50,6 +51,12 @@ public final class BrightnessInfo implements Parcelable { */ public static final int HIGH_BRIGHTNESS_MODE_SUNLIGHT = 1; + /** + * High brightness mode is ON due to high ambient light (sunlight). The high brightness range is + * currently accessible to the user. + */ + public static final int HIGH_BRIGHTNESS_MODE_HDR = 2; + /** Brightness */ public final float brightness; @@ -73,6 +80,21 @@ public final class BrightnessInfo implements Parcelable { this.highBrightnessMode = highBrightnessMode; } + /** + * @return User-friendly string for specified {@link HighBrightnessMode} parameter. + */ + public static String hbmToString(@HighBrightnessMode int highBrightnessMode) { + switch (highBrightnessMode) { + case HIGH_BRIGHTNESS_MODE_OFF: + return "off"; + case HIGH_BRIGHTNESS_MODE_HDR: + return "hdr"; + case HIGH_BRIGHTNESS_MODE_SUNLIGHT: + return "sunlight"; + } + return "invalid"; + } + @Override public int describeContents() { return 0; diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 91b96dc174731..1a07cb854caef 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -738,13 +738,19 @@ class AutomaticBrightnessController { float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName, mForegroundAppCategory); float newScreenAutoBrightness = clampScreenBrightness(value); + + // The min/max range can change for brightness due to HBM. See if the current brightness + // value still falls within the current range (which could have changed). + final boolean currentBrightnessWithinAllowedRange = BrightnessSynchronizer.floatEquals( + mScreenAutoBrightness, clampScreenBrightness(mScreenAutoBrightness)); // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold, // in which case we ignore the new screen brightness if it doesn't differ enough from the // previous one. if (!Float.isNaN(mScreenAutoBrightness) && !isManuallySet && newScreenAutoBrightness > mScreenDarkeningThreshold - && newScreenAutoBrightness < mScreenBrighteningThreshold) { + && newScreenAutoBrightness < mScreenBrighteningThreshold + && currentBrightnessWithinAllowedRange) { if (mLoggingEnabled) { Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold + " < " + newScreenAutoBrightness diff --git a/services/core/java/com/android/server/display/DisplayBlanker.java b/services/core/java/com/android/server/display/DisplayBlanker.java index e2129ba13626f..8de49af3de222 100644 --- a/services/core/java/com/android/server/display/DisplayBlanker.java +++ b/services/core/java/com/android/server/display/DisplayBlanker.java @@ -20,5 +20,8 @@ package com.android.server.display; * Interface used to update the actual display state. */ public interface DisplayBlanker { - void requestDisplayState(int displayId, int state, float brightness); + /** + * Requests the specified display state and brightness levels for the specified displayId. + */ + void requestDisplayState(int displayId, int state, float brightness, float sdrBrightness); } diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index b3070b7cf1ba1..35f29579b417e 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -156,10 +156,12 @@ abstract class DisplayDevice { * * @param state The new display state. * @param brightnessState The new display brightnessState. + * @param sdrBrightnessState The new display brightnessState for SDR layers. * @return A runnable containing work to be deferred until after we have * exited the critical section, or null if none. */ - public Runnable requestDisplayStateLocked(int state, float brightnessState) { + public Runnable requestDisplayStateLocked(int state, float brightnessState, + float sdrBrightnessState) { return null; } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 2d7145fef69c5..c46cfe3d1d2f3 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -104,6 +104,7 @@ public class DisplayDeviceConfig { private float mBrightnessRampSlowIncrease = Float.NaN; private Spline mBrightnessToBacklightSpline; private Spline mBacklightToBrightnessSpline; + private Spline mBacklightToNitsSpline; private List mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; @@ -218,6 +219,20 @@ public class DisplayDeviceConfig { return mBrightnessToBacklightSpline.interpolate(brightness); } + /** + * Calculates the nits value for the specified backlight value if a mapping exists. + * + * @return The mapped nits or 0 if no mapping exits. + */ + public float getNitsFromBacklight(float backlight) { + if (mBacklightToNitsSpline == null) { + Slog.wtf(TAG, "requesting nits when no mapping exists."); + return -1; + } + backlight = Math.max(backlight, mBacklightMinimum); + return mBacklightToNitsSpline.interpolate(backlight); + } + /** * Return an array of equal length to backlight and nits, that covers the entire system * brightness range of 0.0-1.0. @@ -257,6 +272,13 @@ public class DisplayDeviceConfig { return mAmbientLightSensor; } + /** + * @return true if a nits to backlight mapping is defined in this config, false otherwise. + */ + public boolean hasNitsMapping() { + return mBacklightToNitsSpline != null; + } + /** * @param quirkValue The quirk to test. * @return {@code true} if the specified quirk is present in this configuration, @@ -584,6 +606,7 @@ public class DisplayDeviceConfig { } mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight); mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness); + mBacklightToNitsSpline = Spline.createSpline(mBacklight, mNits); } private void loadQuirks(DisplayConfiguration config) { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 789f08fb31878..0a4b137fa6cdd 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -257,7 +257,8 @@ public final class DisplayManagerService extends SystemService { private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() { // Synchronized to avoid race conditions when updating multiple display states. @Override - public synchronized void requestDisplayState(int displayId, int state, float brightness) { + public synchronized void requestDisplayState(int displayId, int state, float brightness, + float sdrBrightness) { boolean allInactive = true; boolean allOff = true; final boolean stateChanged; @@ -288,7 +289,7 @@ public final class DisplayManagerService extends SystemService { // The order of operations is important for legacy reasons. if (state == Display.STATE_OFF) { - requestDisplayStateInternal(displayId, state, brightness); + requestDisplayStateInternal(displayId, state, brightness, sdrBrightness); } if (stateChanged) { @@ -296,7 +297,7 @@ public final class DisplayManagerService extends SystemService { } if (state != Display.STATE_OFF) { - requestDisplayStateInternal(displayId, state, brightness); + requestDisplayStateInternal(displayId, state, brightness, sdrBrightness); } } }; @@ -316,7 +317,7 @@ public final class DisplayManagerService extends SystemService { // A map from LogicalDisplay ID to display brightness. @GuardedBy("mSyncRoot") - private final SparseArray mDisplayBrightnesses = new SparseArray<>(); + private final SparseArray mDisplayBrightnesses = new SparseArray<>(); // Set to true when there are pending display changes that have yet to be applied // to the surface flinger state. @@ -667,11 +668,8 @@ public final class DisplayManagerService extends SystemService { } } - private void requestDisplayStateInternal(int displayId, int state, float brightnessState) { - if (state == Display.STATE_UNKNOWN) { - state = Display.STATE_ON; - } - if (state == Display.STATE_OFF) { + private float clampBrightness(int displayState, float brightnessState) { + if (displayState == Display.STATE_OFF) { brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; } else if (brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT && brightnessState < PowerManager.BRIGHTNESS_MIN) { @@ -679,6 +677,17 @@ public final class DisplayManagerService extends SystemService { } else if (brightnessState > PowerManager.BRIGHTNESS_MAX) { brightnessState = PowerManager.BRIGHTNESS_MAX; } + return brightnessState; + } + + private void requestDisplayStateInternal(int displayId, int state, float brightnessState, + float sdrBrightnessState) { + if (state == Display.STATE_UNKNOWN) { + state = Display.STATE_ON; + } + + brightnessState = clampBrightness(state, brightnessState); + sdrBrightnessState = clampBrightness(state, sdrBrightnessState); // Update the display state within the lock. // Note that we do not need to schedule traversals here although it @@ -688,20 +697,26 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { final int index = mDisplayStates.indexOfKey(displayId); + final BrightnessPair brightnessPair = + index < 0 ? null : mDisplayBrightnesses.valueAt(index); if (index < 0 || (mDisplayStates.valueAt(index) == state - && BrightnessSynchronizer.floatEquals(mDisplayBrightnesses.valueAt(index), - brightnessState))) { + && BrightnessSynchronizer.floatEquals( + brightnessPair.brightness, brightnessState) + && BrightnessSynchronizer.floatEquals( + brightnessPair.sdrBrightness, sdrBrightnessState))) { return; // Display no longer exists or no change. } traceMessage = "requestDisplayStateInternal(" + displayId + ", " + Display.stateToString(state) - + ", brightness=" + brightnessState + ")"; + + ", brightness=" + brightnessState + + ", sdrBrightness=" + sdrBrightnessState + ")"; Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, traceMessage, displayId); mDisplayStates.setValueAt(index, state); - mDisplayBrightnesses.setValueAt(index, brightnessState); + brightnessPair.brightness = brightnessState; + brightnessPair.sdrBrightness = sdrBrightnessState; runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId) .getPrimaryDisplayDeviceLocked()); } @@ -1235,7 +1250,9 @@ public final class DisplayManagerService extends SystemService { addDisplayPowerControllerLocked(display); mDisplayStates.append(displayId, Display.STATE_UNKNOWN); - mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault); + final float brightnessDefault = display.getDisplayInfoLocked().brightnessDefault; + mDisplayBrightnesses.append(displayId, + new BrightnessPair(brightnessDefault, brightnessDefault)); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); @@ -1265,11 +1282,6 @@ public final class DisplayManagerService extends SystemService { // this point. sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); scheduleTraversalLocked(false); - - DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); - if (dpc != null) { - dpc.onDisplayChanged(); - } } private void handleLogicalDisplayFrameRateOverridesChangedLocked( @@ -1301,6 +1313,11 @@ public final class DisplayManagerService extends SystemService { if (work != null) { mHandler.post(work); } + final int displayId = display.getDisplayIdLocked(); + DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); + if (dpc != null) { + dpc.onDisplayChanged(); + } handleLogicalDisplayChangedLocked(display); } @@ -1326,8 +1343,9 @@ public final class DisplayManagerService extends SystemService { // Only send a request for display state if display state has already been initialized. if (state != Display.STATE_UNKNOWN) { - final float brightness = mDisplayBrightnesses.get(displayId); - return device.requestDisplayStateLocked(state, brightness); + final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId); + return device.requestDisplayStateLocked(state, brightnessPair.brightness, + brightnessPair.sdrBrightness); } } return null; @@ -1935,10 +1953,11 @@ public final class DisplayManagerService extends SystemService { for (int i = 0; i < displayStateCount; i++) { final int displayId = mDisplayStates.keyAt(i); final int displayState = mDisplayStates.valueAt(i); - final float brightness = mDisplayBrightnesses.valueAt(i); + final BrightnessPair brightnessPair = mDisplayBrightnesses.valueAt(i); pw.println(" Display Id=" + displayId); pw.println(" Display State=" + Display.stateToString(displayState)); - pw.println(" Display Brightness=" + brightness); + pw.println(" Display Brightness=" + brightnessPair.brightness); + pw.println(" Display SdrBrightness=" + brightnessPair.sdrBrightness); } IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); @@ -3277,6 +3296,16 @@ public final class DisplayManagerService extends SystemService { } }; + private class BrightnessPair { + public float brightness; + public float sdrBrightness; + + BrightnessPair(float brightness, float sdrBrightness) { + this.brightness = brightness; + this.sdrBrightness = sdrBrightness; + } + } + /** * Functional interface for providing time. * TODO(b/184781936): merge with PowerManagerService.Clock @@ -3288,5 +3317,4 @@ public final class DisplayManagerService extends SystemService { */ long uptimeMillis(); } - } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 3340e3c73fa17..7a50a34ae4ada 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -38,6 +38,7 @@ import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.metrics.LogMaker; import android.net.Uri; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -61,6 +62,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; +import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; @@ -223,6 +225,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @GuardedBy("mCachedBrightnessInfo") private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo(); + private DisplayDevice mDisplayDevice; + // True if we should fade the screen while turning it off, false if we should play // a stylish color fade animation instead. private boolean mColorFadeFadesConfig; @@ -424,7 +428,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animators. private ObjectAnimator mColorFadeOnAnimator; private ObjectAnimator mColorFadeOffAnimator; - private RampAnimator mScreenBrightnessRampAnimator; + private DualRampAnimator mScreenBrightnessRampAnimator; private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener; // True if this DisplayPowerController has been stopped and should no longer be running. @@ -442,6 +446,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call Runnable onBrightnessChangeRunnable) { mLogicalDisplay = logicalDisplay; mDisplayId = mLogicalDisplay.getDisplayIdLocked(); + mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); mHandler = new DisplayControllerHandler(handler.getLooper()); if (mDisplayId == Display.DEFAULT_DISPLAY) { @@ -780,12 +785,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * when displays get swapped on foldable devices. For example, different brightness properties * of each display need to be properly reflected in AutomaticBrightnessController. */ + @GuardedBy("DisplayManagerService.mSyncRoot") public void onDisplayChanged() { - // TODO: b/175821789 - Support high brightness on multiple (folding) displays - mUniqueDisplayId = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); - mDisplayDeviceConfig = mLogicalDisplay.getPrimaryDisplayDeviceLocked() - .getDisplayDeviceConfig(); - loadAmbientLightSensor(); + final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); + if (device == null) { + Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: " + + mLogicalDisplay.getDisplayIdLocked()); + return; + } + + final String uniqueId = device.getUniqueId(); + final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); + final IBinder token = device.getDisplayTokenLocked(); + mHandler.post(() -> { + if (mDisplayDevice == device) { + return; + } + mDisplayDevice = device; + mUniqueDisplayId = uniqueId; + mDisplayDeviceConfig = config; + + loadAmbientLightSensor(); + mHbmController.resetHbmData(token, config.getHighBrightnessModeData()); + }); } /** @@ -855,8 +877,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mColorFadeOffAnimator.addListener(mAnimatorListener); } - mScreenBrightnessRampAnimator = new RampAnimator<>( - mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT); + mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState, + DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, + DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); noteScreenState(mPowerState.getScreenState()); @@ -902,6 +925,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ private void cleanupHandlerThreadAfterStop() { setProximitySensorEnabled(false); + mHbmController.stop(); mHandler.removeCallbacksAndMessages(null); if (mUnfinishedBusiness) { mCallbacks.releaseSuspendBlocker(); @@ -1205,9 +1229,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // timeout is about to expire. if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - brightnessState = Math.max(Math.min(brightnessState - - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT, - mScreenBrightnessDimConfig), PowerManager.BRIGHTNESS_MIN); + brightnessState = Math.max( + Math.min(brightnessState - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT, + mScreenBrightnessDimConfig), + PowerManager.BRIGHTNESS_MIN); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); } if (!mAppliedDimming) { @@ -1282,12 +1307,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // transformations to the brightness have pushed it outside of the currently // allowed range. float animateValue = clampScreenBrightness(brightnessState); + + // If there are any HDR layers on the screen, we have a special brightness value that we + // use instead. We still preserve the calculated brightness for Standard Dynamic Range + // (SDR) layers, but the main brightness value will be the one for HDR. + float sdrAnimateValue = animateValue; + if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR + && ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0 + || (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) { + animateValue = mHbmController.getHdrBrightnessValue(); + } + final float currentBrightness = mPowerState.getScreenBrightness(); + final float currentSdrBrightness = mPowerState.getSdrScreenBrightness(); if (isValidBrightnessValue(animateValue) - && !BrightnessSynchronizer.floatEquals(animateValue, currentBrightness)) { + && (!BrightnessSynchronizer.floatEquals(animateValue, currentBrightness) + || !BrightnessSynchronizer.floatEquals( + sdrAnimateValue, currentSdrBrightness))) { if (initialRampSkip || hasBrightnessBuckets || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) { - animateScreenBrightness(animateValue, SCREEN_ANIMATION_RATE_MINIMUM); + animateScreenBrightness(animateValue, sdrAnimateValue, + SCREEN_ANIMATION_RATE_MINIMUM); } else { boolean isIncreasing = animateValue > currentBrightness; final float rampSpeed; @@ -1300,7 +1340,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else { rampSpeed = mBrightnessRampRateFastDecrease; } - animateScreenBrightness(animateValue, rampSpeed); + animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed); } } @@ -1446,9 +1486,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private HighBrightnessModeController createHbmController() { final DisplayDeviceConfig ddConfig = mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); + final IBinder displayToken = + mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked(); final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; - return new HighBrightnessModeController(mHandler, PowerManager.BRIGHTNESS_MIN, + return new HighBrightnessModeController(mHandler, displayToken, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, () -> { sendUpdatePowerStateLocked(); @@ -1596,11 +1638,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && brightnessState <= PowerManager.BRIGHTNESS_MAX; } - private void animateScreenBrightness(float target, float rate) { + private void animateScreenBrightness(float target, float sdrTarget, float rate) { if (DEBUG) { - Slog.d(TAG, "Animating brightness: target=" + target +", rate=" + rate); + Slog.d(TAG, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget + + ", rate=" + rate); } - if (mScreenBrightnessRampAnimator.animateTo(target, rate)) { + if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) { Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target); // TODO(b/153319140) remove when we can get this from the above trace invocation SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target)); @@ -2295,6 +2338,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return; } handleSettingsChange(false /*userSwitch*/); + break; } } } @@ -2392,7 +2436,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call static final int MODIFIER_DIMMED = 0x1; static final int MODIFIER_LOW_POWER = 0x2; - static final int MODIFIER_MASK = 0x3; + static final int MODIFIER_HDR = 0x4; + static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR; // ADJUSTMENT_* // These things can happen at any point, even if the main brightness reason doesn't @@ -2464,6 +2509,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if ((modifier & MODIFIER_DIMMED) != 0) { sb.append(" dim"); } + if ((modifier & MODIFIER_HDR) != 0) { + sb.append(" hdr"); + } int strlen = sb.length(); if (sb.charAt(strlen - 1) == '[') { sb.setLength(strlen - 2); diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 77aff5b03dd29..b58dd38348aa0 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -62,6 +62,7 @@ final class DisplayPowerState { private int mScreenState; private float mScreenBrightness; + private float mSdrScreenBrightness; private boolean mScreenReady; private boolean mScreenUpdatePending; @@ -92,6 +93,7 @@ final class DisplayPowerState { mScreenState = displayState; mScreenBrightness = (displayState != Display.STATE_OFF) ? PowerManager.BRIGHTNESS_MAX : PowerManager.BRIGHTNESS_OFF_FLOAT; + mSdrScreenBrightness = mScreenBrightness; scheduleScreenUpdate(); mColorFadePrepared = false; @@ -126,6 +128,19 @@ final class DisplayPowerState { } }; + public static final FloatProperty SCREEN_SDR_BRIGHTNESS_FLOAT = + new FloatProperty("sdrScreenBrightnessFloat") { + @Override + public void setValue(DisplayPowerState object, float value) { + object.setSdrScreenBrightness(value); + } + + @Override + public Float get(DisplayPowerState object) { + return object.getSdrScreenBrightness(); + } + }; + /** * Sets whether the screen is on, off, or dozing. */ @@ -148,13 +163,39 @@ final class DisplayPowerState { return mScreenState; } + /** + * Sets the display's SDR brightness. + * + * @param brightness The brightness, ranges from 0.0f (minimum / off) to 1.0f (brightest). + */ + public void setSdrScreenBrightness(float brightness) { + if (!BrightnessSynchronizer.floatEquals(mSdrScreenBrightness, brightness)) { + if (DEBUG) { + Slog.d(TAG, "setSdrScreenBrightness: brightness=" + brightness); + } + + mSdrScreenBrightness = brightness; + if (mScreenState != Display.STATE_OFF) { + mScreenReady = false; + scheduleScreenUpdate(); + } + } + } + + /** + * Gets the screen SDR brightness. + */ + public float getSdrScreenBrightness() { + return mSdrScreenBrightness; + } + /** * Sets the display brightness. * * @param brightness The brightness, ranges from 0.0f (minimum / off) to 1.0f (brightest). */ public void setScreenBrightness(float brightness) { - if (mScreenBrightness != brightness) { + if (!BrightnessSynchronizer.floatEquals(mScreenBrightness, brightness)) { if (DEBUG) { Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); } @@ -286,6 +327,7 @@ final class DisplayPowerState { pw.println(" mStopped=" + mStopped); pw.println(" mScreenState=" + Display.stateToString(mScreenState)); pw.println(" mScreenBrightness=" + mScreenBrightness); + pw.println(" mSdrScreenBrightness=" + mSdrScreenBrightness); pw.println(" mScreenReady=" + mScreenReady); pw.println(" mScreenUpdatePending=" + mScreenUpdatePending); pw.println(" mColorFadePrepared=" + mColorFadePrepared); @@ -332,7 +374,10 @@ final class DisplayPowerState { float brightnessState = mScreenState != Display.STATE_OFF && mColorFadeLevel > 0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT; - if (mPhotonicModulator.setState(mScreenState, brightnessState)) { + float sdrBrightnessState = mScreenState != Display.STATE_OFF + && mColorFadeLevel > 0f + ? mSdrScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT; + if (mPhotonicModulator.setState(mScreenState, brightnessState, sdrBrightnessState)) { if (DEBUG) { Slog.d(TAG, "Screen ready"); } @@ -373,8 +418,10 @@ final class DisplayPowerState { private int mPendingState = INITIAL_SCREEN_STATE; private float mPendingBacklight = INITIAL_BACKLIGHT_FLOAT; + private float mPendingSdrBacklight = INITIAL_BACKLIGHT_FLOAT; private int mActualState = INITIAL_SCREEN_STATE; private float mActualBacklight = INITIAL_BACKLIGHT_FLOAT; + private float mActualSdrBacklight = INITIAL_BACKLIGHT_FLOAT; private boolean mStateChangeInProgress; private boolean mBacklightChangeInProgress; @@ -382,11 +429,13 @@ final class DisplayPowerState { super("PhotonicModulator"); } - public boolean setState(int state, float brightnessState) { + public boolean setState(int state, float brightnessState, float sdrBrightnessState) { synchronized (mLock) { boolean stateChanged = state != mPendingState; - boolean backlightChanged = !BrightnessSynchronizer.floatEquals( - brightnessState, mPendingBacklight); + boolean backlightChanged = + !BrightnessSynchronizer.floatEquals(brightnessState, mPendingBacklight) + || !BrightnessSynchronizer.floatEquals( + sdrBrightnessState, mPendingSdrBacklight); if (stateChanged || backlightChanged) { if (DEBUG) { Slog.d(TAG, "Requesting new screen state: state=" @@ -395,6 +444,7 @@ final class DisplayPowerState { mPendingState = state; mPendingBacklight = brightnessState; + mPendingSdrBacklight = sdrBrightnessState; boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress; mStateChangeInProgress = stateChanged || mStateChangeInProgress; mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress; @@ -413,8 +463,10 @@ final class DisplayPowerState { pw.println("Photonic Modulator State:"); pw.println(" mPendingState=" + Display.stateToString(mPendingState)); pw.println(" mPendingBacklight=" + mPendingBacklight); + pw.println(" mPendingSdrBacklight=" + mPendingSdrBacklight); pw.println(" mActualState=" + Display.stateToString(mActualState)); pw.println(" mActualBacklight=" + mActualBacklight); + pw.println(" mActualSdrBacklight=" + mActualSdrBacklight); pw.println(" mStateChangeInProgress=" + mStateChangeInProgress); pw.println(" mBacklightChangeInProgress=" + mBacklightChangeInProgress); } @@ -427,13 +479,17 @@ final class DisplayPowerState { final int state; final boolean stateChanged; final float brightnessState; + final float sdrBrightnessState; final boolean backlightChanged; synchronized (mLock) { state = mPendingState; stateChanged = (state != mActualState); brightnessState = mPendingBacklight; - backlightChanged = !BrightnessSynchronizer.floatEquals( - brightnessState, mActualBacklight); + sdrBrightnessState = mPendingSdrBacklight; + backlightChanged = + !BrightnessSynchronizer.floatEquals(brightnessState, mActualBacklight) + || !BrightnessSynchronizer.floatEquals( + sdrBrightnessState, mActualSdrBacklight); if (!stateChanged) { // State changed applied, notify outer class. postScreenUpdateThreadSafe(); @@ -454,14 +510,17 @@ final class DisplayPowerState { } mActualState = state; mActualBacklight = brightnessState; + mActualSdrBacklight = sdrBrightnessState; } // Apply pending change. if (DEBUG) { Slog.d(TAG, "Updating screen state: id=" + mDisplayId + ", state=" - + Display.stateToString(state) + ", backlight=" + brightnessState); + + Display.stateToString(state) + ", backlight=" + brightnessState + + ", sdrBacklight=" + sdrBrightnessState); } - mBlanker.requestDisplayState(mDisplayId, state, brightnessState); + mBlanker.requestDisplayState(mDisplayId, state, brightnessState, + sdrBrightnessState); } } } diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index e6486bd2a79a2..b9487779dfd35 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -18,9 +18,11 @@ package com.android.server.display; import android.hardware.display.BrightnessInfo; import android.os.Handler; +import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; import android.util.Slog; +import android.view.SurfaceControlHdrLayerInfoListener; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData; @@ -45,17 +47,21 @@ class HighBrightnessModeController { private final float mBrightnessMin; private final float mBrightnessMax; - private final HighBrightnessModeData mHbmData; private final Handler mHandler; private final Runnable mHbmChangeCallback; private final Runnable mRecalcRunnable; private final Clock mClock; + private SurfaceControlHdrLayerInfoListener mHdrListener; + private HighBrightnessModeData mHbmData; + private IBinder mRegisteredDisplayToken; + private boolean mIsInAllowedAmbientRange = false; private boolean mIsTimeAvailable = false; private boolean mIsAutoBrightnessEnabled = false; private float mAutoBrightness; private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; + private boolean mIsHdrLayerPresent = false; /** * If HBM is currently running, this is the start time for the current HBM session. @@ -69,23 +75,26 @@ class HighBrightnessModeController { */ private LinkedList mEvents = new LinkedList<>(); - HighBrightnessModeController(Handler handler, float brightnessMin, float brightnessMax, - HighBrightnessModeData hbmData, Runnable hbmChangeCallback) { - this(SystemClock::uptimeMillis, handler, brightnessMin, brightnessMax, hbmData, - hbmChangeCallback); + HighBrightnessModeController(Handler handler, IBinder displayToken, float brightnessMin, + float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback) { + this(SystemClock::uptimeMillis, handler, displayToken, brightnessMin, brightnessMax, + hbmData, hbmChangeCallback); } @VisibleForTesting - HighBrightnessModeController(Clock clock, Handler handler, float brightnessMin, - float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback) { + HighBrightnessModeController(Clock clock, Handler handler, IBinder displayToken, + float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, + Runnable hbmChangeCallback) { mClock = clock; mHandler = handler; mBrightnessMin = brightnessMin; mBrightnessMax = brightnessMax; - mHbmData = hbmData; mHbmChangeCallback = hbmChangeCallback; mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; mRecalcRunnable = this::recalculateTimeAllowance; + mHdrListener = new HdrListener(); + + resetHbmData(displayToken, hbmData); } void setAutoBrightnessEnabled(boolean isEnabled) { @@ -117,6 +126,10 @@ class HighBrightnessModeController { } } + float getHdrBrightnessValue() { + return mBrightnessMax; + } + void onAmbientLuxChange(float ambientLux) { if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) { return; @@ -138,11 +151,12 @@ class HighBrightnessModeController { // If we are starting or ending a high brightness mode session, store the current // session in mRunningStartTimeMillis, or the old one in mEvents. - final boolean wasOldBrightnessHigh = oldAutoBrightness > mHbmData.transitionPoint; - final boolean isNewBrightnessHigh = mAutoBrightness > mHbmData.transitionPoint; - if (wasOldBrightnessHigh != isNewBrightnessHigh) { + final boolean wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1; + final boolean shouldHbmDrainAvailableTime = mAutoBrightness > mHbmData.transitionPoint + && !mIsHdrLayerPresent; + if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) { final long currentTime = mClock.uptimeMillis(); - if (isNewBrightnessHigh) { + if (shouldHbmDrainAvailableTime) { mRunningStartTimeMillis = currentTime; } else { mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime)); @@ -161,30 +175,49 @@ class HighBrightnessModeController { return mHbmMode; } + void stop() { + registerHdrListener(null /*displayToken*/); + } + + void resetHbmData(IBinder displayToken, HighBrightnessModeData hbmData) { + mHbmData = hbmData; + unregisterHdrListener(); + if (deviceSupportsHbm()) { + registerHdrListener(displayToken); + recalculateTimeAllowance(); + } + } + void dump(PrintWriter pw) { pw.println("HighBrightnessModeController:"); - pw.println(" mBrightnessMin=" + mBrightnessMin); - pw.println(" mBrightnessMax=" + mBrightnessMax); + pw.println(" mCurrentMin=" + getCurrentBrightnessMin()); + pw.println(" mCurrentMax=" + getCurrentBrightnessMax()); + pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode)); + pw.println(" remainingTime=" + calculateRemainingTime(mClock.uptimeMillis())); pw.println(" mHbmData=" + mHbmData); pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange); pw.println(" mIsTimeAvailable= " + mIsTimeAvailable); pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled); pw.println(" mAutoBrightness=" + mAutoBrightness); + pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent); + pw.println(" mBrightnessMin=" + mBrightnessMin); + pw.println(" mBrightnessMax=" + mBrightnessMax); } private boolean isCurrentlyAllowed() { - return mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange; + return mIsHdrLayerPresent + || (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange); } private boolean deviceSupportsHbm() { return mHbmData != null; } - /** - * Recalculates the allowable HBM time. - */ - private void recalculateTimeAllowance() { - final long currentTime = mClock.uptimeMillis(); + private long calculateRemainingTime(long currentTime) { + if (!deviceSupportsHbm()) { + return 0; + } + long timeAlreadyUsed = 0; // First, lets see how much time we've taken for any currently running @@ -222,8 +255,15 @@ class HighBrightnessModeController { Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed); } - // See how much allowable time we have left. - final long remainingTime = Math.max(0, mHbmData.timeMaxMillis - timeAlreadyUsed); + return Math.max(0, mHbmData.timeMaxMillis - timeAlreadyUsed); + } + + /** + * Recalculates the allowable HBM time. + */ + private void recalculateTimeAllowance() { + final long currentTime = mClock.uptimeMillis(); + final long remainingTime = calculateRemainingTime(currentTime); // We allow HBM if there is more than the minimum required time available // or if brightness is already in the high range, if there is any time left at all. @@ -242,6 +282,7 @@ class HighBrightnessModeController { // If we are not allowed...timeout when the oldest event moved outside of the timing // window by at least minTime. Basically, we're calculating the soonest time we can // get {@code timeMinMillis} back to us. + final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis; final HbmEvent lastEvent = mEvents.getLast(); final long startTimePlusMinMillis = Math.max(windowstartTimeMillis, lastEvent.startTimeMillis) @@ -278,12 +319,36 @@ class HighBrightnessModeController { } private int calculateHighBrightnessMode() { - if (deviceSupportsHbm() && isCurrentlyAllowed()) { + if (!deviceSupportsHbm()) { + return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; + } else if (mIsHdrLayerPresent) { + return BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR; + } else if (isCurrentlyAllowed()) { return BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; } + return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; } + private void registerHdrListener(IBinder displayToken) { + if (mRegisteredDisplayToken == displayToken) { + return; + } + + unregisterHdrListener(); + mRegisteredDisplayToken = displayToken; + if (mRegisteredDisplayToken != null) { + mHdrListener.register(mRegisteredDisplayToken); + } + } + + private void unregisterHdrListener() { + if (mRegisteredDisplayToken != null) { + mHdrListener.unregister(mRegisteredDisplayToken); + mIsHdrLayerPresent = false; + } + } + /** * Represents an event in which High Brightness Mode was enabled. */ @@ -302,4 +367,18 @@ class HighBrightnessModeController { + ((endTimeMillis - startTimeMillis) / 1000) + "]"; } } + + private class HdrListener extends SurfaceControlHdrLayerInfoListener { + @Override + public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, + int maxW, int maxH, int flags) { + mHandler.post(() -> { + mIsHdrLayerPresent = numberOfHdrLayers > 0; + // Calling the auto-brightness update so that we can recalculate + // auto-brightness with HDR in mind. When HDR layers are present, + // we don't limit auto-brightness' HBM time limits. + onAutoBrightnessChanged(mAutoBrightness); + }); + } + } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 2546118f1cc78..754e35eff6c16 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -67,6 +67,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private static final int NO_DISPLAY_MODE_ID = 0; + private static final float NITS_INVALID = -1; + private final LongSparseArray mDevices = new LongSparseArray<>(); private final Injector mInjector; @@ -190,6 +192,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mState = Display.STATE_UNKNOWN; // This is only set in the runnable returned from requestDisplayStateLocked. private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; + private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private int mDefaultModeId; private int mDefaultModeGroup; private int mActiveModeId; @@ -644,13 +647,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public Runnable requestDisplayStateLocked(final int state, final float brightnessState) { + public Runnable requestDisplayStateLocked(final int state, final float brightnessState, + final float sdrBrightnessState) { // Assume that the brightness is off if the display is being turned off. assert state != Display.STATE_OFF || BrightnessSynchronizer.floatEquals( brightnessState, PowerManager.BRIGHTNESS_OFF_FLOAT); final boolean stateChanged = (mState != state); - final boolean brightnessChanged = (!BrightnessSynchronizer.floatEquals( - mBrightnessState, brightnessState)); + final boolean brightnessChanged = + !(BrightnessSynchronizer.floatEquals(mBrightnessState, brightnessState) + && BrightnessSynchronizer.floatEquals(mSdrBrightnessState, sdrBrightnessState)); if (stateChanged || brightnessChanged) { final long physicalDisplayId = mPhysicalDisplayId; final IBinder token = getDisplayTokenLocked(); @@ -702,8 +707,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Apply brightness changes given that we are in a non-suspended state. if (brightnessChanged || vrModeChange) { - setDisplayBrightness(brightnessState); + setDisplayBrightness(brightnessState, sdrBrightnessState); mBrightnessState = brightnessState; + mSdrBrightnessState = sdrBrightnessState; } // Enter the final desired state, possibly suspended. @@ -764,8 +770,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - private void setDisplayBrightness(float brightness) { - // Ensure brightnessState is valid, before processing and sending to + private void setDisplayBrightness(float brightness, float sdrBrightness) { + // Ensure brightnessState is valid before processing and sending to // surface control if (Float.isNaN(brightness)) { return; @@ -774,17 +780,31 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (DEBUG) { Slog.d(TAG, "setDisplayBrightness(" + "id=" + physicalDisplayId - + ", brightness=" + brightness + ")"); + + ", brightness=" + brightness + + ", sdrBrightness=" + sdrBrightness + ")"); } Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" - + "id=" + physicalDisplayId + ", brightness=" + brightness + ")"); + + "id=" + physicalDisplayId + ", brightness=" + brightness + + ", sdrBrightness=" + sdrBrightness + ")"); try { - float backlight = brightnessToBacklight(brightness); - mBacklightAdapter.setBacklight(backlight); + final float backlight = brightnessToBacklight(brightness); + float nits = NITS_INVALID; + float sdrBacklight = PowerManager.BRIGHTNESS_INVALID_FLOAT; + float sdrNits = NITS_INVALID; + if (getDisplayDeviceConfig().hasNitsMapping() + && sdrBrightness != PowerManager.BRIGHTNESS_INVALID_FLOAT) { + nits = backlightToNits(backlight); + sdrBacklight = brightnessToBacklight(sdrBrightness); + sdrNits = backlightToNits(sdrBacklight); + } + mBacklightAdapter.setBacklight(sdrBacklight, sdrNits, backlight, nits); Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", BrightnessSynchronizer.brightnessFloatToInt(brightness)); + Trace.traceCounter(Trace.TRACE_TAG_POWER, + "SdrScreenBrightness", + BrightnessSynchronizer.brightnessFloatToInt(sdrBrightness)); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -793,6 +813,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { private float brightnessToBacklight(float brightness) { return getDisplayDeviceConfig().getBacklightFromBrightness(brightness); } + + private float backlightToNits(float backlight) { + return getDisplayDeviceConfig().getNitsFromBacklight(backlight); + } }; } return null; @@ -1242,6 +1266,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { public boolean setDisplayBrightness(IBinder displayToken, float brightness) { return SurfaceControl.setDisplayBrightness(displayToken, brightness); } + + public boolean setDisplayBrightness(IBinder displayToken, float sdrBacklight, + float sdrNits, float displayBacklight, float displayNits) { + return SurfaceControl.setDisplayBrightness(displayToken, sdrBacklight, sdrNits, + displayBacklight, displayNits); + } + } static class BacklightAdapter { @@ -1273,9 +1304,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { } // Set backlight within min and max backlight values - void setBacklight(float backlight) { + void setBacklight(float sdrBacklight, float sdrNits, float backlight, float nits) { if (mUseSurfaceControlBrightness || mForceSurfaceControl) { - mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight); + if (sdrBacklight == PowerManager.BRIGHTNESS_INVALID_FLOAT) { + mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight); + } else { + mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, sdrBacklight, sdrNits, + backlight, nits); + } } else if (mBacklight != null) { mBacklight.setBrightness(backlight); } diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java index 26004a8da1a13..20feafa2d19cc 100644 --- a/services/core/java/com/android/server/display/RampAnimator.java +++ b/services/core/java/com/android/server/display/RampAnimator.java @@ -26,7 +26,7 @@ import com.android.internal.display.BrightnessSynchronizer; * A custom animator that progressively updates a property value at * a given variable rate until it reaches a particular target value. */ -final class RampAnimator { +class RampAnimator { private final T mObject; private final FloatProperty mProperty; private final Choreographer mChoreographer; @@ -174,4 +174,52 @@ final class RampAnimator { public interface Listener { void onAnimationEnd(); } + + static class DualRampAnimator { + private final RampAnimator mFirst; + private final RampAnimator mSecond; + private final Listener mInternalListener = new Listener() { + @Override + public void onAnimationEnd() { + if (mListener != null && !isAnimating()) { + mListener.onAnimationEnd(); + } + } + }; + + private Listener mListener; + + DualRampAnimator(T object, FloatProperty firstProperty, + FloatProperty secondProperty) { + mFirst = new RampAnimator(object, firstProperty); + mFirst.setListener(mInternalListener); + mSecond = new RampAnimator(object, secondProperty); + mSecond.setListener(mInternalListener); + } + + /** + * Starts animating towards the specified values. + * + * If this is the first time the property is being set or if the rate is 0, + * the value jumps directly to the target. + * + * @param firstTarget The first target value. + * @param secondTarget The second target value. + * @param rate The convergence rate in units per second, or 0 to set the value immediately. + * @return True if either target differs from the previous target. + */ + public boolean animateTo(float firstTarget, float secondTarget, float rate) { + final boolean firstRetval = mFirst.animateTo(firstTarget, rate); + final boolean secondRetval = mSecond.animateTo(secondTarget, rate); + return firstRetval && secondRetval; + } + + public void setListener(Listener listener) { + mListener = listener; + } + + public boolean isAnimating() { + return mFirst.isAnimating() && mSecond.isAnimating(); + } + } } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 52a810bd8caac..b7931c8a8424f 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -293,7 +293,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } @Override - public Runnable requestDisplayStateLocked(int state, float brightnessState) { + public Runnable requestDisplayStateLocked(int state, float brightnessState, + float sdrBrightnessState) { if (state != mDisplayState) { mDisplayState = state; if (state == Display.STATE_OFF) { diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index f1d8e6c167d7a..e1012a9a4cada 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -649,14 +649,14 @@ public class LocalDisplayAdapterTest { // Test as default display BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, mSurfaceControlProxy); - ba.setBacklight(0.514f); - verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f); + ba.setBacklight(0.514f, 100f, 0.614f, 500f); + verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f, 100f, 0.614f, 500f); // Test as not default display BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/, mSurfaceControlProxy); - ba2.setBacklight(0.323f); - verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f); + ba2.setBacklight(0.323f, 101f, 0.723f, 601f); + verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f, 101f, 0.723f, 601f); } @Test @@ -668,7 +668,7 @@ public class LocalDisplayAdapterTest { BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, mSurfaceControlProxy); - ba.setBacklight(0.123f); + ba.setBacklight(1f, 1f, 0.123f, 1f); verify(mMockedBacklight).setBrightness(0.123f); } @@ -681,7 +681,7 @@ public class LocalDisplayAdapterTest { BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/, mSurfaceControlProxy); - ba.setBacklight(0.456f); + ba.setBacklight(0.456f, 1f, 1f, 1f); // Adapter does not forward any brightness in this case. verify(mMockedBacklight, never()).setBrightness(anyFloat()); diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java index 88a21b4a8fa89..8e4cdc91d0e62 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -21,6 +21,7 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLI import static org.junit.Assert.assertEquals; +import android.os.Binder; import android.os.Handler; import android.os.Message; import android.os.test.TestLooper; @@ -55,6 +56,7 @@ public class HighBrightnessModeControllerTest { private OffsettableClock mClock; private TestLooper mTestLooper; private Handler mHandler; + private Binder mDisplayToken; private static final HighBrightnessModeData DEFAULT_HBM_DATA = new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS, @@ -64,6 +66,7 @@ public class HighBrightnessModeControllerTest { public void setUp() { mClock = new OffsettableClock.Stopped(); mTestLooper = new TestLooper(mClock::now); + mDisplayToken = null; mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { @@ -79,14 +82,14 @@ public class HighBrightnessModeControllerTest { @Test public void testNoHbmData() { final HighBrightnessModeController hbmc = new HighBrightnessModeController( - mClock::now, mHandler, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}); + mClock::now, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); } @Test public void testNoHbmData_Enabled() { final HighBrightnessModeController hbmc = new HighBrightnessModeController( - mClock::now, mHandler, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}); + mClock::now, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}); hbmc.setAutoBrightnessEnabled(true); hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); @@ -264,8 +267,8 @@ public class HighBrightnessModeControllerTest { // Creates instance with standard initialization values. private HighBrightnessModeController createDefaultHbm() { - return new HighBrightnessModeController(mClock::now, mHandler, DEFAULT_MIN, DEFAULT_MAX, - DEFAULT_HBM_DATA, () -> {}); + return new HighBrightnessModeController(mClock::now, mHandler, mDisplayToken, DEFAULT_MIN, + DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {}); } private void advanceTime(long timeMs) {