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

Commit 8281f604 authored by Rupesh Bansal's avatar Rupesh Bansal
Browse files

Maintaining lux buffers since elapsed time and not uptime

We have historically preserved the lux buffers over a configurable long
horizon values since the clock uptime. However, the time when android is
sleeping is not accounted for in this, which is why the lux buffers can
go incorrect. Changing this to uptime, and also using the exact time at
which a sensor event was emitted to maintain the bufffer

Test: atest AutomaticBrightnessControllerTest
Bug: 341219242
Change-Id: I6b5c5b2c8754ddcd97249fcaf89b1575a865d4d8
parent 8a6e0bfb
Loading
Loading
Loading
Loading
+53 −11
Original line number Diff line number Diff line
@@ -56,10 +56,12 @@ import com.android.server.EventLogTags;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.config.HysteresisLevels;
import com.android.server.display.feature.DisplayManagerFlags;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.TimeUnit;

/**
 * Manages the associated display brightness when in auto-brightness mode. This is also
@@ -277,6 +279,8 @@ public class AutomaticBrightnessController {
    private Clock mClock;
    private final Injector mInjector;

    private final DisplayManagerFlags mDisplayManagerFlags;

    AutomaticBrightnessController(Callbacks callbacks, Looper looper,
            SensorManager sensorManager, Sensor lightSensor,
            SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
@@ -291,7 +295,8 @@ public class AutomaticBrightnessController {
            BrightnessRangeController brightnessModeController,
            BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
            int ambientLightHorizonLong, float userLux, float userNits,
            BrightnessClamperController brightnessClamperController) {
            BrightnessClamperController brightnessClamperController,
            DisplayManagerFlags displayManagerFlags) {
        this(new Injector(), callbacks, looper, sensorManager, lightSensor,
                brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
                dozeScaleFactor, lightSensorRate, initialLightSensorRate,
@@ -301,7 +306,7 @@ public class AutomaticBrightnessController {
                screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                screenBrightnessThresholdsIdle, context, brightnessModeController,
                brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
                userNits, brightnessClamperController
                userNits, brightnessClamperController, displayManagerFlags
        );
    }

@@ -320,9 +325,10 @@ public class AutomaticBrightnessController {
            BrightnessRangeController brightnessRangeController,
            BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
            int ambientLightHorizonLong, float userLux, float userNits,
            BrightnessClamperController brightnessClamperController) {
            BrightnessClamperController brightnessClamperController,
            DisplayManagerFlags displayManagerFlags) {
        mInjector = injector;
        mClock = injector.createClock();
        mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness());
        mContext = context;
        mCallbacks = callbacks;
        mSensorManager = sensorManager;
@@ -367,6 +373,7 @@ public class AutomaticBrightnessController {
        mBrightnessClamperController = brightnessClamperController;
        mBrightnessThrottler = brightnessThrottler;
        mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
        mDisplayManagerFlags = displayManagerFlags;

        // Use the given short-term model
        if (userNits != BrightnessMappingStrategy.INVALID_NITS) {
@@ -719,7 +726,6 @@ public class AutomaticBrightnessController {
        mRecentLightSamples++;
        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
        mAmbientLightRingBuffer.push(time, lux);

        // Remember this sample value.
        mLastObservedLux = lux;
        mLastObservedLuxTime = time;
@@ -863,7 +869,7 @@ public class AutomaticBrightnessController {
    }

    private void updateAmbientLux() {
        long time = mClock.uptimeMillis();
        long time = mClock.getSensorEventScaleTime();
        mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
        updateAmbientLux(time);
    }
@@ -940,7 +946,16 @@ public class AutomaticBrightnessController {
            Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
                    nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
        }
        mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);

        // The nextTransitionTime is computed as elapsedTime(Which also accounts for the time when
        // android was sleeping) as the main reference. However, handlers work on the uptime(Not
        // accounting for the time when android was sleeping)
        mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
                convertToUptime(nextTransitionTime));
    }

    private long convertToUptime(long time) {
        return time - mClock.getSensorEventScaleTime() + mClock.uptimeMillis();
    }

    private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
@@ -1366,7 +1381,9 @@ public class AutomaticBrightnessController {
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (mLightSensorEnabled) {
                final long time = mClock.uptimeMillis();
                // The time received from the sensor is in nano seconds, hence changing it to ms
                final long time = (mDisplayManagerFlags.offloadControlsDozeAutoBrightness())
                        ? TimeUnit.NANOSECONDS.toMillis(event.timestamp) : mClock.uptimeMillis();
                final float lux = event.values[0];
                handleLightSensorEvent(time, lux);
            }
@@ -1399,6 +1416,12 @@ public class AutomaticBrightnessController {
         * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
         */
        long uptimeMillis();

        /**
         * Gets the time on either the elapsedTime or the uptime scale, depending on how we
         * processing the events from the sensor
         */
        long getSensorEventScaleTime();
    }

    /**
@@ -1546,7 +1569,8 @@ public class AutomaticBrightnessController {
            StringBuilder buf = new StringBuilder();
            buf.append('[');
            for (int i = 0; i < mCount; i++) {
                final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
                final long next = i + 1 < mCount ? getTime(i + 1)
                        : mClock.getSensorEventScaleTime();
                if (i != 0) {
                    buf.append(", ");
                }
@@ -1571,13 +1595,31 @@ public class AutomaticBrightnessController {
        }
    }

    private static class RealClock implements Clock {
        private final boolean mOffloadControlsDozeBrightness;

        RealClock(boolean offloadControlsDozeBrightness) {
            mOffloadControlsDozeBrightness = offloadControlsDozeBrightness;
        }

        @Override
        public long uptimeMillis() {
            return SystemClock.uptimeMillis();
        }

        public long getSensorEventScaleTime() {
            return (mOffloadControlsDozeBrightness)
                    ? SystemClock.elapsedRealtime() : uptimeMillis();
        }
    }

    public static class Injector {
        public Handler getBackgroundThreadHandler() {
            return BackgroundThread.getHandler();
        }

        Clock createClock() {
            return SystemClock::uptimeMillis;
        Clock createClock(boolean offloadControlsDozeBrightness) {
            return new RealClock(offloadControlsDozeBrightness);
        }
    }
}
+4 −3
Original line number Diff line number Diff line
@@ -1115,7 +1115,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                    screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
                    mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
                    mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits,
                    mBrightnessClamperController);
                    mBrightnessClamperController, mFlags);
            mDisplayBrightnessController.setUpAutoBrightness(
                    mAutomaticBrightnessController, mSensorManager, mDisplayDeviceConfig, mHandler,
                    defaultModeBrightnessMapper, mIsEnabled, mLeadDisplayId);
@@ -3158,7 +3158,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                BrightnessRangeController brightnessModeController,
                BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
                int ambientLightHorizonLong, float userLux, float userNits,
                BrightnessClamperController brightnessClamperController) {
                BrightnessClamperController brightnessClamperController,
                DisplayManagerFlags displayManagerFlags) {

            return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor,
                    brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin,
@@ -3169,7 +3170,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
                    screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                    screenBrightnessThresholdsIdle, context, brightnessModeController,
                    brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
                    userNits, brightnessClamperController);
                    userNits, brightnessClamperController, displayManagerFlags);
        }

        BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
+55 −3
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import androidx.test.runner.AndroidJUnit4;

import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.config.HysteresisLevels;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.OffsettableClock;

import org.junit.After;
@@ -68,6 +69,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessControllerTest {
    private static final int ANDROID_SLEEP_TIME = 1000;
    private static final int NANO_SECONDS_MULTIPLIER = 1000000;
    private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
    private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
    private static final int LIGHT_SENSOR_RATE = 20;
@@ -100,6 +103,8 @@ public class AutomaticBrightnessControllerTest {
    @Mock BrightnessRangeController mBrightnessRangeController;
    @Mock
    BrightnessClamperController mBrightnessClamperController;
    @Mock
    DisplayManagerFlags mDisplayManagerFlags;
    @Mock BrightnessThrottler mBrightnessThrottler;

    @Before
@@ -148,8 +153,18 @@ public class AutomaticBrightnessControllerTest {
                    }

                    @Override
                    AutomaticBrightnessController.Clock createClock() {
                        return mClock::now;
                    AutomaticBrightnessController.Clock createClock(boolean isEnabled) {
                        return new AutomaticBrightnessController.Clock() {
                            @Override
                            public long uptimeMillis() {
                                return mClock.now();
                            }

                            @Override
                            public long getSensorEventScaleTime() {
                                return mClock.now() + ANDROID_SLEEP_TIME;
                            }
                        };
                    }

                }, // pass in test looper instead, pass in offsettable clock
@@ -166,7 +181,7 @@ public class AutomaticBrightnessControllerTest {
                mContext, mBrightnessRangeController, mBrightnessThrottler,
                useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
                useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
                mBrightnessClamperController
                mBrightnessClamperController, mDisplayManagerFlags
        );

        when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
@@ -799,6 +814,43 @@ public class AutomaticBrightnessControllerTest {
        assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
    }

    @Test
    public void testAmbientLuxBuffers_prunedBeyondLongHorizonExceptLatestValue() throws Exception {
        when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
        ArgumentCaptor<SensorEventListener> listenerCaptor =
                ArgumentCaptor.forClass(SensorEventListener.class);
        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
        SensorEventListener listener = listenerCaptor.getValue();

        // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
        int increment = 11;
        int lux = 5000;
        for (int i = 0; i < 1000; i++) {
            lux += increment;
            mClock.fastForward(increment);
            listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux,
                    (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
        }
        mClock.fastForward(AMBIENT_LIGHT_HORIZON_LONG + 10);
        int newLux = 2000;
        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, newLux,
                (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));

        float[] sensorValues = mController.getLastSensorValues();
        long[] sensorTimestamps = mController.getLastSensorTimestamps();
        // Only the values within the horizon should be kept
        assertEquals(2, sensorValues.length);
        assertEquals(2, sensorTimestamps.length);

        assertEquals(lux, sensorValues[0], EPSILON);
        assertEquals(newLux, sensorValues[1], EPSILON);
        assertEquals(mClock.now() + ANDROID_SLEEP_TIME - AMBIENT_LIGHT_HORIZON_LONG,
                sensorTimestamps[0]);
        assertEquals(mClock.now() + ANDROID_SLEEP_TIME,
                sensorTimestamps[1]);
    }

    @Test
    public void testGetSensorReadingsFullBuffer() throws Exception {
        ArgumentCaptor<SensorEventListener> listenerCaptor =
+4 −2
Original line number Diff line number Diff line
@@ -1251,7 +1251,8 @@ public final class DisplayPowerControllerTest {
                /* ambientLightHorizonLong= */ anyInt(),
                eq(lux),
                eq(nits),
                any(BrightnessClamperController.class)
                any(BrightnessClamperController.class),
                any(DisplayManagerFlags.class)
        );
    }

@@ -2247,7 +2248,8 @@ public final class DisplayPowerControllerTest {
                BrightnessRangeController brightnessRangeController,
                BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
                int ambientLightHorizonLong, float userLux, float userNits,
                BrightnessClamperController brightnessClamperController) {
                BrightnessClamperController brightnessClamperController,
                DisplayManagerFlags displayManagerFlags) {
            return mAutomaticBrightnessController;
        }

+9 −1
Original line number Diff line number Diff line
@@ -30,13 +30,21 @@ import java.lang.reflect.Method;
public final class TestUtils {

    public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
        return createSensorEvent(sensor, value, SystemClock.elapsedRealtimeNanos());
    }

    /**
     * Creates a light sensor event
     */
    public static SensorEvent createSensorEvent(Sensor sensor, int value, long timestamp)
            throws Exception {
        final Constructor<SensorEvent> constructor =
                SensorEvent.class.getDeclaredConstructor(int.class);
        constructor.setAccessible(true);
        final SensorEvent event = constructor.newInstance(1);
        event.sensor = sensor;
        event.values[0] = value;
        event.timestamp = SystemClock.elapsedRealtimeNanos();
        event.timestamp = timestamp;
        return event;
    }