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

Commit 9ff7d4ce authored by petsjonkin's avatar petsjonkin
Browse files

HdrBrightnessModifier: adding LowPowerMode settings listener

HdrBrightnessModifier should respect "allowInLowPowerMode" config, and if not allowed Hdr boost should be disabled in low power mode

Bug: b/356587573
Test: manual testing, atest HdrBrightnessModifierTest
Flag: com.android.server.display.feature.flags.new_hdr_brightness_modifier
Change-Id: I9b8014c5616ad64f33937a3eb119918a4be3e5f0
parent 5b8c49ef
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -60,7 +60,8 @@ public final class BrightnessInfo implements Parcelable {
    @IntDef(prefix = {"BRIGHTNESS_MAX_REASON_"}, value = {
            BRIGHTNESS_MAX_REASON_NONE,
            BRIGHTNESS_MAX_REASON_THERMAL,
            BRIGHTNESS_MAX_REASON_POWER_IC
            BRIGHTNESS_MAX_REASON_POWER_IC,
            BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface BrightnessMaxReason {}
+1 −1
Original line number Diff line number Diff line
@@ -347,7 +347,7 @@ public class BrightnessClamperController {
                        data.mDisplayDeviceConfig));
            }
            if (flags.useNewHdrBrightnessModifier()) {
                modifiers.add(new HdrBrightnessModifier(handler, listener, data));
                modifiers.add(new HdrBrightnessModifier(handler, context, listener, data));
            }
            return modifiers;
        }
+116 −32
Original line number Diff line number Diff line
@@ -21,10 +21,15 @@ import static com.android.server.display.brightness.clamper.LightSensorControlle

import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.DisplayManagerInternal;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.view.SurfaceControlHdrLayerInfoListener;

import com.android.internal.annotations.VisibleForTesting;
@@ -44,6 +49,11 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
    static final float DEFAULT_MAX_HDR_SDR_RATIO = 1.0f;
    private static final float DEFAULT_HDR_LAYER_SIZE = -1.0f;

    private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(
            Settings.Global.LOW_POWER_MODE);

    private final ContentObserver mContentObserver;

    private final SurfaceControlHdrLayerInfoListener mHdrListener =
            new SurfaceControlHdrLayerInfoListener() {
                @Override
@@ -52,7 +62,8 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
                    boolean hdrLayerPresent = numberOfHdrLayers > 0;
                    mHandler.post(() -> HdrBrightnessModifier.this.onHdrInfoChanged(
                            hdrLayerPresent ? (float) (maxW * maxH) : DEFAULT_HDR_LAYER_SIZE,
                            hdrLayerPresent ? maxDesiredHdrSdrRatio : DEFAULT_MAX_HDR_SDR_RATIO));
                            hdrLayerPresent ? Math.max(maxDesiredHdrSdrRatio,
                                    DEFAULT_MAX_HDR_SDR_RATIO) : DEFAULT_MAX_HDR_SDR_RATIO));
                }
            };

@@ -62,6 +73,7 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
    private final Runnable mDebouncer;

    private IBinder mRegisteredDisplayToken;
    private boolean mContentObserverRegistered = false;

    private DisplayDeviceConfig mDisplayDeviceConfig;
    @Nullable
@@ -73,6 +85,8 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,

    private float mAmbientLux = INVALID_LUX;

    private boolean mLowPowerMode = false;

    private Mode mMode = Mode.NO_HDR;
    // The maximum brightness allowed for current lux
    private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
