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

Commit f1e67364 authored by Dan Gittik's avatar Dan Gittik
Browse files

Added safeguards for pushed brighntess curve.

Currently, the only safeguard is making sure the brightness curve isn't too dark to turn
auto-brightness off (or connect the phone to a computer and reset the curve with ADB).

Test: atest BrightnessMappingStrategyTest.

Change-Id: Ic87292fe51639a0001d0d5643ca24cda37de5753
Fixes: 74439069
parent c6d9f7ce
Loading
Loading
Loading
Loading
+24 −0
Original line number Original line Diff line number Diff line
@@ -1340,6 +1340,30 @@
    <integer-array name="config_autoBrightnessKeyboardBacklightValues">
    <integer-array name="config_autoBrightnessKeyboardBacklightValues">
    </integer-array>
    </integer-array>


    <!-- Array of light sensor lux values to define the minimum brightness curve, which guarantees
         that any curve that dips below it is rejected by the system.
         This prevents auto-brightness from setting the screen so dark as to prevent the user from
         disabling auto-brightness or reseting the brightness curve via ADB.

         The control points must be strictly increasing. Each control point corresponds to an entry
         in the minimum brightness nits array. -->
    <integer-array name="config_autoBrightnessMinimumBrightnessCurveLux">
        <item>2000</item>
        <item>4000</item>
    </integer-array>

    <!-- Array of desired screen brightness in nits corresponding to the lux values
         in the config_autoBrightnessMinimumBrightnessCurveLux array.

         This array should have size one greater than the size of the
         config_autoBrightnessMinimumBrightnessCurveLux array. The values must be non-negative and
         non-decreasing. -->
    <array name="config_autoBrightnessMinimumBrightnessCurveNits">
        <item>1.0</item>
        <item>50.0</item>
        <item>90.0</item>
    </array>

    <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
    <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
         percent. The length of this array is assumed to be one greater than
         percent. The length of this array is assumed to be one greater than
         config_dynamicHysteresisLuxLevels. The brightening threshold is calculated as
         config_dynamicHysteresisLuxLevels. The brightening threshold is calculated as
+2 −0
Original line number Original line Diff line number Diff line
@@ -1794,6 +1794,8 @@
  <java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
  <java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
  <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
  <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
  <java-symbol type="array" name="config_autoBrightnessLevels" />
  <java-symbol type="array" name="config_autoBrightnessLevels" />
  <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveLux" />
  <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveNits" />
  <java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
  <java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
  <java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
  <java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
  <java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
  <java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
