Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 161958d2 authored by petsjonkin's avatar petsjonkin
Browse files

Introducing HDRClamper

Bug: b/283447291
Test: atest HdrClamperTest, see bug comment 9 for manual testing

Change-Id: I9e0ae3a02cd9427644ea133a131010ff5a4763ae
parent 6035cef0
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
package com.android.server.display;

import android.hardware.display.BrightnessInfo;
import android.os.Handler;
import android.os.IBinder;
import android.provider.DeviceConfigInterface;

import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.feature.DeviceConfigParameterProvider;

import java.io.PrintWriter;
@@ -31,23 +33,30 @@ class BrightnessRangeController {
    private final NormalBrightnessModeController mNormalBrightnessModeController =
            new NormalBrightnessModeController();

    private final HdrClamper mHdrClamper;

    private final Runnable mModeChangeCallback;
    private final boolean mUseNbmController;

    private final boolean mUseHdrClamper;


    BrightnessRangeController(HighBrightnessModeController hbmController,
            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler) {
        this(hbmController, modeChangeCallback, displayDeviceConfig,
                new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())),
                new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
    }

    BrightnessRangeController(HighBrightnessModeController hbmController,
            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
            DeviceConfigParameterProvider configParameterProvider) {
            HdrClamper hdrClamper, DeviceConfigParameterProvider configParameterProvider) {
        mHbmController = hbmController;
        mModeChangeCallback = modeChangeCallback;
        mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
        mUseHdrClamper = false;
        mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
        mHdrClamper = hdrClamper;
    }

    void dump(PrintWriter pw) {
@@ -63,6 +72,9 @@ class BrightnessRangeController {
                () -> mNormalBrightnessModeController.onAmbientLuxChange(ambientLux),
                () -> mHbmController.onAmbientLuxChange(ambientLux)
        );
        if (mUseHdrClamper) {
            mHdrClamper.onAmbientLuxChange(ambientLux);
        }
    }

    float getNormalBrightnessMax() {
@@ -118,7 +130,8 @@ class BrightnessRangeController {
    }

    float getHdrBrightnessValue() {
        return mHbmController.getHdrBrightnessValue();
        float hdrBrightness =  mHbmController.getHdrBrightnessValue();
        return Math.min(hdrBrightness, mHdrClamper.getMaxBrightness());
    }

    float getTransitionPoint() {
@@ -138,4 +151,8 @@ class BrightnessRangeController {
            hbmChangesFunc.run();
        }
    }

    public float getHdrTransitionRate() {
        return mHdrClamper.getTransitionRate();
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -677,7 +677,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);

        mBrightnessRangeController = new BrightnessRangeController(hbmController,
                modeChangeCallback, mDisplayDeviceConfig);
                modeChangeCallback, mDisplayDeviceConfig, mHandler);

        mBrightnessThrottler = createBrightnessThrottlerLocked();

+16 −2
Original line number Diff line number Diff line
@@ -539,8 +539,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                modeChangeCallback);
        mBrightnessThrottler = createBrightnessThrottlerLocked();

        mBrightnessRangeController = new BrightnessRangeController(hbmController,
                modeChangeCallback, mDisplayDeviceConfig);
        mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
                modeChangeCallback, mDisplayDeviceConfig, mHandler);

        mDisplayBrightnessController =
                new DisplayBrightnessController(context, null,
@@ -1497,6 +1497,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
            // allowed range.
            float animateValue = clampScreenBrightness(brightnessState);

            // custom transition duration
            float customTransitionRate = -1f;

            // 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.
@@ -1511,6 +1514,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                // We want to scale HDR brightness level with the SDR level, we also need to restore
                // SDR brightness immediately when entering dim or low power mode.
                animateValue = mBrightnessRangeController.getHdrBrightnessValue();
                customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
            }

            final float currentBrightness = mPowerState.getScreenBrightness();