@@ -81,17 +95,17 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
    private float mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
    private float mPendingTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;

    HdrBrightnessModifier(Handler handler,
    HdrBrightnessModifier(Handler handler, Context context,
            BrightnessClamperController.ClamperChangeListener clamperChangeListener,
            BrightnessClamperController.DisplayDeviceData displayData) {
        this(new Handler(handler.getLooper()), clamperChangeListener, new Injector(), displayData);
        this(new Handler(handler.getLooper()), clamperChangeListener,
                new Injector(context), displayData);
    }

    @VisibleForTesting
    HdrBrightnessModifier(Handler handler,
            BrightnessClamperController.ClamperChangeListener clamperChangeListener,
            Injector injector,
            BrightnessClamperController.DisplayDeviceData displayData) {
            Injector injector, BrightnessClamperController.DisplayDeviceData displayData) {
        mHandler = handler;
        mClamperChangeListener = clamperChangeListener;
        mInjector = injector;
@@ -100,6 +114,12 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
            mMaxBrightness = mPendingMaxBrightness;
            mClamperChangeListener.onChanged();
        };
        mContentObserver = new ContentObserver(mHandler) {
            @Override
            public void onChange(boolean selfChange) {
                onLowPowerModeChange();
            }
        };
        mHandler.post(() -> onDisplayChanged(displayData));
    }

@@ -135,12 +155,14 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        pw.println("  mMaxDesiredHdrRatio=" + mMaxDesiredHdrRatio);
        pw.println("  mHdrLayerSize=" + mHdrLayerSize);
        pw.println("  mAmbientLux=" + mAmbientLux);
        pw.println("  mLowPowerMode=" + mLowPowerMode);
        pw.println("  mMode=" + mMode);
        pw.println("  mMaxBrightness=" + mMaxBrightness);
        pw.println("  mPendingMaxBrightness=" + mPendingMaxBrightness);
        pw.println("  mTransitionRate=" + mTransitionRate);
        pw.println("  mPendingTransitionRate=" + mPendingTransitionRate);
        pw.println("  mHdrListener registered=" + (mRegisteredDisplayToken != null));
        pw.println("  mContentObserverRegistered=" + mContentObserverRegistered);
    }

    // Called in DisplayControllerHandler
@@ -182,7 +204,25 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        } else {
            registerHdrListener(displayData.mDisplayToken);
        }
        recalculate(data, mMaxDesiredHdrRatio);
        if (data == null || data.allowInLowPowerMode) {
            unregisterContentObserver();
        } else {
            registerContentObserver();
        }

        Mode newMode = recalculateMode(data);
        // mode changed, or mode was HDR  and HdrBrightnessData changed
        boolean needToNotifyChange = mMode != newMode
                || (mMode != HdrBrightnessModifier.Mode.NO_HDR && data != mHdrBrightnessData);
        mMode = newMode;
        mHdrBrightnessData = data;
        mMaxBrightness = findBrightnessLimit(mHdrBrightnessData, mAmbientLux);

        if (needToNotifyChange) {
            // data changed, reset custom transition rate
            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
            mClamperChangeListener.onChanged();
        }
    }

    // Called in DisplayControllerHandler, when any modifier state changes
