Interpolate high light white balance

In very high lux conditions, add an option to force a
cooler white balance cct to maximize display brightness.

Test: atest AmbientLuxTest
Bug: 134417265
Change-Id: I3db16eaf66c38955e2b11db11c766b85b8a0b5b8
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1e7f516..c2592fa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4045,11 +4045,38 @@
     </array>
 
     <!-- See DisplayWhiteBalanceController.
-         The ambient color temperature (in cct) to which we fall back when the ambient brightness
-         drops beneath a certain threshold. -->
+         The ambient color temperature (in cct) to which we interpolate towards using the
+         the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnesses
+         and config_displayWhiteBalanceLowLightAmbientBiases. -->
     <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
 
     <!-- See DisplayWhiteBalanceController.
+         A float array containing a list of ambient brightnesses, in Lux. This array,
+         together with config_displayWhiteBalanceHighLightAmbientBiases, is used to generate a
+         lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+         ambient brightness readings to a bias, where the bias is used to linearly interpolate
+         between ambient color temperature and
+         config_displayWhiteBalanceHighLightAmbientColorTemperature.
+         This table is optional. If used, this array must,
+         1) Contain at least two entries
+         2) Be the same length as config_displayWhiteBalanceHighLightAmbientBiases. -->
+    <array name ="config_displayWhiteBalanceHighLightAmbientBrightnesses">
+    </array>
+
+    <!-- See DisplayWhiteBalanceController.
+         An array containing a list of biases. See
+         config_displayWhiteBalanceHighLightAmbientBrightnesses for additional details.
+         This array must be in the range of [0.0, 1.0]. -->
+    <array name ="config_displayWhiteBalanceHighLightAmbientBiases">
+    </array>
+
+    <!-- See DisplayWhiteBalanceController.
+         The ambient color temperature (in cct) to which we interpolate towards using the
+         the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnesses
+         and config_displayWhiteBalanceHighLightAmbientBiases. -->
+    <item name="config_displayWhiteBalanceHighLightAmbientColorTemperature" format="float" type="dimen">8000.0</item>
+
+    <!-- See DisplayWhiteBalanceController.
          A float array containing a list of ambient color temperatures, in Kelvin. This array,
          together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
          lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c0a5729..36d4bf2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3749,6 +3749,9 @@
   <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" />
   <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
+  <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnesses" />
+  <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiases" />
+  <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperature" />
   <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
   <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
   <java-symbol type="drawable" name="ic_action_open" />
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 395319d..dd3876d 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -64,6 +64,7 @@
     private DisplayWhiteBalanceThrottler mThrottler;
 
     private final float mLowLightAmbientColorTemperature;
+    private final float mHighLightAmbientColorTemperature;
 
     private float mAmbientColorTemperature;
 
@@ -83,9 +84,17 @@
     // A piecewise linear relationship between ambient and display color temperatures.
     private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
 
+    // In very low or very high brightness conditions ambient EQ should to set to a default
+    // instead of using mAmbientToDisplayColorTemperatureSpline. However, setting ambient EQ
+    // based on thresholds can cause the display to rapidly change color temperature. To solve
+    // this, mLowLightAmbientBrightnessToBiasSpline and mHighLightAmbientBrightnessToBiasSpline
+    // are used to smoothly interpolate from ambient color temperature to the defaults.
     // A piecewise linear relationship between low light brightness and low light bias.
     private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
 
