diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java index da51569ee5cce..1c1b69b647236 100644 --- a/services/core/java/com/android/server/display/DisplayBrightnessState.java +++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java @@ -133,6 +133,13 @@ public final class DisplayBrightnessState { mShouldUseAutoBrightness, mIsSlowChange); } + /** + * Helper methods to create builder + */ + public static Builder builder() { + return new Builder(); + } + /** * A DisplayBrightnessState's builder class. */ diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 32c37b03d5915..4db8777ba6058 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -347,7 +347,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private boolean mDozing; private boolean mAppliedDimming; - private boolean mAppliedLowPower; + private boolean mAppliedThrottling; // Reason for which the brightness was last changed. See {@link BrightnessReason} for more @@ -1465,24 +1465,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal slowChange = false; mAppliedDimming = false; } - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor - // as long as it is above the minimum threshold. - if (mPowerRequest.lowPowerMode) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { - final float brightnessFactor = - Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); - final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); - brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN); - mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER); - } - if (!mAppliedLowPower) { - slowChange = false; - } - mAppliedLowPower = true; - } else if (mAppliedLowPower) { - slowChange = false; - mAppliedLowPower = false; - } + + DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, + brightnessState, slowChange); + + brightnessState = clampedState.getBrightness(); + slowChange = clampedState.isSlowChange(); + mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier()); // The current brightness to use has been calculated at this point, and HbmController should // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it @@ -1540,8 +1529,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // allowed range. float animateValue = clampScreenBrightness(brightnessState); - animateValue = mBrightnessClamperController.clamp(animateValue); - // 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. @@ -2408,7 +2395,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal pw.println(" mPowerRequest=" + mPowerRequest); pw.println(" mBrightnessReason=" + mBrightnessReason); pw.println(" mAppliedDimming=" + mAppliedDimming); - pw.println(" mAppliedLowPower=" + mAppliedLowPower); pw.println(" mAppliedThrottling=" + mAppliedThrottling); pw.println(" mDozing=" + mDozing); pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState)); diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java index 9345a3d971227..54a280fddb6cf 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java @@ -21,6 +21,9 @@ import android.os.PowerManager; import java.io.PrintWriter; +/** + * Provides max allowed brightness + */ abstract class BrightnessClamper { protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java index d0f28c3bea81e..f19d00b82aabd 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java @@ -20,6 +20,7 @@ import static com.android.server.display.brightness.clamper.BrightnessClamper.Ty import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.display.DisplayManagerInternal; import android.os.Handler; import android.os.HandlerExecutor; import android.os.PowerManager; @@ -28,6 +29,7 @@ import android.provider.DeviceConfigInterface; import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; import com.android.server.display.feature.DeviceConfigParameterProvider; @@ -42,7 +44,7 @@ import java.util.concurrent.Executor; */ public class BrightnessClamperController { - private static final boolean ENABLED = false; + private static final boolean THERMAL_ENABLED = false; private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; private final Handler mHandler; @@ -50,6 +52,8 @@ public class BrightnessClamperController { private final Executor mExecutor; private final List> mClampers = new ArrayList<>(); + + private final List mModifiers = new ArrayList<>(); private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged); private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; @@ -77,11 +81,12 @@ public class BrightnessClamperController { } }; - if (ENABLED) { + if (THERMAL_ENABLED) { mClampers.add( new BrightnessThermalClamper(handler, clamperChangeListenerInternal, data)); - start(); } + mModifiers.add(new BrightnessLowPowerModeModifier()); + start(); } /** @@ -95,8 +100,18 @@ public class BrightnessClamperController { * Applies clamping * Called in DisplayControllerHandler */ - public float clamp(float value) { - return Math.min(value, mBrightnessCap); + public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request, + float brightnessValue, boolean slowChange) { + float cappedBrightness = Math.min(brightnessValue, mBrightnessCap); + + DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder(); + builder.setIsSlowChange(slowChange); + builder.setBrightness(cappedBrightness); + + for (int i = 0; i < mModifiers.size(); i++) { + mModifiers.get(i).apply(request, builder); + } + return builder.build(); } /** @@ -108,6 +123,7 @@ public class BrightnessClamperController { writer.println(" mClamperType: " + mClamperType); IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); mClampers.forEach(clamper -> clamper.dump(ipw)); + mModifiers.forEach(modifier -> modifier.dump(ipw)); } /** @@ -144,8 +160,10 @@ public class BrightnessClamperController { } private void start() { - mDeviceConfigParameterProvider.addOnPropertiesChangedListener( - mExecutor, mOnPropertiesChangedListener); + if (!mClampers.isEmpty()) { + mDeviceConfigParameterProvider.addOnPropertiesChangedListener( + mExecutor, mOnPropertiesChangedListener); + } } /** diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java new file mode 100644 index 0000000000000..f48ad2f19d6b6 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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.display.brightness.clamper; + +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManager; + +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; + +import java.io.PrintWriter; + +class BrightnessLowPowerModeModifier implements BrightnessModifier { + + private boolean mAppliedLowPower = false; + + @Override + public void apply(DisplayManagerInternal.DisplayPowerRequest request, + DisplayBrightnessState.Builder stateBuilder) { + // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor + // as long as it is above the minimum threshold. + if (request.lowPowerMode) { + float value = stateBuilder.getBrightness(); + if (value > PowerManager.BRIGHTNESS_MIN) { + final float brightnessFactor = + Math.min(request.screenLowPowerBrightnessFactor, 1); + final float lowPowerBrightnessFloat = Math.max((value * brightnessFactor), + PowerManager.BRIGHTNESS_MIN); + stateBuilder.setBrightness(lowPowerBrightnessFloat); + stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_LOW_POWER); + } + if (!mAppliedLowPower) { + stateBuilder.setIsSlowChange(false); + } + mAppliedLowPower = true; + } else if (mAppliedLowPower) { + stateBuilder.setIsSlowChange(false); + mAppliedLowPower = false; + } + } + + @Override + public void dump(PrintWriter pw) { + pw.println("BrightnessLowPowerModeModifier:"); + pw.println(" mAppliedLowPower=" + mAppliedLowPower); + } +} diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java new file mode 100644 index 0000000000000..3a33df657ff85 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.display.brightness.clamper; + +import android.hardware.display.DisplayManagerInternal; + +import com.android.server.display.DisplayBrightnessState; + +import java.io.PrintWriter; + +/** + * Modifies current brightness based on request + */ +interface BrightnessModifier { + + void apply(DisplayManagerInternal.DisplayPowerRequest request, + DisplayBrightnessState.Builder builder); + + void dump(PrintWriter pw); +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java new file mode 100644 index 0000000000000..266f5c198f039 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifierTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 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.display.brightness.clamper; + +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import androidx.test.filters.SmallTest; + +import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.BrightnessReason; + +import org.junit.Before; +import org.junit.Test; + +@SmallTest +public class BrightnessLowPowerModeModifierTest { + private static final float FLOAT_TOLERANCE = 0.001f; + private static final float DEFAULT_BRIGHTNESS = 0.5f; + private static final float LOW_POWER_BRIGHTNESS_FACTOR = 0.8f; + private static final float EXPECTED_LOW_POWER_BRIGHTNESS = + DEFAULT_BRIGHTNESS * LOW_POWER_BRIGHTNESS_FACTOR; + private final DisplayPowerRequest mRequest = new DisplayPowerRequest(); + private final DisplayBrightnessState.Builder mBuilder = prepareBuilder(); + private BrightnessLowPowerModeModifier mClamper; + + @Before + public void setUp() { + mClamper = new BrightnessLowPowerModeModifier(); + mRequest.screenLowPowerBrightnessFactor = LOW_POWER_BRIGHTNESS_FACTOR; + mRequest.lowPowerMode = true; + } + + @Test + public void testApply_lowPowerModeOff() { + mRequest.lowPowerMode = false; + + mClamper.apply(mRequest, mBuilder); + + assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(0, mBuilder.getBrightnessReason().getModifier()); + assertTrue(mBuilder.isSlowChange()); + } + + @Test + public void testApply_lowPowerModeOn() { + mClamper.apply(mRequest, mBuilder); + + assertEquals(EXPECTED_LOW_POWER_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(BrightnessReason.MODIFIER_LOW_POWER, + mBuilder.getBrightnessReason().getModifier()); + assertFalse(mBuilder.isSlowChange()); + } + + @Test + public void testApply_lowPowerModeOnAndLowPowerBrightnessFactorHigh() { + mRequest.screenLowPowerBrightnessFactor = 1.1f; + + mClamper.apply(mRequest, mBuilder); + + assertEquals(DEFAULT_BRIGHTNESS, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(BrightnessReason.MODIFIER_LOW_POWER, + mBuilder.getBrightnessReason().getModifier()); + assertFalse(mBuilder.isSlowChange()); + } + + @Test + public void testApply_lowPowerModeOnAndMinBrightness() { + mBuilder.setBrightness(0.0f); + mClamper.apply(mRequest, mBuilder); + + assertEquals(0.0f, mBuilder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(0, mBuilder.getBrightnessReason().getModifier()); + assertFalse(mBuilder.isSlowChange()); + } + + @Test + public void testApply_lowPowerModeOnAndLowPowerAlreadyApplied() { + mClamper.apply(mRequest, mBuilder); + DisplayBrightnessState.Builder builder = prepareBuilder(); + + mClamper.apply(mRequest, builder); + + assertEquals(EXPECTED_LOW_POWER_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(BrightnessReason.MODIFIER_LOW_POWER, + builder.getBrightnessReason().getModifier()); + assertTrue(builder.isSlowChange()); + } + + @Test + public void testApply_lowPowerModeOffAfterLowPowerOn() { + mClamper.apply(mRequest, mBuilder); + mRequest.lowPowerMode = false; + DisplayBrightnessState.Builder builder = prepareBuilder(); + + mClamper.apply(mRequest, builder); + + assertEquals(DEFAULT_BRIGHTNESS, builder.getBrightness(), FLOAT_TOLERANCE); + assertEquals(0, builder.getBrightnessReason().getModifier()); + assertFalse(builder.isSlowChange()); + } + + private DisplayBrightnessState.Builder prepareBuilder() { + DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder(); + builder.setBrightness(DEFAULT_BRIGHTNESS); + builder.setIsSlowChange(true); + return builder; + } +}