@@ -1523,6 +1527,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                        || !isDisplayContentVisible || brightnessIsTemporary) {
                    animateScreenBrightness(animateValue, sdrAnimateValue,
                            SCREEN_ANIMATION_RATE_MINIMUM);
                } else if (customTransitionRate > 0) {
                    animateScreenBrightness(animateValue, sdrAnimateValue,
                            customTransitionRate);
                } else {
                    boolean isIncreasing = animateValue > currentBrightness;
                    final float rampSpeed;
@@ -2968,6 +2975,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
                    hbmChangeCallback, hbmMetadata, context);
        }

        BrightnessRangeController getBrightnessRangeController(
                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
                DisplayDeviceConfig displayDeviceConfig, Handler handler) {
            return new BrightnessRangeController(hbmController,
                    modeChangeCallback, displayDeviceConfig, handler);
        }

        DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
                SensorManager sensorManager, Resources resources) {
            return DisplayWhiteBalanceFactory.create(handler,
+132 −0
Original line number Diff line number Diff line
/*
 * 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.os.Handler;
import android.os.PowerManager;

import com.android.internal.annotations.VisibleForTesting;

import java.util.HashMap;
import java.util.Map;

public class HdrClamper {

    private final Configuration mConfiguration = new Configuration();

    private final BrightnessClamperController.ClamperChangeListener mClamperChangeListener;

    private final Handler mHandler;

    private final Runnable mDebouncer;

    private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;

    // brightness change speed, in units per seconds,
    private float mTransitionRate = -1f;

    private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;

    private float mDesiredTransitionDuration = -1; // in seconds

    public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
            Handler handler) {
        mClamperChangeListener = clamperChangeListener;
        mHandler = handler;
        mDebouncer = () -> {
            mTransitionRate = Math.abs((mMaxBrightness - mDesiredMaxBrightness)
                    / mDesiredTransitionDuration);
            mMaxBrightness = mDesiredMaxBrightness;
            mClamperChangeListener.onChanged();
        };
    }

    // Called in same looper: mHandler.getLooper()
    public float getMaxBrightness() {
        return mMaxBrightness;
    }

    // Called in same looper: mHandler.getLooper()
    public float getTransitionRate() {
        return mTransitionRate;
    }


    /**
     * Updates brightness cap in response to ambient lux change.
     * Called by ABC in same looper: mHandler.getLooper()
     */
    public void onAmbientLuxChange(float ambientLux) {
        float expectedMaxBrightness = findBrightnessLimit(ambientLux);
        if (mMaxBrightness == expectedMaxBrightness) {
            mDesiredMaxBrightness = mMaxBrightness;
            mDesiredTransitionDuration = -1;
            mTransitionRate = -1f;
            mHandler.removeCallbacks(mDebouncer);
        } else if (mDesiredMaxBrightness != expectedMaxBrightness) {
            mDesiredMaxBrightness = expectedMaxBrightness;
            long debounceTime;
            if (mDesiredMaxBrightness > mMaxBrightness) {
                debounceTime = mConfiguration.mIncreaseConfig.mDebounceTimeMillis;
                mDesiredTransitionDuration =
                        (float) mConfiguration.mIncreaseConfig.mTransitionTimeMillis / 1000;
            } else {
                debounceTime = mConfiguration.mDecreaseConfig.mDebounceTimeMillis;
                mDesiredTransitionDuration =
                        (float) mConfiguration.mDecreaseConfig.mTransitionTimeMillis / 1000;
            }

            mHandler.removeCallbacks(mDebouncer);
            mHandler.postDelayed(mDebouncer, debounceTime);
        }
    }

    @VisibleForTesting
    Configuration getConfiguration() {
        return mConfiguration;
    }

    private float findBrightnessLimit(float ambientLux) {
        float foundAmbientBoundary = Float.MAX_VALUE;
        float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
        for (Map.Entry<Float, Float> brightnessPoint :
                mConfiguration.mMaxBrightnessLimits.entrySet()) {
            float ambientBoundary = brightnessPoint.getKey();
            // find ambient lux upper boundary closest to current ambient lux
            if (ambientBoundary > ambientLux && ambientBoundary < foundAmbientBoundary) {
                foundMaxBrightness = brightnessPoint.getValue();
                foundAmbientBoundary = ambientBoundary;
            }
        }
        return foundMaxBrightness;
    }

    @VisibleForTesting
    static class Configuration {
        final Map<Float, Float> mMaxBrightnessLimits = new HashMap<>();
        final TransitionConfiguration mIncreaseConfig = new TransitionConfiguration();

        final TransitionConfiguration mDecreaseConfig = new TransitionConfiguration();
    }

    @VisibleForTesting
    static class TransitionConfiguration {
        long mDebounceTimeMillis;

        long mTransitionTimeMillis;
    }
}
+50 −3
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -67,7 +68,9 @@ import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
@@ -1175,6 +1178,26 @@ public final class DisplayPowerController2Test {
        verify(mHolder.displayPowerState, times(1)).stop();
    }

    @Test
    public void testRampRateForHdrContent() {
        float clampedBrightness = 0.6f;
        float transitionRate = 35.5f;

        DisplayPowerRequest dpr = new DisplayPowerRequest();
        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
        when(mHolder.hbmController.getHighBrightnessMode()).thenReturn(
                BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR);
        when(mHolder.hbmController.getHdrBrightnessValue()).thenReturn(PowerManager.BRIGHTNESS_MAX);
        when(mHolder.hdrClamper.getMaxBrightness()).thenReturn(clampedBrightness);
        when(mHolder.hdrClamper.getTransitionRate()).thenReturn(transitionRate);

        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
        advanceTime(1); // Run updatePowerState

        verify(mHolder.animator, atLeastOnce()).animateTo(eq(clampedBrightness), anyFloat(),
                eq(transitionRate));
    }

    /**
     * Creates a mock and registers it to {@link LocalServices}.
     */
@@ -1270,12 +1293,16 @@ public final class DisplayPowerController2Test {
        final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
                mock(ScreenOffBrightnessSensorController.class);
        final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
        final HdrClamper hdrClamper = mock(HdrClamper.class);
        final DeviceConfigParameterProvider deviceConfigParameterProvider =
                mock(DeviceConfigParameterProvider.class);

        when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);

        TestInjector injector = spy(new TestInjector(displayPowerState, animator,
                automaticBrightnessController, wakelockController, brightnessMappingStrategy,
                hysteresisLevels, screenOffBrightnessSensorController, hbmController));
                hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
                deviceConfigParameterProvider));

        final LogicalDisplay display = mock(LogicalDisplay.class);
        final DisplayDevice device = mock(DisplayDevice.class);