@@ -225,30 +265,6 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        // && expectedMaxBrightness != mMaxBrightness
    }

    // Called in DisplayControllerHandler
    private void recalculate(@Nullable HdrBrightnessData data, float maxDesiredHdrRatio) {
        Mode newMode = recalculateMode(data);
        // if HDR mode changed, notify changed
        boolean needToNotifyChange = mMode != newMode;
        // If HDR mode is active, we need to check if other HDR params are changed
        if (mMode != HdrBrightnessModifier.Mode.NO_HDR) {
            if (!BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrRatio)
                    || data != mHdrBrightnessData) {
                needToNotifyChange = true;
            }
        }

        mMode = newMode;
        mHdrBrightnessData = data;
        mMaxDesiredHdrRatio = maxDesiredHdrRatio;

        if (needToNotifyChange) {
            // data or hdr layer changed, reset custom transition rate
            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
            mClamperChangeListener.onChanged();
        }
    }

    // Called in DisplayControllerHandler
    private Mode recalculateMode(@Nullable HdrBrightnessData data) {
        // no config
@@ -259,6 +275,10 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        if (mHdrLayerSize == DEFAULT_HDR_LAYER_SIZE) {
            return Mode.NO_HDR;
        }
        // low power mode and not allowed in low power mode
        if (!data.allowInLowPowerMode && mLowPowerMode) {
            return Mode.NO_HDR;
        }
        // HDR layer < minHdr % for Nbm
        if (mHdrLayerSize < mScreenSize * data.minimumHdrPercentOfScreenForNbm) {
            return Mode.NO_HDR;
@@ -271,6 +291,16 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        return Mode.HBM_HDR;
    }

    private void onLowPowerModeChange() {
        mLowPowerMode = mInjector.isLowPowerMode();
        Mode newMode = recalculateMode(mHdrBrightnessData);
        if (newMode != mMode) {
            mMode = newMode;
            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
            mClamperChangeListener.onChanged();
        }
    }

    private float getMaxBrightness(Mode mode, float maxBrightness, HdrBrightnessData data) {
        if (mode == Mode.NBM_HDR) {
            return Math.min(data.hbmTransitionPoint, maxBrightness);
@@ -282,7 +312,13 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
    }

    // Called in DisplayControllerHandler
    private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
    private float findBrightnessLimit(@Nullable HdrBrightnessData data, float ambientLux) {
        if (data == null) {
            return PowerManager.BRIGHTNESS_MAX;
        }
        if (ambientLux == INVALID_LUX) {
            return PowerManager.BRIGHTNESS_MAX;
        }
        float foundAmbientBoundary = Float.MAX_VALUE;
        float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
        for (Map.Entry<Float, Float> brightnessPoint :
@@ -300,7 +336,17 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
    // Called in DisplayControllerHandler
    private void onHdrInfoChanged(float hdrLayerSize, float maxDesiredHdrSdrRatio) {
        mHdrLayerSize = hdrLayerSize;
        recalculate(mHdrBrightnessData, maxDesiredHdrSdrRatio);
        Mode newMode = recalculateMode(mHdrBrightnessData);
        // mode changed, or mode was HDR  and maxDesiredHdrRatio changed
        boolean needToNotifyChange = mMode != newMode
                || (mMode != HdrBrightnessModifier.Mode.NO_HDR
                && !BrightnessSynchronizer.floatEquals(mMaxDesiredHdrRatio, maxDesiredHdrSdrRatio));
        mMode = newMode;
        mMaxDesiredHdrRatio = maxDesiredHdrSdrRatio;
        if (needToNotifyChange) {
            mTransitionRate = CUSTOM_ANIMATION_RATE_NOT_SET;
            mClamperChangeListener.onChanged();
        }
    }

    // Called in DisplayControllerHandler
@@ -324,12 +370,36 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        }
    }

    // Called in DisplayControllerHandler
    private void registerContentObserver() {
        if (!mContentObserverRegistered) {
            mInjector.registerContentObserver(mContentObserver, mLowPowerModeSetting);
            mContentObserverRegistered = true;
            mLowPowerMode = mInjector.isLowPowerMode();
        }
    }

    // Called in DisplayControllerHandler
    private void unregisterContentObserver() {
        if (mContentObserverRegistered) {
            mInjector.unregisterContentObserver(mContentObserver);
            mContentObserverRegistered = false;
            mLowPowerMode = false;
        }
    }

    private enum Mode {
        NO_HDR, NBM_HDR, HBM_HDR
    }

    @SuppressLint("MissingPermission")
    static class Injector {
        private final Context mContext;

        Injector(Context context) {
            mContext = context;
        }

        void registerHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
            listener.register(token);
        }
@@ -337,5 +407,19 @@ public class HdrBrightnessModifier implements BrightnessStateModifier,
        void unregisterHdrListener(SurfaceControlHdrLayerInfoListener listener, IBinder token) {
            listener.unregister(token);
        }

        void registerContentObserver(ContentObserver observer, Uri uri) {
            mContext.getContentResolver().registerContentObserver(uri, false,
                    observer, UserHandle.USER_ALL);
        }

        void unregisterContentObserver(ContentObserver observer) {
            mContext.getContentResolver().unregisterContentObserver(observer);
        }

        boolean isLowPowerMode() {
            return Settings.Global.getInt(
                    mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) != 0;
        }
    }
}
+73 −22
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package com.android.server.display.brightness.clamper