+36 −4
Original line number Original line Diff line number Diff line
@@ -60,8 +60,14 @@ public abstract class BrightnessMappingStrategy {
        int[] backlightRange = resources.getIntArray(
        int[] backlightRange = resources.getIntArray(
                com.android.internal.R.array.config_screenBrightnessBacklight);
                com.android.internal.R.array.config_screenBrightnessBacklight);


        float[] minimumBrightnessCurveLux = getLuxLevels(resources.getIntArray(
                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux));
        float[] minimumBrightnessCurveNits = getFloatArray(resources.obtainTypedArray(
                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits));

        if (isValidMapping(nitsRange, backlightRange)
        if (isValidMapping(nitsRange, backlightRange)
                && isValidMapping(luxLevels, brightnessLevelsNits)) {
                && isValidMapping(luxLevels, brightnessLevelsNits)
                && isValidMapping(minimumBrightnessCurveLux, minimumBrightnessCurveNits)) {
            int minimumBacklight = resources.getInteger(
            int minimumBacklight = resources.getInteger(
                    com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
                    com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
            int maximumBacklight = resources.getInteger(
            int maximumBacklight = resources.getInteger(
@@ -73,7 +79,8 @@ public abstract class BrightnessMappingStrategy {
            }
            }
            BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
            BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
            builder.setCurve(luxLevels, brightnessLevelsNits);
            builder.setCurve(luxLevels, brightnessLevelsNits);
            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
                    minimumBrightnessCurveLux, minimumBrightnessCurveNits);
        } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
        } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
            return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
            return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
        } else {
        } else {
@@ -448,8 +455,11 @@ public abstract class BrightnessMappingStrategy {
        private float mUserLux;
        private float mUserLux;
        private float mUserBrightness;
        private float mUserBrightness;


        private final Spline mMinimumBrightnessCurve;

        public PhysicalMappingStrategy(BrightnessConfiguration config,
        public PhysicalMappingStrategy(BrightnessConfiguration config,
                float[] nits, int[] backlight) {
                float[] nits, int[] backlight, float[] minimumBrightnessCurveLux,
                float[] minimumBrightnessCurveNits) {
            Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
            Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
                    "Nits and backlight arrays must not be empty!");
                    "Nits and backlight arrays must not be empty!");
            Preconditions.checkArgument(nits.length == backlight.length,
            Preconditions.checkArgument(nits.length == backlight.length,
@@ -469,6 +479,9 @@ public abstract class BrightnessMappingStrategy {
                normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
                normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
            }
            }


            mMinimumBrightnessCurve = Spline.createSpline(
                minimumBrightnessCurveLux, minimumBrightnessCurveNits);

            mNitsToBacklightSpline = createSpline(nits, normalizedBacklight);
            mNitsToBacklightSpline = createSpline(nits, normalizedBacklight);
            mBacklightToNitsSpline = createSpline(normalizedBacklight, nits);
            mBacklightToNitsSpline = createSpline(normalizedBacklight, nits);


@@ -484,7 +497,7 @@ public abstract class BrightnessMappingStrategy {
            if (config.equals(mConfig)) {
            if (config.equals(mConfig)) {
                return false;
                return false;
            }
            }

            validateBrightnessConfiguration(config);
            Pair<float[], float[]> curve = config.getCurve();
            Pair<float[], float[]> curve = config.getCurve();
            mBrightnessSpline = createSpline(curve.first /*lux*/, curve.second /*nits*/);
            mBrightnessSpline = createSpline(curve.first /*lux*/, curve.second /*nits*/);
            mConfig = config;
            mConfig = config;
@@ -549,5 +562,24 @@ public abstract class BrightnessMappingStrategy {
            pw.println("  mUserLux=" + mUserLux);
            pw.println("  mUserLux=" + mUserLux);
            pw.println("  mUserBrightness=" + mUserBrightness);
            pw.println("  mUserBrightness=" + mUserBrightness);
        }
        }

        private void validateBrightnessConfiguration(BrightnessConfiguration config) {
            Pair<float[], float[]> curve = config.getCurve();
            Spline brightnessSpline = Spline.createSpline(curve.first, curve.second);
            if (isBrightnessSplineTooDark(brightnessSpline)) {
                throw new IllegalArgumentException("brightness curve is too dark");
            }
        }

        private boolean isBrightnessSplineTooDark(Spline brightnessSpline) {
            float[] lux = mDefaultConfig.getCurve().first;
            for (int i = 0; i < lux.length; i++) {
                if (brightnessSpline.interpolate(lux[i]) <
                        mMinimumBrightnessCurve.interpolate(lux[i])) {
                    return true;
                }
            }
            return false;
        }
    }
    }
}
}
+90 −0
Original line number Original line Diff line number Diff line
@@ -85,6 +85,9 @@ public class BrightnessMappingStrategyTest {
        255
        255
    };
    };


    private static final int[] MINIMUM_BRIGHTNESS_CURVE_LUX = { 2000, 4000 };
    private static final float[] MINIMUM_BRIGHTNESS_CURVE_NITS = { 1.0f, 50.0f, 90.0f };

    private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
    private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
    private static final int[] BACKLIGHT_RANGE = { 1, 255 };
    private static final int[] BACKLIGHT_RANGE = { 1, 255 };


