From b29fdf1d71405da5cc4bee0bceeb2e5ebef44f1e Mon Sep 17 00:00:00 2001 From: Kenny Guy Date: Wed, 4 Dec 2019 12:57:04 +0000 Subject: [PATCH] Make the short term brightness model configurable. Bug: 146141793 Test: atest BrightnessConfigurationTest Test: atest AutomaticBrightnessControllerTest Test: manual - use reflection to configure the new parameters from turbo app and check dumpsys Change-Id: I78af8009f15400f2f91e55363066d97f008a7922 --- api/system-current.txt | 7 + api/test-current.txt | 7 + .../display/BrightnessConfiguration.java | 185 +++++++++++++++++- .../display/BrightnessConfigurationTest.java | 40 +++- .../AutomaticBrightnessController.java | 39 +--- .../display/BrightnessMappingStrategy.java | 89 ++++++++- .../display/DisplayPowerController.java | 5 +- .../AutomaticBrightnessControllerTest.java | 26 ++- 8 files changed, 353 insertions(+), 45 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index d18c0dcb5ab31..ad95691f2c429 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -2353,9 +2353,13 @@ package android.hardware.display { method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int); method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String); method public android.util.Pair getCurve(); + method public float getShortTermModelLowerLuxMultiplier(); + method public long getShortTermModelTimeout(); + method public float getShortTermModelUpperLuxMultiplier(); method public boolean shouldCollectColorSamples(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final long SHORT_TERM_TIMEOUT_UNSET = -1L; // 0xffffffffffffffffL } public static class BrightnessConfiguration.Builder { @@ -2366,6 +2370,9 @@ package android.hardware.display { method public int getMaxCorrectionsByCategory(); method public int getMaxCorrectionsByPackageName(); method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String); + method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float); + method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeout(long); + method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float); method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean); } diff --git a/api/test-current.txt b/api/test-current.txt index 3bdd4790372b1..4a047c653952b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1023,9 +1023,13 @@ package android.hardware.display { method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int); method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String); method public android.util.Pair getCurve(); + method public float getShortTermModelLowerLuxMultiplier(); + method public long getShortTermModelTimeout(); + method public float getShortTermModelUpperLuxMultiplier(); method public boolean shouldCollectColorSamples(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final long SHORT_TERM_TIMEOUT_UNSET = -1L; // 0xffffffffffffffffL } public static class BrightnessConfiguration.Builder { @@ -1036,6 +1040,9 @@ package android.hardware.display { method public int getMaxCorrectionsByCategory(); method public int getMaxCorrectionsByPackageName(); method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String); + method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float); + method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeout(long); + method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float); method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean); } diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index 139be8ee4d7b7..0a385389e2cfd 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -56,6 +57,16 @@ public final class BrightnessConfiguration implements Parcelable { private static final String ATTR_PACKAGE_NAME = "package-name"; private static final String ATTR_CATEGORY = "category"; private static final String ATTR_COLLECT_COLOR = "collect-color"; + private static final String ATTR_MODEL_TIMEOUT = "model-timeout"; + private static final String ATTR_MODEL_LOWER_BOUND = "model-lower-bound"; + private static final String ATTR_MODEL_UPPER_BOUND = "model-upper-bound"; + /** + * Returned from {@link #getShortTermModelTimeout()} if no timeout has been set. + * In this case the device will use the default timeout available in the + * {@link BrightnessConfiguration} returned from + * {@link DisplayManager#getDefaultBrightnessConfiguration()}. + */ + public static final long SHORT_TERM_TIMEOUT_UNSET = -1; private final float[] mLux; private final float[] mNits; @@ -63,17 +74,26 @@ public final class BrightnessConfiguration implements Parcelable { private final Map mCorrectionsByCategory; private final String mDescription; private final boolean mShouldCollectColorSamples; + private final long mShortTermModelTimeout; + private final float mShortTermModelLowerLuxMultiplier; + private final float mShortTermModelUpperLuxMultiplier; private BrightnessConfiguration(float[] lux, float[] nits, Map correctionsByPackageName, Map correctionsByCategory, String description, - boolean shouldCollectColorSamples) { + boolean shouldCollectColorSamples, + long shortTermModelTimeout, + float shortTermModelLowerLuxMultiplier, + float shortTermModelUpperLuxMultiplier) { mLux = lux; mNits = nits; mCorrectionsByPackageName = correctionsByPackageName; mCorrectionsByCategory = correctionsByCategory; mDescription = description; mShouldCollectColorSamples = shouldCollectColorSamples; + mShortTermModelTimeout = shortTermModelTimeout; + mShortTermModelLowerLuxMultiplier = shortTermModelLowerLuxMultiplier; + mShortTermModelUpperLuxMultiplier = shortTermModelUpperLuxMultiplier; } /** @@ -132,6 +152,42 @@ public final class BrightnessConfiguration implements Parcelable { return mShouldCollectColorSamples; } + /** + * Returns the timeout for the short term model in milliseconds. + * + * If the screen is inactive for this timeout then the short term model + * will check the lux range defined by {@link #getShortTermModelLowerLuxMultiplier()} and + * {@link #getShortTermModelUpperLuxMultiplier()} to decide whether to keep any adjustment + * the user has made to adaptive brightness. + */ + public long getShortTermModelTimeout() { + return mShortTermModelTimeout; + } + + /** + * Returns the multiplier used to calculate the upper bound for which + * a users adaptive brightness is considered valid. + * + * For example if a user changes the brightness when the ambient light level + * is 100 lux, the adjustment will be kept if the current ambient light level + * is {@code <= 100 + (100 * getShortTermModelUpperLuxMultiplier())}. + */ + public float getShortTermModelUpperLuxMultiplier() { + return mShortTermModelUpperLuxMultiplier; + } + + /** + * Returns the multiplier used to calculate the lower bound for which + * a users adaptive brightness is considered valid. + * + * For example if a user changes the brightness when the ambient light level + * is 100 lux, the adjustment will be kept if the current ambient light level + * is {@code >= 100 - (100 * getShortTermModelLowerLuxMultiplier())}. + */ + public float getShortTermModelLowerLuxMultiplier() { + return mShortTermModelLowerLuxMultiplier; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloatArray(mLux); @@ -152,6 +208,9 @@ public final class BrightnessConfiguration implements Parcelable { } dest.writeString(mDescription); dest.writeBoolean(mShouldCollectColorSamples); + dest.writeLong(mShortTermModelTimeout); + dest.writeFloat(mShortTermModelLowerLuxMultiplier); + dest.writeFloat(mShortTermModelUpperLuxMultiplier); } @Override @@ -182,6 +241,15 @@ public final class BrightnessConfiguration implements Parcelable { sb.append(mDescription); } sb.append(", shouldCollectColorSamples = " + mShouldCollectColorSamples); + if (mShortTermModelTimeout >= 0) { + sb.append(", shortTermModelTimeout = " + mShortTermModelTimeout); + } + if (!Float.isNaN(mShortTermModelLowerLuxMultiplier)) { + sb.append(", shortTermModelLowerLuxMultiplier = " + mShortTermModelLowerLuxMultiplier); + } + if (!Float.isNaN(mShortTermModelLowerLuxMultiplier)) { + sb.append(", shortTermModelUpperLuxMultiplier = " + mShortTermModelUpperLuxMultiplier); + } sb.append("'}"); return sb.toString(); } @@ -197,6 +265,9 @@ public final class BrightnessConfiguration implements Parcelable { result = result * 31 + mDescription.hashCode(); } result = result * 31 + Boolean.hashCode(mShouldCollectColorSamples); + result = result * 31 + Long.hashCode(mShortTermModelTimeout); + result = result * 31 + Float.hashCode(mShortTermModelLowerLuxMultiplier); + result = result * 31 + Float.hashCode(mShortTermModelUpperLuxMultiplier); return result; } @@ -213,7 +284,19 @@ public final class BrightnessConfiguration implements Parcelable { && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName) && mCorrectionsByCategory.equals(other.mCorrectionsByCategory) && Objects.equals(mDescription, other.mDescription) - && mShouldCollectColorSamples == other.mShouldCollectColorSamples; + && mShouldCollectColorSamples == other.mShouldCollectColorSamples + && mShortTermModelTimeout == other.mShortTermModelTimeout + && checkFloatEquals(mShortTermModelLowerLuxMultiplier, + other.mShortTermModelLowerLuxMultiplier) + && checkFloatEquals(mShortTermModelUpperLuxMultiplier, + other.mShortTermModelUpperLuxMultiplier); + } + + private boolean checkFloatEquals(float one, float two) { + if (Float.isNaN(one) && Float.isNaN(two)) { + return true; + } + return one == two; } public static final @android.annotation.NonNull Creator CREATOR = @@ -243,6 +326,9 @@ public final class BrightnessConfiguration implements Parcelable { builder.setDescription(description); final boolean shouldCollectColorSamples = in.readBoolean(); builder.setShouldCollectColorSamples(shouldCollectColorSamples); + builder.setShortTermModelTimeout(in.readLong()); + builder.setShortTermModelLowerLuxMultiplier(in.readFloat()); + builder.setShortTermModelUpperLuxMultiplier(in.readFloat()); return builder.build(); } @@ -296,6 +382,18 @@ public final class BrightnessConfiguration implements Parcelable { if (mShouldCollectColorSamples) { serializer.attribute(null, ATTR_COLLECT_COLOR, Boolean.toString(true)); } + if (mShortTermModelTimeout >= 0) { + serializer.attribute(null, ATTR_MODEL_TIMEOUT, + Long.toString(mShortTermModelTimeout)); + } + if (!Float.isNaN(mShortTermModelLowerLuxMultiplier)) { + serializer.attribute(null, ATTR_MODEL_LOWER_BOUND, + Float.toString(mShortTermModelLowerLuxMultiplier)); + } + if (!Float.isNaN(mShortTermModelUpperLuxMultiplier)) { + serializer.attribute(null, ATTR_MODEL_UPPER_BOUND, + Float.toString(mShortTermModelUpperLuxMultiplier)); + } serializer.endTag(null, TAG_BRIGHTNESS_PARAMS); } @@ -320,6 +418,9 @@ public final class BrightnessConfiguration implements Parcelable { Map correctionsByPackageName = new HashMap<>(); Map correctionsByCategory = new HashMap<>(); boolean shouldCollectColorSamples = false; + long shortTermModelTimeout = SHORT_TERM_TIMEOUT_UNSET; + float shortTermModelLowerLuxMultiplier = Float.NaN; + float shortTermModelUpperLuxMultiplier = Float.NaN; final int configDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, configDepth)) { if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) { @@ -357,6 +458,12 @@ public final class BrightnessConfiguration implements Parcelable { } else if (TAG_BRIGHTNESS_PARAMS.equals(parser.getName())) { shouldCollectColorSamples = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_COLLECT_COLOR)); + Long timeout = loadLongFromXml(parser, ATTR_MODEL_TIMEOUT); + if (timeout != null) { + shortTermModelTimeout = timeout; + } + shortTermModelLowerLuxMultiplier = loadFloatFromXml(parser, ATTR_MODEL_LOWER_BOUND); + shortTermModelUpperLuxMultiplier = loadFloatFromXml(parser, ATTR_MODEL_UPPER_BOUND); } } final int n = luxList.size(); @@ -380,6 +487,9 @@ public final class BrightnessConfiguration implements Parcelable { builder.addCorrectionByCategory(category, correction); } builder.setShouldCollectColorSamples(shouldCollectColorSamples); + builder.setShortTermModelTimeout(shortTermModelTimeout); + builder.setShortTermModelLowerLuxMultiplier(shortTermModelLowerLuxMultiplier); + builder.setShortTermModelUpperLuxMultiplier(shortTermModelUpperLuxMultiplier); return builder.build(); } @@ -392,6 +502,16 @@ public final class BrightnessConfiguration implements Parcelable { } } + private static Long loadLongFromXml(XmlPullParser parser, String attribute) { + final String string = parser.getAttributeValue(null, attribute); + try { + return Long.parseLong(string); + } catch (NullPointerException | NumberFormatException e) { + // Ignoring + } + return null; + } + /** * A builder class for {@link BrightnessConfiguration}s. */ @@ -405,6 +525,9 @@ public final class BrightnessConfiguration implements Parcelable { private Map mCorrectionsByCategory; private String mDescription; private boolean mShouldCollectColorSamples; + private long mShortTermModelTimeout = SHORT_TERM_TIMEOUT_UNSET; + private float mShortTermModelLowerLuxMultiplier = Float.NaN; + private float mShortTermModelUpperLuxMultiplier = Float.NaN; /** * Constructs the builder with the control points for the brightness curve. @@ -541,6 +664,60 @@ public final class BrightnessConfiguration implements Parcelable { return this; } + /** + * Sets the timeout for the short term model in milliseconds. + * + * If the screen is inactive for this timeout then the short term model + * will check the lux range defined by {@link #setShortTermModelLowerLuxMultiplier(float))} + * and {@link #setShortTermModelUpperLuxMultiplier(float)} to decide whether to keep any + * adjustment the user has made to adaptive brightness. + */ + @NonNull + public Builder setShortTermModelTimeout(long shortTermModelTimeout) { + mShortTermModelTimeout = shortTermModelTimeout; + return this; + } + + /** + * Sets the multiplier used to calculate the upper bound for which + * a users adaptive brightness is considered valid. + * + * For example if a user changes the brightness when the ambient light level + * is 100 lux, the adjustment will be kept if the current ambient light level + * is {@code <= 100 + (100 * shortTermModelUpperLuxMultiplier)}. + * + * @throws IllegalArgumentException if shortTermModelUpperLuxMultiplier is negative. + */ + @NonNull + public Builder setShortTermModelUpperLuxMultiplier( + @FloatRange(from = 0.0f) float shortTermModelUpperLuxMultiplier) { + if (shortTermModelUpperLuxMultiplier < 0.0f) { + throw new IllegalArgumentException("Negative lux multiplier"); + } + mShortTermModelUpperLuxMultiplier = shortTermModelUpperLuxMultiplier; + return this; + } + + /** + * Returns the multiplier used to calculate the lower bound for which + * a users adaptive brightness is considered valid. + * + * For example if a user changes the brightness when the ambient light level + * is 100 lux, the adjustment will be kept if the current ambient light level + * is {@code >= 100 - (100 * shortTermModelLowerLuxMultiplier)}. + * + * @throws IllegalArgumentException if shortTermModelUpperLuxMultiplier is negative. + */ + @NonNull + public Builder setShortTermModelLowerLuxMultiplier( + @FloatRange(from = 0.0f) float shortTermModelLowerLuxMultiplier) { + if (shortTermModelLowerLuxMultiplier < 0.0f) { + throw new IllegalArgumentException("Negative lux multiplier"); + } + mShortTermModelLowerLuxMultiplier = shortTermModelLowerLuxMultiplier; + return this; + } + /** * Builds the {@link BrightnessConfiguration}. */ @@ -550,7 +727,9 @@ public final class BrightnessConfiguration implements Parcelable { throw new IllegalStateException("A curve must be set!"); } return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName, - mCorrectionsByCategory, mDescription, mShouldCollectColorSamples); + mCorrectionsByCategory, mDescription, mShouldCollectColorSamples, + mShortTermModelTimeout, mShortTermModelLowerLuxMultiplier, + mShortTermModelUpperLuxMultiplier); } private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) { diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java index 85aa1184c501a..895b22c7037cd 100644 --- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java +++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java @@ -114,11 +114,27 @@ public class BrightnessConfigurationTest { }); } + @Test + public void testLuxMultipliersMustBePositive() { + BrightnessConfiguration.Builder config = new BrightnessConfiguration.Builder( + LUX_LEVELS, NITS_LEVELS); + assertThrows(IllegalArgumentException.class, () -> { + config.setShortTermModelUpperLuxMultiplier(-1f); + }); + + assertThrows(IllegalArgumentException.class, () -> { + config.setShortTermModelLowerLuxMultiplier(-1f); + }); + } + @Test public void testParceledConfigIsEquivalent() { BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); builder.setShouldCollectColorSamples(true); + builder.setShortTermModelTimeout(1234L); + builder.setShortTermModelLowerLuxMultiplier(0.9f); + builder.setShortTermModelUpperLuxMultiplier(0.2f); builder.addCorrectionByCategory(3, BrightnessCorrection.createScaleAndTranslateLog(1.0f, 2.0f)); builder.addCorrectionByPackageName("a.package.name", @@ -137,6 +153,9 @@ public class BrightnessConfigurationTest { BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); builder.setShouldCollectColorSamples(true); + builder.setShortTermModelTimeout(123L); + builder.setShortTermModelLowerLuxMultiplier(0.4f); + builder.setShortTermModelUpperLuxMultiplier(0.8f); builder.addCorrectionByCategory(3, BrightnessCorrection.createScaleAndTranslateLog(1.0f, 2.0f)); builder.addCorrectionByPackageName("a.package.name", @@ -208,13 +227,28 @@ public class BrightnessConfigurationTest { BrightnessCorrection.createScaleAndTranslateLog(1.0f, 2.0f)); builder.addCorrectionByPackageName("a.package.name", BrightnessCorrection.createScaleAndTranslateLog(1.0f, 2.0f)); + BrightnessConfiguration correctionsDiffer = builder.build(); + assertNotEquals(baseConfig, correctionsDiffer); + + builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); + builder.setShouldCollectColorSamples(true); BrightnessConfiguration colorCollectionDiffers = builder.build(); assertNotEquals(baseConfig, colorCollectionDiffers); builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); - builder.setShouldCollectColorSamples(true); - BrightnessConfiguration correctionsDiffer = builder.build(); - assertNotEquals(baseConfig, correctionsDiffer); + builder.setShortTermModelTimeout(300L); + BrightnessConfiguration timeoutDiffers = builder.build(); + assertNotEquals(baseConfig, timeoutDiffers); + + builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); + builder.setShortTermModelLowerLuxMultiplier(0.7f); + BrightnessConfiguration lowerLuxDiffers = builder.build(); + assertNotEquals(baseConfig, lowerLuxDiffers); + + builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS); + builder.setShortTermModelUpperLuxMultiplier(0.6f); + BrightnessConfiguration upperLuxDiffers = builder.build(); + assertNotEquals(baseConfig, upperLuxDiffers); } private static void assertArrayEquals(float[] expected, float[] actual, String name) { diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 177e2d8b5fa26..c99774a41f104 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -52,9 +52,6 @@ class AutomaticBrightnessController { private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false; - // If true, enables the use of the screen auto-brightness adjustment setting. - private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true; - // How long the current sensor reading is assumed to be valid beyond the current time. // This provides a bit of prediction, as well as ensures that the weight for the last sample is // non-zero, which in turn ensures that the total weight is non-zero. @@ -131,13 +128,6 @@ class AutomaticBrightnessController { private boolean mLoggingEnabled; - // Timeout after which we remove the effects any user interactions might've had on the - // brightness mapping. This timeout doesn't start until we transition to a non-interactive - // display policy so that we don't reset while users are using their devices, but also so that - // we don't erroneously keep the short-term model if the device is dozing but the display is - // fully on. - private long mShortTermModelTimeout; - // Amount of time to delay auto-brightness after screen on while waiting for // the light sensor to warm-up in milliseconds. // May be 0 if no warm-up is required. @@ -202,7 +192,6 @@ class AutomaticBrightnessController { // we use a relative threshold to determine when to revert to the OEM curve. private boolean mShortTermModelValid; private float mShortTermModelAnchor; - private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f; // Context-sensitive brightness configurations require keeping track of the foreground app's // package name and category, which is done by registering a TaskStackListener to call back to @@ -224,14 +213,13 @@ class AutomaticBrightnessController { int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout, + HysteresisLevels screenBrightnessThresholds, PackageManager packageManager) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper, lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, - ambientBrightnessThresholds, screenBrightnessThresholds, shortTermModelTimeout, - packageManager); + ambientBrightnessThresholds, screenBrightnessThresholds, packageManager); } @VisibleForTesting @@ -241,7 +229,7 @@ class AutomaticBrightnessController { int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout, + HysteresisLevels screenBrightnessThresholds, PackageManager packageManager) { mInjector = injector; mCallbacks = callbacks; @@ -261,7 +249,6 @@ class AutomaticBrightnessController { mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS; mAmbientBrightnessThresholds = ambientBrightnessThresholds; mScreenBrightnessThresholds = screenBrightnessThresholds; - mShortTermModelTimeout = shortTermModelTimeout; mShortTermModelValid = true; mShortTermModelAnchor = -1; @@ -370,7 +357,7 @@ class AutomaticBrightnessController { } if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) { mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL, - mShortTermModelTimeout); + mBrightnessMapper.getShortTermModelTimeout()); } else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) { mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL); } @@ -452,7 +439,7 @@ class AutomaticBrightnessController { pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer); pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy)); - pw.println(" mShortTermModelTimeout=" + mShortTermModelTimeout); + pw.println(" mShortTermModelTimeout=" + mBrightnessMapper.getShortTermModelTimeout()); pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor); pw.println(" mShortTermModelValid=" + mShortTermModelValid); pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending); @@ -552,20 +539,10 @@ class AutomaticBrightnessController { // If the short term model was invalidated and the change is drastic enough, reset it. if (!mShortTermModelValid && mShortTermModelAnchor != -1) { - final float minAmbientLux = - mShortTermModelAnchor - mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO; - final float maxAmbientLux = - mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO; - if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) { - if (mLoggingEnabled) { - Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " + - minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux); - } - mShortTermModelValid = true; - } else { - Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + mAmbientLux + - "(" + minAmbientLux + ", " + maxAmbientLux + ")"); + if (mBrightnessMapper.shouldResetShortTermModel(mAmbientLux, mShortTermModelAnchor)) { resetShortTermModel(); + } else { + mShortTermModelValid = true; } } } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 171cc5abdb97d..ff0b01594bcf4 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -47,6 +47,7 @@ public abstract class BrightnessMappingStrategy { private static final float LUX_GRAD_SMOOTHING = 0.25f; private static final float MAX_GRAD = 1.0f; + private static final float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f; protected boolean mLoggingEnabled; @@ -69,6 +70,9 @@ public abstract class BrightnessMappingStrategy { int[] backlightRange = resources.getIntArray( com.android.internal.R.array.config_screenBrightnessBacklight); + long shortTermModelTimeout = resources.getInteger( + com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); + if (isValidMapping(nitsRange, backlightRange) && isValidMapping(luxLevels, brightnessLevelsNits)) { int minimumBacklight = resources.getInteger( @@ -82,11 +86,14 @@ public abstract class BrightnessMappingStrategy { } BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( luxLevels, brightnessLevelsNits); + builder.setShortTermModelTimeout(shortTermModelTimeout); + builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); + builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange, autoBrightnessAdjustmentMaxGamma); } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight, - autoBrightnessAdjustmentMaxGamma); + autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout); } else { return null; } @@ -188,6 +195,12 @@ public abstract class BrightnessMappingStrategy { */ public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config); + /** + * Gets the current {@link BrightnessConfiguration}. + */ + @Nullable + public abstract BrightnessConfiguration getBrightnessConfiguration(); + /** * Returns the desired brightness of the display based on the current ambient lux, including * any context-related corrections. @@ -274,8 +287,53 @@ public abstract class BrightnessMappingStrategy { /** @return The default brightness configuration. */ public abstract BrightnessConfiguration getDefaultConfig(); + + /** + * Returns the timeout for the short term model + * + * Timeout after which we remove the effects any user interactions might've had on the + * brightness mapping. This timeout doesn't start until we transition to a non-interactive + * display policy so that we don't reset while users are using their devices, but also so that + * we don't erroneously keep the short-term model if the device is dozing but the + * display is fully on. + */ + public abstract long getShortTermModelTimeout(); + public abstract void dump(PrintWriter pw); + /** + * Check if the short term model should be reset given the anchor lux the last + * brightness change was made at and the current ambient lux. + */ + public boolean shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor) { + BrightnessConfiguration config = getBrightnessConfiguration(); + float minThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO; + float maxThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO; + if (config != null) { + if (!Float.isNaN(config.getShortTermModelLowerLuxMultiplier())) { + minThresholdRatio = config.getShortTermModelLowerLuxMultiplier(); + } + if (!Float.isNaN(config.getShortTermModelUpperLuxMultiplier())) { + maxThresholdRatio = config.getShortTermModelUpperLuxMultiplier(); + } + } + final float minAmbientLux = + shortTermModelAnchor - shortTermModelAnchor * minThresholdRatio; + final float maxAmbientLux = + shortTermModelAnchor + shortTermModelAnchor * maxThresholdRatio; + if (minAmbientLux < ambientLux && ambientLux <= maxAmbientLux) { + if (mLoggingEnabled) { + Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " + + minAmbientLux + " < " + ambientLux + " < " + maxAmbientLux); + } + return false; + } else { + Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + ambientLux + + "(" + minAmbientLux + ", " + maxAmbientLux + ")"); + return true; + } + } + protected float normalizeAbsoluteBrightness(int brightness) { brightness = MathUtils.constrain(brightness, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); @@ -455,8 +513,10 @@ public abstract class BrightnessMappingStrategy { private float mAutoBrightnessAdjustment; private float mUserLux; private float mUserBrightness; + private long mShortTermModelTimeout; - public SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma) { + private SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma, + long timeout) { Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, "Lux and brightness arrays must not be empty!"); Preconditions.checkArgument(lux.length == brightness.length, @@ -481,6 +541,12 @@ public abstract class BrightnessMappingStrategy { PLOG.start("simple mapping strategy"); } computeSpline(); + mShortTermModelTimeout = timeout; + } + + @Override + public long getShortTermModelTimeout() { + return mShortTermModelTimeout; } @Override @@ -488,6 +554,11 @@ public abstract class BrightnessMappingStrategy { return false; } + @Override + public BrightnessConfiguration getBrightnessConfiguration() { + return null; + } + @Override public float getBrightness(float lux, String packageName, @ApplicationInfo.Category int category) { @@ -659,6 +730,15 @@ public abstract class BrightnessMappingStrategy { computeSpline(); } + @Override + public long getShortTermModelTimeout() { + if (mConfig.getShortTermModelTimeout() >= 0) { + return mConfig.getShortTermModelTimeout(); + } else { + return mDefaultConfig.getShortTermModelTimeout(); + } + } + @Override public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { if (config == null) { @@ -675,6 +755,11 @@ public abstract class BrightnessMappingStrategy { return true; } + @Override + public BrightnessConfiguration getBrightnessConfiguration() { + return mConfig; + } + @Override public float getBrightness(float lux, String packageName, @ApplicationInfo.Category int category) { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index e42545e111ed4..f1655f0ed6e3c 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -483,8 +483,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + initialLightSensorRate + ") to be less than or equal to " + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); } - int shortTermModelTimeout = resources.getInteger( - com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); String lightSensorType = resources.getString( com.android.internal.R.string.config_displayLightSensorType); @@ -498,8 +496,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, - screenBrightnessThresholds, shortTermModelTimeout, - context.getPackageManager()); + screenBrightnessThresholds, context.getPackageManager()); } else { mUseSoftwareAutoBrightnessConfig = false; } diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index f6c4d3aa5f5f6..ca00116d7eadb 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -53,7 +53,6 @@ public class AutomaticBrightnessControllerTest { private static final int INITIAL_LIGHT_SENSOR_RATE = 20; private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0; private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0; - private static final int SHORT_TERM_MODEL_TIMEOUT = 0; private static final float DOZE_SCALE_FACTOR = 0.0f; private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false; @@ -86,7 +85,7 @@ public class AutomaticBrightnessControllerTest { BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds, - mScreenBrightnessThresholds, SHORT_TERM_MODEL_TIMEOUT, mPackageManager); + mScreenBrightnessThresholds, mPackageManager); controller.setLoggingEnabled(true); // Configure the brightness controller and grab an instance of the sensor listener, @@ -189,4 +188,27 @@ public class AutomaticBrightnessControllerTest { listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); assertEquals(255, controller.getAutomaticScreenBrightness()); } + + @Test + public void testUserAddUserDataPoint() throws Exception { + Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); + AutomaticBrightnessController controller = setupController(lightSensor); + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Sensor reads 1000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000)); + + // User sets brightness to 100 + controller.configure(true /* enable */, null /* configuration */, + 100 /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, + false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); + + // There should be a user data point added to the mapper. + verify(mBrightnessMappingStrategy).addUserDataPoint(1000f, 100); + } }