import android.content.Context
import android.database.ContentObserver
import android.hardware.display.DisplayManagerInternal
import android.net.Uri
import android.os.IBinder
import android.os.PowerManager.BRIGHTNESS_MAX
import android.util.Spline
@@ -51,7 +54,7 @@ class HdrBrightnessModifierTest {

    private val stoppedClock = OffsettableClock.Stopped()
    private val testHandler = TestHandler(null, stoppedClock)
    private val testInjector = TestInjector()
    private val testInjector = TestInjector(mock<Context>())
    private val mockChangeListener = mock<ClamperChangeListener>()
    private val mockDisplayDeviceConfig = mock<DisplayDeviceConfig>()
    private val mockDisplayBinder = mock<IBinder>()
@@ -63,14 +66,14 @@ class HdrBrightnessModifierTest {
    private val dummyData = createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinder)

    @Test
    fun `change listener is not called on init`() {
    fun changeListenerIsNotCalledOnInit() {
        initHdrModifier()

        verify(mockChangeListener, never()).onChanged()
    }

    @Test
    fun `hdr listener registered on init if hdr data is present`() {
    fun hdrListenerRegisteredOnInit_hdrDataPresent() {
        initHdrModifier()

        assertThat(testInjector.registeredHdrListener).isNotNull()
@@ -78,22 +81,19 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `hdr listener not registered on init if hdr data is missing`() {
        initHdrModifier(null)

        testHandler.flush()
    fun hdrListenerNotRegisteredOnInit_hdrDataMissing() {
        initHdrModifier(hdrBrightnessData = null)

        assertThat(testInjector.registeredHdrListener).isNull()
        assertThat(testInjector.registeredToken).isNull()
    }

    @Test
    fun `unsubscribes hdr listener when display changed with no hdr data`() {
    fun unsubscribeHdrListener_displayChangedWithNoHdrData() {
        initHdrModifier()

        whenever(mockDisplayDeviceConfig.hdrBrightnessData).thenReturn(null)
        modifier.onDisplayChanged(dummyData)
        testHandler.flush()

        assertThat(testInjector.registeredHdrListener).isNull()
        assertThat(testInjector.registeredToken).isNull()
@@ -101,12 +101,11 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `resubscribes hdr listener when display changed with different token`() {
    fun resubscribesHdrListener_displayChangedWithDifferentToken() {
        initHdrModifier()

        modifier.onDisplayChanged(
            createDisplayDeviceData(mockDisplayDeviceConfig, mockDisplayBinderOther))
        testHandler.flush()

        assertThat(testInjector.registeredHdrListener).isNotNull()
        assertThat(testInjector.registeredToken).isEqualTo(mockDisplayBinderOther)
@@ -114,7 +113,28 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test NO_HDR mode`() {
    fun contentObserverNotRegisteredOnInit_hdrDataMissing() {
        initHdrModifier(null)

        assertThat(testInjector.registeredContentObserver).isNull()
    }

    @Test
    fun contentObserverNotRegisteredOnInit_allowedInLowPowerMode() {
        initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = true))

        assertThat(testInjector.registeredContentObserver).isNull()
    }

    @Test
    fun contentObserverRegisteredOnInit_notAllowedInLowPowerMode() {
        initHdrModifier(createHdrBrightnessData(allowInLowPowerMode = false))

        assertThat(testInjector.registeredContentObserver).isNotNull()
    }

    @Test
    fun testNoHdrMode() {
        initHdrModifier()
        // screen size = 10_000
        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -131,7 +151,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test NBM_HDR mode`() {
    fun testNbmHdrMode() {
        initHdrModifier()
        // screen size = 10_000
        val transitionPoint = 0.55f
@@ -157,7 +177,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test HBM_HDR mode`() {
    fun testHbmHdrMode() {
        initHdrModifier()
        // screen size = 10_000
        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -182,7 +202,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test display change no HDR content`() {
    fun testDisplayChange_noHdrContent() {
        initHdrModifier()
        setupDisplay(width = 100, height = 100)
        assertModifierState()
@@ -195,7 +215,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test display change with HDR content`() {
    fun testDisplayChange_hdrContent() {
        initHdrModifier()
        setupDisplay(width = 100, height = 100)
        setupHdrLayer(width = 100, height = 100, maxHdrRatio = 5f)
@@ -218,7 +238,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test ambient lux decrease above maxBrightnessLimits no HDR`() {
    fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitNoHdr() {
        initHdrModifier()
        modifier.setAmbientLux(1000f)
        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -234,7 +254,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test ambient lux decrease above maxBrightnessLimits with HDR`() {
    fun testSetAmbientLux_decreaseAboveMaxBrightnessLimitWithHdr() {
        initHdrModifier()
        modifier.setAmbientLux(1000f)
        setupDisplay(width = 200, height = 200, hdrBrightnessData = createHdrBrightnessData(
@@ -260,7 +280,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test ambient lux decrease below maxBrightnessLimits no HDR`() {
    fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitNoHdr() {
        initHdrModifier()
        modifier.setAmbientLux(1000f)
        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
@@ -276,7 +296,7 @@ class HdrBrightnessModifierTest {
    }

    @Test
    fun `test ambient lux decrease below maxBrightnessLimits with HDR`() {
    fun testSetAmbientLux_decreaseBelowMaxBrightnessLimitWithHdr() {
        initHdrModifier()
        modifier.setAmbientLux(1000f)
        val maxBrightness = 0.6f
@@ -322,6 +342,23 @@ class HdrBrightnessModifierTest {
        )
    }

    @Test
    fun testLowPower_notAllowedInLowPower() {
        initHdrModifier()
        setupDisplay(width = 100, height = 100, hdrBrightnessData = createHdrBrightnessData(
            allowInLowPowerMode = false
        ))
        setupHdrLayer(width = 100, height = 100)
        clearInvocations(mockChangeListener)

        testInjector.isLowPower = true
        testInjector.registeredContentObserver!!.onChange(true)

        verify(mockChangeListener).onChanged()
        assertModifierState()
    }

    // Helper functions
    private fun setupHdrLayer(width: Int = 100, height: Int = 100, maxHdrRatio: Float = 0.8f) {
        testInjector.registeredHdrListener!!.onHdrInfoChanged(
            mockDisplayBinder, 1, width, height, 0, maxHdrRatio
@@ -345,7 +382,6 @@ class HdrBrightnessModifierTest {
            width = width,
            height = height
        ))
        testHandler.flush()
    }

    private fun initHdrModifier(hdrBrightnessData: HdrBrightnessData? = createHdrBrightnessData()) {
@@ -384,9 +420,12 @@ class HdrBrightnessModifierTest {
        assertThat(stateBuilder.customAnimationRate).isEqualTo(animationRate)
    }

    internal class TestInjector : Injector() {
    internal class TestInjector(context: Context) : Injector(context) {
        var registeredHdrListener: SurfaceControlHdrLayerInfoListener? = null
        var registeredToken: IBinder? = null
        var registeredContentObserver: ContentObserver? = null

        var isLowPower: Boolean = false

        override fun registerHdrListener(
            listener: SurfaceControlHdrLayerInfoListener, token: IBinder
@@ -401,5 +440,17 @@ class HdrBrightnessModifierTest {
            registeredHdrListener = null
            registeredToken = null
        }

        override fun registerContentObserver(observer: ContentObserver, uri: Uri) {
            registeredContentObserver = observer
        }

        override fun unregisterContentObserver(observer: ContentObserver) {
            registeredContentObserver = null
        }

        override fun isLowPowerMode(): Boolean {
            return isLowPower
        }
    }
}
 No newline at end of file