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

Commit 056493c0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add limitations to HDR." into sc-dev am: 5b31467f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15221893

Change-Id: I757a081ade4c44d304f4c84162677b258ba817de
parents 4a29b131 5b31467f
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -778,7 +778,6 @@ class AutomaticBrightnessController {
                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
            mScreenDarkeningThreshold = clampScreenBrightness(
                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
            mHbmController.onAutoBrightnessChanged(mScreenAutoBrightness);

            if (sendUpdate) {
                mCallbacks.updateBrightness();
+5 −5
Original line number Diff line number Diff line
@@ -1281,6 +1281,11 @@ public final class DisplayManagerService extends SystemService {
        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
        scheduleTraversalLocked(false);
        mPersistentDataStore.saveIfNeeded();

        DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
        if (dpc != null) {
            dpc.onDisplayChanged();
        }
    }

    private void handleLogicalDisplayFrameRateOverridesChangedLocked(
@@ -1312,11 +1317,6 @@ 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);
    }

+14 −10
Original line number Diff line number Diff line
@@ -511,7 +511,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        mSkipScreenOnBrightnessRamp = resources.getBoolean(
                com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);

        mHbmController = createHbmController();
        mHbmController = createHbmControllerLocked();

        // Seed the cached brightness
        saveBrightnessInfo(getScreenBrightnessSetting());
@@ -717,6 +717,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        final String uniqueId = device.getUniqueId();
        final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
        final IBinder token = device.getDisplayTokenLocked();
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        mHandler.post(() -> {
            if (mDisplayDevice == device) {
                return;
@@ -724,7 +725,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
            mDisplayDevice = device;
            mUniqueDisplayId = uniqueId;
            mDisplayDeviceConfig = config;
            loadFromDisplayDeviceConfig(token);
            loadFromDisplayDeviceConfig(token, info);
        });
    }

@@ -765,7 +766,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        }
    }

    private void loadFromDisplayDeviceConfig(IBinder token) {
    private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
        // All properties that depend on the associated DisplayDevice and the DDC must be
        // updated here.
        loadAmbientLightSensor();
@@ -774,7 +775,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        loadNitsRange(mContext.getResources());
        setUpAutoBrightness(mContext.getResources(), mHandler);
        reloadReduceBrightColours();
        mHbmController.resetHbmData(token, mDisplayDeviceConfig.getHighBrightnessModeData());
        mHbmController.resetHbmData(info.width, info.height, token,
                mDisplayDeviceConfig.getHighBrightnessModeData(), mBrightnessSetting);
    }

    private void sendUpdatePowerState() {
@@ -1343,6 +1345,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
            if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
                    && ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0
                    || (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) {
                // We want to scale HDR brightness level with the SDR level
                animateValue = mHbmController.getHdrBrightnessValue();
            }

@@ -1514,21 +1517,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
        }
    }

    private HighBrightnessModeController createHbmController() {
        final DisplayDeviceConfig ddConfig =
                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
    private HighBrightnessModeController createHbmControllerLocked() {
        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
        final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
        final IBinder displayToken =
                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
        final DisplayDeviceConfig.HighBrightnessModeData hbmData =
                ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
        return new HighBrightnessModeController(mHandler, displayToken, PowerManager.BRIGHTNESS_MIN,
                PowerManager.BRIGHTNESS_MAX, hbmData,
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
                () -> {
                    sendUpdatePowerStateLocked();
                    mHandler.post(mOnBrightnessChangeRunnable);
                    // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
                    mAutomaticBrightnessController.update();
                }, mContext);
                }, mContext, mBrightnessSetting);
    }

    private void blockScreenOn() {
+80 −34
Original line number Diff line number Diff line
@@ -31,11 +31,13 @@ import android.os.SystemClock;
import android.os.Temperature;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.SurfaceControlHdrLayerInfoListener;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.BrightnessSetting.BrightnessSettingListener;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import com.android.server.display.DisplayManagerService.Clock;

@@ -56,6 +58,8 @@ class HighBrightnessModeController {

    private static final boolean DEBUG = false;

    private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;

    private final float mBrightnessMin;
    private final float mBrightnessMax;
    private final Handler mHandler;
@@ -66,6 +70,7 @@ class HighBrightnessModeController {
    private final Context mContext;
    private final SettingsObserver mSettingsObserver;
    private final Injector mInjector;
    private final BrightnessSettingListener mBrightnessSettingListener = this::onBrightnessChanged;

    private SurfaceControlHdrLayerInfoListener mHdrListener;
    private HighBrightnessModeData mHbmData;
@@ -74,11 +79,15 @@ class HighBrightnessModeController {
    private boolean mIsInAllowedAmbientRange = false;
    private boolean mIsTimeAvailable = false;
    private boolean mIsAutoBrightnessEnabled = false;
    private float mAutoBrightness;
    private float mBrightness;
    private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
    private boolean mIsHdrLayerPresent = false;
    private boolean mIsThermalStatusWithinLimit = true;
    private boolean mIsBlockedByLowPowerMode = false;
    private int mWidth;
    private int mHeight;
    private BrightnessSetting mBrightnessSetting;
    private float mAmbientLux;

    /**
     * If HBM is currently running, this is the start time for the current HBM session.
@@ -92,30 +101,32 @@ class HighBrightnessModeController {
     */
    private LinkedList<HbmEvent> mEvents = new LinkedList<>();

    HighBrightnessModeController(Handler handler, IBinder displayToken, float brightnessMin,
            float brightnessMax, HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
            Context context) {
        this(new Injector(), handler, displayToken, brightnessMin, brightnessMax,
                hbmData, hbmChangeCallback, context);
    HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
            float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
            Runnable hbmChangeCallback, Context context, BrightnessSetting brightnessSetting) {
        this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax,
                hbmData, hbmChangeCallback, context, brightnessSetting);
    }

    @VisibleForTesting
    HighBrightnessModeController(Injector injector, Handler handler, IBinder displayToken,
            float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
            Runnable hbmChangeCallback, Context context) {
    HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
            IBinder displayToken, float brightnessMin, float brightnessMax,
            HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
            Context context, BrightnessSetting brightnessSetting) {
        mInjector = injector;
        mContext = context;
        mClock = injector.getClock();
        mHandler = handler;
        mBrightnessMin = brightnessMin;
        mBrightnessMax = brightnessMax;
        mBrightness = brightnessSetting.getBrightness();
        mHbmChangeCallback = hbmChangeCallback;
        mContext = context;
        mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
        mRecalcRunnable = this::recalculateTimeAllowance;
        mHdrListener = new HdrListener();
        mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
        mSettingsObserver = new SettingsObserver(mHandler);
        resetHbmData(displayToken, hbmData);
        mRecalcRunnable = this::recalculateTimeAllowance;
        mHdrListener = new HdrListener();

        resetHbmData(width, height, displayToken, hbmData, brightnessSetting);
    }

    void setAutoBrightnessEnabled(boolean isEnabled) {
@@ -123,7 +134,7 @@ class HighBrightnessModeController {
            return;
        }
        if (DEBUG) {
            Slog.d(TAG, "setAutoBrightness( " + isEnabled + " )");
            Slog.d(TAG, "setAutoBrightnessEnabled( " + isEnabled + " )");
        }
        mIsAutoBrightnessEnabled = isEnabled;
        mIsInAllowedAmbientRange = false; // reset when auto-brightness switches
@@ -147,11 +158,22 @@ class HighBrightnessModeController {
        }
    }

    float getNormalBrightnessMax() {
        return deviceSupportsHbm() ? mHbmData.transitionPoint : mBrightnessMax;
    }

    float getHdrBrightnessValue() {
        return mBrightnessMax;
        // For HDR brightness, we take the current brightness and scale it to the max. The reason
        // we do this is because we want brightness to go to HBM max when it would normally go
        // to normal max, meaning it should not wait to go to 10000 lux (or whatever the transition
        // point happens to be) in order to go full HDR. Likewise, HDR on manual brightness should
        // automatically scale the brightness without forcing the user to adjust to higher values.
        return MathUtils.map(getCurrentBrightnessMin(), getCurrentBrightnessMax(),
                mBrightnessMin, mBrightnessMax, mBrightness);
    }

    void onAmbientLuxChange(float ambientLux) {
        mAmbientLux = ambientLux;
        if (!deviceSupportsHbm() || !mIsAutoBrightnessEnabled) {
            return;
        }
@@ -163,17 +185,17 @@ class HighBrightnessModeController {
        }
    }

    void onAutoBrightnessChanged(float autoBrightness) {
    @VisibleForTesting
    void onBrightnessChanged(float brightness) {
        if (!deviceSupportsHbm()) {
            return;
        }
        final float oldAutoBrightness = mAutoBrightness;
        mAutoBrightness = autoBrightness;
        mBrightness = brightness;

        // 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 wasHbmDrainingAvailableTime = mRunningStartTimeMillis != -1;
        final boolean shouldHbmDrainAvailableTime = mAutoBrightness > mHbmData.transitionPoint
        final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
                && !mIsHdrLayerPresent;
        if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
            final long currentTime = mClock.uptimeMillis();
@@ -202,8 +224,12 @@ class HighBrightnessModeController {
        mSettingsObserver.stopObserving();
    }

    void resetHbmData(IBinder displayToken, HighBrightnessModeData hbmData) {
    void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData,
            BrightnessSetting brightnessSetting) {
        mWidth = width;
        mHeight = height;
        mHbmData = hbmData;
        resetBrightnessSetting(brightnessSetting);
        unregisterHdrListener();
        mSkinThermalStatusObserver.stopObserving();
        mSettingsObserver.stopObserving();
@@ -227,21 +253,23 @@ class HighBrightnessModeController {

    private void dumpLocal(PrintWriter pw) {
        pw.println("HighBrightnessModeController:");
        pw.println("  mBrightness=" + mBrightness);
        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("  mAmbientLux=" + mAmbientLux);
        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);
        pw.println("  remainingTime=" + calculateRemainingTime(mClock.uptimeMillis()));
        pw.println("  mIsTimeAvailable= " + mIsTimeAvailable);
        pw.println("  mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
        pw.println("  mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
        pw.println("  mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
        pw.println("  width*height=" + mWidth + "*" + mHeight);
        pw.println("  mEvents=");
        final long currentTime = mClock.uptimeMillis();
        long lastStartTime = currentTime;
@@ -268,9 +296,26 @@ class HighBrightnessModeController {
        return event.startTimeMillis;
    }

    private void resetBrightnessSetting(BrightnessSetting brightnessSetting) {
        if (mBrightnessSetting != null) {
            mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
        }
        mBrightnessSetting = brightnessSetting;
        if (mBrightnessSetting != null) {
            mBrightnessSetting.registerListener(mBrightnessSettingListener);
        }
    }

    private boolean isCurrentlyAllowed() {
        return mIsHdrLayerPresent
                || (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange
        // Returns true if HBM is allowed (above the ambient lux threshold) and there's still
        // time within the current window for additional HBM usage. We return false if there is an
        // HDR layer because we don't want the brightness MAX to change for HDR, which has its
        // brightness scaled in a different way than sunlight HBM that doesn't require changing
        // the MAX. HDR also needs to work under manual brightness which never adjusts the
        // brightness maximum; so we implement HDR-HBM in a way that doesn't adjust the max.
        // See {@link #getHdrBrightnessValue}.
        return !mIsHdrLayerPresent
                && (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange
                && mIsThermalStatusWithinLimit && !mIsBlockedByLowPowerMode);
    }

@@ -334,13 +379,13 @@ class HighBrightnessModeController {
        // or if brightness is already in the high range, if there is any time left at all.
        final boolean isAllowedWithoutRestrictions = remainingTime >= mHbmData.timeMinMillis;
        final boolean isOnlyAllowedToStayOn = !isAllowedWithoutRestrictions
                && remainingTime > 0 && mAutoBrightness > mHbmData.transitionPoint;
                && remainingTime > 0 && mBrightness > mHbmData.transitionPoint;
        mIsTimeAvailable = isAllowedWithoutRestrictions || isOnlyAllowedToStayOn;

        // Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
        // brightness change doesn't happen before then.
        long nextTimeout = -1;
        if (mAutoBrightness > mHbmData.transitionPoint) {
        if (mBrightness > mHbmData.transitionPoint) {
            // if we're in high-lux now, timeout when we run out of allowed time.
            nextTimeout = currentTime + remainingTime;
        } else if (!mIsTimeAvailable && mEvents.size() > 0) {
@@ -370,7 +415,7 @@ class HighBrightnessModeController {
                    + ", mIsInAllowedAmbientRange: " + mIsInAllowedAmbientRange
                    + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit
                    + ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode
                    + ", brightness: " + mAutoBrightness
                    + ", mBrightness: " + mBrightness
                    + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
                    + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
                    + ", events: " + mEvents);
@@ -447,11 +492,12 @@ class HighBrightnessModeController {
        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);
                mIsHdrLayerPresent = numberOfHdrLayers > 0
                        && (float) (maxW * maxH)
                                >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED);
                // Calling the brightness update so that we can recalculate
                // brightness with HDR in mind.
                onBrightnessChanged(mBrightness);
            });
        }
    }
+22 −14
Original line number Diff line number Diff line
@@ -74,6 +74,9 @@ public class HighBrightnessModeControllerTest {
    private static final float DEFAULT_MIN = 0.01f;
    private static final float DEFAULT_MAX = 0.80f;

    private static final int DISPLAY_WIDTH = 900;
    private static final int DISPLAY_HEIGHT = 1600;

    private static final float EPSILON = 0.000001f;

    private OffsettableClock mClock;
@@ -90,6 +93,8 @@ public class HighBrightnessModeControllerTest {

    @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;

    @Mock private BrightnessSetting mBrightnessSetting;

    private static final HighBrightnessModeData DEFAULT_HBM_DATA =
            new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS,
                    TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS,
@@ -98,6 +103,8 @@ public class HighBrightnessModeControllerTest {
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mClock = new OffsettableClock.Stopped();
        mTestLooper = new TestLooper(mClock::now);
        mDisplayToken = null;
        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
        final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
@@ -114,8 +121,8 @@ public class HighBrightnessModeControllerTest {
    public void testNoHbmData() {
        initHandler(null);
        final HighBrightnessModeController hbmc = new HighBrightnessModeController(
                mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null,
                () -> {}, mContextSpy);
                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
                DEFAULT_MAX, null, () -> {}, mContextSpy, mBrightnessSetting);
        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
    }

@@ -123,8 +130,8 @@ public class HighBrightnessModeControllerTest {
    public void testNoHbmData_Enabled() {
        initHandler(null);
        final HighBrightnessModeController hbmc = new HighBrightnessModeController(
                mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null,
                () -> {}, mContextSpy);
                mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
                DEFAULT_MAX, null, () -> {}, mContextSpy, mBrightnessSetting);
        hbmc.setAutoBrightnessEnabled(true);
        hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -180,7 +187,7 @@ public class HighBrightnessModeControllerTest {

        hbmc.setAutoBrightnessEnabled(true);
        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
        hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);

        // Verify we are in HBM
        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -212,7 +219,7 @@ public class HighBrightnessModeControllerTest {

        hbmc.setAutoBrightnessEnabled(true);
        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
        hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);

        // Verify we are in HBM
        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -237,18 +244,18 @@ public class HighBrightnessModeControllerTest {
        hbmc.setAutoBrightnessEnabled(true);
        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);

        hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
        advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);

        // Verify we are in HBM
        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);

        hbmc.onAutoBrightnessChanged(TRANSITION_POINT - 0.01f);
        hbmc.onBrightnessChanged(TRANSITION_POINT - 0.01f);
        advanceTime(1);

        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);

        hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
        advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);

        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -267,13 +274,13 @@ public class HighBrightnessModeControllerTest {
        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);

        // Go into HBM for half the allowed window
        hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 0.01f);
        hbmc.onBrightnessChanged(TRANSITION_POINT + 0.01f);
        advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2);
        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);

        // Move lux below threshold (ending first event);
        hbmc.onAmbientLuxChange(MINIMUM_LUX - 1);
        hbmc.onAutoBrightnessChanged(TRANSITION_POINT);
        hbmc.onBrightnessChanged(TRANSITION_POINT);
        assertState(hbmc, DEFAULT_MIN, TRANSITION_POINT, HIGH_BRIGHTNESS_MODE_OFF);

        // Move up some amount of time so that there's still time in the window even after a
@@ -283,7 +290,7 @@ public class HighBrightnessModeControllerTest {

        // Go into HBM for just under the second half of allowed window
        hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
        hbmc.onAutoBrightnessChanged(TRANSITION_POINT + 1);
        hbmc.onBrightnessChanged(TRANSITION_POINT + 1);
        advanceTime((TIME_ALLOWED_IN_WINDOW_MILLIS / 2) - 1);

        assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_SUNLIGHT);
@@ -355,8 +362,9 @@ public class HighBrightnessModeControllerTest {
    // Creates instance with standard initialization values.
    private HighBrightnessModeController createDefaultHbm(OffsettableClock clock) {
        initHandler(clock);
        return new HighBrightnessModeController(mInjectorMock, mHandler, mDisplayToken, DEFAULT_MIN,
                DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {}, mContextSpy);
        return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
                DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {},
                mContextSpy, mBrightnessSetting);
    }

    private void initHandler(OffsettableClock clock) {