+    // A piecewise linear relationship between high light brightness and high light bias.
+    private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+
     /**
      * @param brightnessSensor
      *      The sensor used to detect changes in the ambient brightness.
@@ -100,12 +109,22 @@
      * @param throttler
      *      The throttler used to determine whether the new display color temperature should be
      *      updated or not.
-     * @param lowLightAmbientBrightnessThreshold
-     *      The ambient brightness threshold beneath which we fall back to a fixed ambient color
-     *      temperature.
+     * @param lowLightAmbientBrightnesses
+     *      The ambient brightness used to map the ambient brightnesses to the biases used to
+     *      interpolate to lowLightAmbientColorTemperature.
+     * @param lowLightAmbientBiases
+     *      The biases used to map the ambient brightnesses to the biases used to interpolate to
+     *      lowLightAmbientColorTemperature.
      * @param lowLightAmbientColorTemperature
-     *      The ambient color temperature to which we fall back when the ambient brightness drops
-     *      beneath a certain threshold.
+     *      The ambient color temperature to which we interpolate to based on the low light curve.
+     * @param highLightAmbientBrightnesses
+     *      The ambient brightness used to map the ambient brightnesses to the biases used to
+     *      interpolate to highLightAmbientColorTemperature.
+     * @param highLightAmbientBiases
+     *      The biases used to map the ambient brightnesses to the biases used to interpolate to
+     *      highLightAmbientColorTemperature.
+     * @param highLightAmbientColorTemperature
+     *      The ambient color temperature to which we interpolate to based on the high light curve.
      * @param ambientColorTemperatures
      *      The ambient color tempeartures used to map the ambient color temperature to the display
      *      color temperature (or null if no mapping is necessary).
@@ -128,6 +147,8 @@
             @NonNull DisplayWhiteBalanceThrottler throttler,
             float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases,
             float lowLightAmbientColorTemperature,
+            float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases,
+            float highLightAmbientColorTemperature,
             float[] ambientColorTemperatures, float[] displayColorTemperatures) {
         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
                 colorTemperatureFilter, throttler);
@@ -140,6 +161,7 @@
         mColorTemperatureFilter = colorTemperatureFilter;
         mThrottler = throttler;
         mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+        mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
         mAmbientColorTemperature = -1.0f;
         mPendingAmbientColorTemperature = -1.0f;
         mLastAmbientColorTemperature = -1.0f;
@@ -158,12 +180,40 @@
                     mLowLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
                     != 1.0f) {
                 Slog.d(TAG, "invalid low light ambient brightness to bias spline, "
-                        + "bias must begin at 0.0 and end at 1.0");
+                        + "bias must begin at 0.0 and end at 1.0.");
                 mLowLightAmbientBrightnessToBiasSpline = null;
             }
         }
 
         try {
+            mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
+                    highLightAmbientBrightnesses, highLightAmbientBiases);
+        } catch (Exception e) {
+            Slog.e(TAG, "failed to create high light ambient brightness to bias spline.", e);
+            mHighLightAmbientBrightnessToBiasSpline = null;
+        }
+        if (mHighLightAmbientBrightnessToBiasSpline != null) {
+            if (mHighLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
+                    mHighLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
+                    != 1.0f) {
+                Slog.d(TAG, "invalid high light ambient brightness to bias spline, "
+                        + "bias must begin at 0.0 and end at 1.0.");
+                mHighLightAmbientBrightnessToBiasSpline = null;
+            }
+        }
+
+        if (mLowLightAmbientBrightnessToBiasSpline != null &&
+                mHighLightAmbientBrightnessToBiasSpline != null) {
+            if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
+                    highLightAmbientBrightnesses[0]) {
+                Slog.d(TAG, "invalid low light and high light ambient brightness to bias spline "
+                        + "combination, defined domains must not intersect.");
+                mLowLightAmbientBrightnessToBiasSpline = null;
+                mHighLightAmbientBrightnessToBiasSpline = null;
+            }
+        }
+
+        try {
             mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
                     ambientColorTemperatures, displayColorTemperatures);
         } catch (Exception e) {
@@ -264,6 +314,7 @@
         mColorTemperatureFilter.dump(writer);
         mThrottler.dump(writer);
         writer.println("  mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+        writer.println("  mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
         writer.println("  mAmbientColorTemperature=" + mAmbientColorTemperature);
         writer.println("  mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
@@ -273,6 +324,8 @@
                 + mAmbientToDisplayColorTemperatureSpline);
         writer.println("  mLowLightAmbientBrightnessToBiasSpline="
                 + mLowLightAmbientBrightnessToBiasSpline);
+        writer.println("  mHighLightAmbientBrightnessToBiasSpline="
+                + mHighLightAmbientBrightnessToBiasSpline);
     }
 
     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -309,6 +362,12 @@
                     bias * ambientColorTemperature + (1.0f - bias)
                     * mLowLightAmbientColorTemperature;
         }
+        if (mHighLightAmbientBrightnessToBiasSpline != null) {
+            float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+            ambientColorTemperature =
+                    (1.0f - bias) * ambientColorTemperature + bias
+                    * mHighLightAmbientColorTemperature;
+        }
 
         if (mAmbientColorTemperatureOverride != -1.0f) {
             if (mLoggingEnabled) {
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index b1b465e..4df7d6b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -72,6 +72,15 @@
         final float lowLightAmbientColorTemperature = getFloat(resources,
                 com.android.internal.R.dimen
                 .config_displayWhiteBalanceLowLightAmbientColorTemperature);
+        final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources,
+                com.android.internal.R.array
+                .config_displayWhiteBalanceHighLightAmbientBrightnesses);
+        final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources,
+                com.android.internal.R.array
+                .config_displayWhiteBalanceHighLightAmbientBiases);
+        final float highLightAmbientColorTemperature = getFloat(resources,
+                com.android.internal.R.dimen
+                .config_displayWhiteBalanceHighLightAmbientColorTemperature);
         final float[] ambientColorTemperatures = getFloatArray(resources,
                 com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
         final float[] displayColorTempeartures = getFloatArray(resources,
@@ -80,6 +89,8 @@
                 brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
                 throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
                 displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
+                displayWhiteBalanceHighLightAmbientBrightnesses,
+                displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
                 ambientColorTemperatures, displayColorTempeartures);
         brightnessSensor.setCallbacks(controller);
         colorTemperatureSensor.setCallbacks(controller);
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index de68036..424142c 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -23,7 +23,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import org.mockito.stubbing.Answer;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -56,7 +61,8 @@
 public final class AmbientLuxTest {
     private static final int AMBIENT_COLOR_TYPE = 20705;
     private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
-    private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5700;
+    private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
+    private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f;
 
     private Handler mHandler = new Handler(Looper.getMainLooper());
     private Sensor mLightSensor;
@@ -68,6 +74,8 @@
 
     @Mock private TypedArray mBrightnesses;
     @Mock private TypedArray mBiases;
+    @Mock private TypedArray mHighLightBrightnesses;
+    @Mock private TypedArray mHighLightBiases;
 
     @Before
     public void setUp() throws Exception {
@@ -89,9 +97,10 @@
         when(mResourcesSpy.getInteger(
                 R.integer.config_displayWhiteBalanceIncreaseDebounce))
                 .thenReturn(0);
-        when(mResourcesSpy.getFloat(
-                R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature))
-                .thenReturn(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+        mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature,
+                LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+        mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature,
+                HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
         when(mResourcesSpy.obtainTypedArray(
                 R.array.config_displayWhiteBalanceAmbientColorTemperatures))
                 .thenReturn(createTypedArray());
@@ -105,6 +114,13 @@
         when(mResourcesSpy.obtainTypedArray(
                 R.array.config_displayWhiteBalanceLowLightAmbientBiases))
                 .thenReturn(mBiases);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceHighLightAmbientBrightnesses))
+                .thenReturn(mHighLightBrightnesses);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceHighLightAmbientBiases))
+                .thenReturn(mHighLightBiases);
+        mockThrottler();
     }
 
     @Test
@@ -216,16 +232,119 @@
     }
 
     @Test
-    public void testSpline_InvalidBiases() throws Exception {
-        float[][] invalidBrightnesses =
-                {{10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f},
-                 {10.0f, 1000.0f}, {-1.0f, 1.0f}, {-1.0f, 1.0f}};
-        float[][] invalidBiases =
-                {{0.0f, 2.0f}, {0.0f, 0.9f}, {0.1f, 1.0f}, {-0.1f, 1.0f},
-                 {0.1f, 1.1f}, {0.0f, 1.0f}, {-2.0f, 1.0f}};
-        for (int i = 0; i < invalidBrightnesses.length; ++i) {
-            setBrightnesses(invalidBrightnesses[i][0], invalidBrightnesses[i][1]);
-            setBiases(invalidBiases[i][0], invalidBiases[i][1]);
+    public void testSpline_InvalidEndBias() throws Exception {
+        setBrightnesses(10.0f, 1000.0f);
+        setBiases(0.0f, 2.0f);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 8000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+        setEstimatedBrightnessAndUpdate(controller, luxOverride);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                ambientColorTemperature, 0.001);
+        }
+    }
+
+    @Test
+    public void testSpline_InvalidBeginBias() throws Exception {
+        setBrightnesses(10.0f, 1000.0f);
+        setBiases(0.1f, 1.0f);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 8000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+        setEstimatedBrightnessAndUpdate(controller, luxOverride);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                ambientColorTemperature, 0.001);
+        }
+    }
+
+    @Test
+    public void testSpline_OneSegmentHighLight() throws Exception {
+        final float lowerBrightness = 10.0f;
+        final float upperBrightness = 50.0f;
+        setHighLightBrightnesses(lowerBrightness, upperBrightness);
+        setHighLightBiases(0.0f, 1.0f);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 8000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+            setEstimatedBrightnessAndUpdate(controller,
+                    mix(lowerBrightness, upperBrightness, t));
+            assertEquals(controller.mPendingAmbientColorTemperature,
+                    mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - t),
+                    0.001);
+        }
+
+        setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
+
+        setEstimatedBrightnessAndUpdate(controller, 0.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
+    }
+
+    @Test
+    public void testSpline_TwoSegmentsHighLight() throws Exception {
+        final float brightness0 = 10.0f;
+        final float brightness1 = 50.0f;
+        final float brightness2 = 60.0f;
+        setHighLightBrightnesses(brightness0, brightness1, brightness2);
+        final float bias0 = 0.0f;
+        final float bias1 = 0.25f;
+        final float bias2 = 1.0f;
+        setHighLightBiases(bias0, bias1, bias2);
+
+        DisplayWhiteBalanceController controller =
+                DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+        final float ambientColorTemperature = 6000.0f;
+        setEstimatedColorTemperature(controller, ambientColorTemperature);
+        controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+
+        for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+            float luxOverride = mix(brightness0, brightness1, t);
+            setEstimatedBrightnessAndUpdate(controller, luxOverride);
+            float bias = mix(bias0, bias1, t);
+            assertEquals(controller.mPendingAmbientColorTemperature,
+                    mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
+                    0.01);
+        }
+
+        for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
+            float luxOverride = mix(brightness1, brightness2, t);
+            setEstimatedBrightnessAndUpdate(controller, luxOverride);
+            float bias = mix(bias1, bias2, t);
+            assertEquals(controller.mPendingAmbientColorTemperature,
+                    mix(HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, 1.0f - bias),
+                    0.01);
+        }
+
+        setEstimatedBrightnessAndUpdate(controller, brightness2 + 1.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature,
+                HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001);
+
+        setEstimatedBrightnessAndUpdate(controller, 0.0f);
+        assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
+    }
+
+    @Test
+    public void testSpline_InvalidCombinations() throws Exception {
+            setBrightnesses(100.0f, 200.0f);
+            setBiases(0.0f, 1.0f);
+            setHighLightBrightnesses(150.0f, 250.0f);
+            setHighLightBiases(0.0f, 1.0f);
 
             DisplayWhiteBalanceController controller =
                     DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
@@ -238,7 +357,44 @@
                 assertEquals(controller.mPendingAmbientColorTemperature,
                         ambientColorTemperature, 0.001);
             }
-        }
+    }
+
+    void mockThrottler() {
+        when(mResourcesSpy.getInteger(
+                R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
+        when(mResourcesSpy.getInteger(
+                R.integer.config_displayWhiteBalanceIncreaseDebounce)).thenReturn(0);
+        TypedArray base = mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceBaseThresholds);
+        TypedArray inc = mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceIncreaseThresholds);
+        TypedArray dec = mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceDecreaseThresholds);
+        base = spy(base);
+        inc = spy(inc);
+        dec = spy(dec);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceBaseThresholds)).thenReturn(base);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceIncreaseThresholds)).thenReturn(inc);
+        when(mResourcesSpy.obtainTypedArray(
+                R.array.config_displayWhiteBalanceDecreaseThresholds)).thenReturn(dec);
+        setFloatArrayResource(base, new float[]{0.0f});
+        setFloatArrayResource(inc, new float[]{0.0f});
+        setFloatArrayResource(dec, new float[]{0.0f});
+    }
+
+    private void mockResourcesFloat(int id, float floatValue) {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                TypedValue value = (TypedValue)invocation.getArgument(1);
+                value.type = TypedValue.TYPE_FLOAT;
+                value.data = Float.floatToIntBits(floatValue);
+                return null;
+            }
+        }).when(mResourcesSpy).getValue(
+                eq(id),
+                any(TypedValue.class), eq(true));
     }
 
     private void setEstimatedColorTemperature(DisplayWhiteBalanceController controller,
@@ -262,6 +418,14 @@
         setFloatArrayResource(mBiases, vals);
     }
 
+    private void setHighLightBrightnesses(float... vals) {
+        setFloatArrayResource(mHighLightBrightnesses, vals);
+    }
+
+    private void setHighLightBiases(float... vals) {
+        setFloatArrayResource(mHighLightBiases, vals);
+    }
+
     private void setFloatArrayResource(TypedArray array, float[] vals) {
         when(array.length()).thenReturn(vals.length);
         for (int i = 0; i < vals.length; i++) {