@@ -381,6 +384,19 @@ public class BrightnessMappingStrategyTest {
                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                .thenReturn(mockBrightnessLevelNits);
                .thenReturn(mockBrightnessLevelNits);


        int[] mockMinimumBrightnessCurveLux = new int[MINIMUM_BRIGHTNESS_CURVE_LUX.length];
        for (int i = 0; i < mockMinimumBrightnessCurveLux.length; i++) {
            mockMinimumBrightnessCurveLux[i] = (int) MINIMUM_BRIGHTNESS_CURVE_LUX[i];
        }
        when(mockResources.getIntArray(
                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux))
                .thenReturn(mockMinimumBrightnessCurveLux);
        TypedArray mockMinimumBrightnessCurveNits = createFloatTypedArray(
                MINIMUM_BRIGHTNESS_CURVE_NITS);
        when(mockResources.obtainTypedArray(
                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits))
                .thenReturn(mockMinimumBrightnessCurveNits);

        TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
        TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
        when(mockResources.obtainTypedArray(
        when(mockResources.obtainTypedArray(
                com.android.internal.R.array.config_screenBrightnessNits))
                com.android.internal.R.array.config_screenBrightnessNits))
@@ -419,4 +435,78 @@ public class BrightnessMappingStrategyTest {
        return mockArray;
        return mockArray;
    }
    }


    private float[] getNearMinimumNits(float epsilon) {
        float[] lux = new float[MINIMUM_BRIGHTNESS_CURVE_LUX.length + 1];
        for (int i = 0; i < MINIMUM_BRIGHTNESS_CURVE_LUX.length; i++) {
            lux[i+1] = MINIMUM_BRIGHTNESS_CURVE_LUX[i];
        }
        Spline minimumBrightnessCurve = Spline.createSpline(lux, MINIMUM_BRIGHTNESS_CURVE_NITS);
        float[] nits = new float[LUX_LEVELS.length];
        for (int i = 0; i < nits.length; i++) {
            nits[i] = minimumBrightnessCurve.interpolate(LUX_LEVELS[i]) + epsilon;
        }
        return nits;
    }

    @Test
    public void testCreateWithTooDarkBrightnessConfigurationThrowsException() {
        float[] nits = getNearMinimumNits(-0.1f);
        Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
        Exception thrown = null;
        try {
            BrightnessMappingStrategy.create(res);
        } catch (IllegalArgumentException e) {
            thrown = e;
        }
        assertNotNull("Failed to throw IllegalArgumentException", thrown);
    }

    @Test
    public void testCreationWithBrightEnoughBrightnessConfigurationDoesNotThrowException() {
        float[] nits = getNearMinimumNits(0);
        Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
        assertNotNull("Failed to create BrightnessMappingStrategy",
                BrightnessMappingStrategy.create(res));
    }

    @Test
    public void testSettingTooDarkBrightnessConfigurationThrowsException() {
        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
                BACKLIGHT_RANGE);
        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
        assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
        float[] lux = new float[LUX_LEVELS.length];
        for (int i = 0; i < lux.length; i++) {
            lux[i] = LUX_LEVELS[i];
        }
        float[] nits = getNearMinimumNits(-0.1f);
        BrightnessConfiguration config = new BrightnessConfiguration.Builder()
                .setCurve(lux, nits)
                .build();
        Exception thrown = null;
        try {
            strategy.setBrightnessConfiguration(config);
        } catch (IllegalArgumentException e) {
            thrown = e;
        }
        assertNotNull("Failed to throw IllegalArgumentException", thrown);
    }

    @Test
    public void testSettingBrightEnouhgBrightnessConfigurationDoesNotThrowException() {
        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
                BACKLIGHT_RANGE);
        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
        assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
        float[] lux = new float[LUX_LEVELS.length];
        for (int i = 0; i < lux.length; i++) {
            lux[i] = LUX_LEVELS[i];
        }
        float[] nits = getNearMinimumNits(0);
        BrightnessConfiguration config = new BrightnessConfiguration.Builder()
                .setCurve(lux, nits)
                .build();
        assertTrue("failed to set brightness configuration",
            strategy.setBrightnessConfiguration(config));
    }
}
}