@@ -1293,7 +1320,7 @@ public final class DisplayPowerController2Test {

        return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
                animator, automaticBrightnessController, wakelockController,
                screenOffBrightnessSensorController, hbmController, hbmMetadata,
                screenOffBrightnessSensorController, hbmController, hdrClamper, hbmMetadata,
                brightnessMappingStrategy, injector);
    }

@@ -1311,6 +1338,8 @@ public final class DisplayPowerController2Test {
        public final WakelockController wakelockController;
        public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
        public final HighBrightnessModeController hbmController;

        public final HdrClamper hdrClamper;
        public final HighBrightnessModeMetadata hbmMetadata;
        public final BrightnessMappingStrategy brightnessMappingStrategy;
        public final DisplayPowerController2.Injector injector;
@@ -1322,6 +1351,7 @@ public final class DisplayPowerController2Test {
                WakelockController wakelockController,
                ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
                HighBrightnessModeController hbmController,
                HdrClamper hdrClamper,
                HighBrightnessModeMetadata hbmMetadata,
                BrightnessMappingStrategy brightnessMappingStrategy,
                DisplayPowerController2.Injector injector) {
@@ -1334,6 +1364,7 @@ public final class DisplayPowerController2Test {
            this.wakelockController = wakelockController;
            this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
            this.hbmController = hbmController;
            this.hdrClamper = hdrClamper;
            this.hbmMetadata = hbmMetadata;
            this.brightnessMappingStrategy = brightnessMappingStrategy;
            this.injector = injector;
@@ -1350,13 +1381,19 @@ public final class DisplayPowerController2Test {
        private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
        private final HighBrightnessModeController mHighBrightnessModeController;

        private final HdrClamper mHdrClamper;

        private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;

        TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
                AutomaticBrightnessController automaticBrightnessController,
                WakelockController wakelockController,
                BrightnessMappingStrategy brightnessMappingStrategy,
                HysteresisLevels hysteresisLevels,
                ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
                HighBrightnessModeController highBrightnessModeController) {
                HighBrightnessModeController highBrightnessModeController,
                HdrClamper hdrClamper,
                DeviceConfigParameterProvider deviceConfigParameterProvider) {
            mDisplayPowerState = dps;
            mAnimator = animator;
            mAutomaticBrightnessController = automaticBrightnessController;
@@ -1365,6 +1402,8 @@ public final class DisplayPowerController2Test {
            mHysteresisLevels = hysteresisLevels;
            mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
            mHighBrightnessModeController = highBrightnessModeController;
            mHdrClamper = hdrClamper;
            mDeviceConfigParameterProvider = deviceConfigParameterProvider;
        }

        @Override
@@ -1470,6 +1509,14 @@ public final class DisplayPowerController2Test {
            return mHighBrightnessModeController;
        }

        @Override
        BrightnessRangeController getBrightnessRangeController(
                HighBrightnessModeController hbmController, Runnable modeChangeCallback,
                DisplayDeviceConfig displayDeviceConfig, Handler handler) {
            return new BrightnessRangeController(hbmController, modeChangeCallback,
                    displayDeviceConfig, mHdrClamper, mDeviceConfigParameterProvider);
        }

        @Override
        DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
                SensorManager sensorManager, Resources resources) {
Loading