Merge "Make the short term brightness model configurable."
diff --git a/api/system-current.txt b/api/system-current.txt
index 062df8d..b79289f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2414,9 +2414,13 @@
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
     method public android.util.Pair<float[],float[]> 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<android.hardware.display.BrightnessConfiguration> CREATOR;
+    field public static final long SHORT_TERM_TIMEOUT_UNSET = -1L; // 0xffffffffffffffffL
   }
 
   public static class BrightnessConfiguration.Builder {
@@ -2427,6 +2431,9 @@
     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 a7cecf8..498dd2f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1058,9 +1058,13 @@
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
     method public android.util.Pair<float[],float[]> 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<android.hardware.display.BrightnessConfiguration> CREATOR;
+    field public static final long SHORT_TERM_TIMEOUT_UNSET = -1L; // 0xffffffffffffffffL
   }
 
   public static class BrightnessConfiguration.Builder {
@@ -1071,6 +1075,9 @@
     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 139be8e..0a38538 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 @@
     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 @@
     private final Map<Integer, BrightnessCorrection> 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<String, BrightnessCorrection> correctionsByPackageName,
             Map<Integer, BrightnessCorrection> 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 @@
         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 @@
         }
         dest.writeString(mDescription);
         dest.writeBoolean(mShouldCollectColorSamples);
+        dest.writeLong(mShortTermModelTimeout);
+        dest.writeFloat(mShortTermModelLowerLuxMultiplier);
+        dest.writeFloat(mShortTermModelUpperLuxMultiplier);
     }
 
     @Override
@@ -182,6 +241,15 @@
             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 @@
             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 @@
                 && 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<BrightnessConfiguration> CREATOR =
@@ -243,6 +326,9 @@
             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 @@
         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 @@
         Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
         Map<Integer, BrightnessCorrection> 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 @@
             } 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 @@
             builder.addCorrectionByCategory(category, correction);
         }
         builder.setShouldCollectColorSamples(shouldCollectColorSamples);
+        builder.setShortTermModelTimeout(shortTermModelTimeout);
+        builder.setShortTermModelLowerLuxMultiplier(shortTermModelLowerLuxMultiplier);
+        builder.setShortTermModelUpperLuxMultiplier(shortTermModelUpperLuxMultiplier);
         return builder.build();
     }
 
@@ -392,6 +502,16 @@
         }
     }
 
+    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 @@
         private Map<Integer, BrightnessCorrection> 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.
@@ -542,6 +665,60 @@
         }
 
         /**
+         * 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}.
          */
         @NonNull
@@ -550,7 +727,9 @@
                 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 85aa118..895b22c 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -115,10 +115,26 @@
     }
 
     @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 @@
         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 @@
                 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 177e2d8..c99774a 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 @@
 
     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 @@
 
     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 @@
     // 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 @@
             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 @@
             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 @@
         mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
         mAmbientBrightnessThresholds = ambientBrightnessThresholds;
         mScreenBrightnessThresholds = screenBrightnessThresholds;
-        mShortTermModelTimeout = shortTermModelTimeout;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
 
@@ -370,7 +357,7 @@
         }
         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 @@
         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 @@
 
         // 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 171cc5a..ff0b015 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 @@
 
     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 @@
         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 @@
             }
             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;
         }
@@ -189,6 +196,12 @@
     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 @@
     /** @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 @@
         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 @@
                 PLOG.start("simple mapping strategy");
             }
             computeSpline();
+            mShortTermModelTimeout = timeout;
+        }
+
+        @Override
+        public long getShortTermModelTimeout() {
+            return mShortTermModelTimeout;
         }
 
         @Override
@@ -489,6 +555,11 @@
         }
 
         @Override
+        public BrightnessConfiguration getBrightnessConfiguration() {
+            return null;
+        }
+
+        @Override
         public float getBrightness(float lux, String packageName,
                 @ApplicationInfo.Category int category) {
             return mSpline.interpolate(lux);
@@ -660,6 +731,15 @@
         }
 
         @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) {
                 config = mDefaultConfig;
@@ -676,6 +756,11 @@
         }
 
         @Override
+        public BrightnessConfiguration getBrightnessConfiguration() {
+            return mConfig;
+        }
+
+        @Override
         public float getBrightness(float lux, String packageName,
                 @ApplicationInfo.Category int category) {
             float nits = mBrightnessSpline.interpolate(lux);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index e42545e..f1655f0 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 @@
                         + 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 @@
                         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 f6c4d3a..ca00116 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 @@
     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 @@
                 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 @@
         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<SensorEventListener> 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);
+    }
 }