1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display.whitebalance;
18 
19 import android.annotation.NonNull;
20 import android.util.Slog;
21 import android.util.Spline;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 import com.android.server.LocalServices;
25 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
26 import com.android.server.display.utils.AmbientFilter;
27 import com.android.server.display.utils.History;
28 
29 import java.io.PrintWriter;
30 import java.util.Objects;
31 
32 /**
33  * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
34  * display color temperature depending on the ambient color temperature).
35  *
36  * The DisplayWhiteBalanceController:
37  * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
38  * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
39  *   noise, and arrive at an estimate of the actual ambient color temperature;
40  * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color temperature should
41  *   be updated, suppressing changes that are too frequent or too minor.
42  *
43  *   Calls to this class must happen on the DisplayPowerController(2) handler, to ensure
44  *   values do not get out of sync.
45  */
46 public class DisplayWhiteBalanceController implements
47         AmbientSensor.AmbientBrightnessSensor.Callbacks,
48         AmbientSensor.AmbientColorTemperatureSensor.Callbacks {
49 
50     private static final String TAG = "DisplayWhiteBalanceController";
51     private boolean mLoggingEnabled;
52 
53     private final ColorDisplayServiceInternal mColorDisplayServiceInternal;
54 
55     private final AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
56     @VisibleForTesting
57     AmbientFilter mBrightnessFilter;
58     private final AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
59     @VisibleForTesting
60     AmbientFilter mColorTemperatureFilter;
61     private final DisplayWhiteBalanceThrottler mThrottler;
62     // In low brightness conditions the ALS readings are more noisy and produce
63     // high errors. This default is introduced to provide a fixed display color
64     // temperature when sensor readings become unreliable.
65     private final float mLowLightAmbientColorTemperature;
66     // As above, but used when in strong mode (idle screen brightness mode).
67     private final float mLowLightAmbientColorTemperatureStrong;
68 
69     // In high brightness conditions certain color temperatures can cause peak display
70     // brightness to drop. This fixed color temperature can be used to compensate for
71     // this effect.
72     private final float mHighLightAmbientColorTemperature;
73     // As above, but used when in strong mode (idle screen brightness mode).
74     private final float mHighLightAmbientColorTemperatureStrong;
75 
76     private final boolean mLightModeAllowed;
77 
78     private float mAmbientColorTemperature;
79     @VisibleForTesting
80     float mPendingAmbientColorTemperature;
81     private float mLastAmbientColorTemperature;
82 
83     // The most recent ambient color temperature values are kept for debugging purposes.
84     private final History mAmbientColorTemperatureHistory;
85 
86     // Override the ambient color temperature for debugging purposes.
87     private float mAmbientColorTemperatureOverride;
88 
89     // A piecewise linear relationship between ambient and display color temperatures.
90     private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
91 
92     // A piecewise linear relationship between ambient and display color temperatures, with a
93     // stronger change between the two sets of values.
94     private Spline.LinearSpline mStrongAmbientToDisplayColorTemperatureSpline;
95 
96     // In very low or very high brightness conditions Display White Balance should
97     // be to set to a default instead of using mAmbientToDisplayColorTemperatureSpline.
98     // However, setting Display White Balance based on thresholds can cause the
99     // display to rapidly change color temperature. To solve this,
100     // mLowLightAmbientBrightnessToBiasSpline and
101     // mHighLightAmbientBrightnessToBiasSpline are used to smoothly interpolate from
102     // ambient color temperature to the defaults. A piecewise linear relationship
103     // between low light brightness and low light bias.
104     private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
105     private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSplineStrong;
106 
107     // A piecewise linear relationship between high light brightness and high light bias.
108     private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
109     private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSplineStrong;
110 
111     private float mLatestAmbientColorTemperature;
112     private float mLatestAmbientBrightness;
113     private float mLatestLowLightBias;
114     private float mLatestHighLightBias;
115 
116     private boolean mEnabled;
117 
118     // Whether a higher-strength adjustment should be applied; this must be enabled in addition to
119     // mEnabled in order to be applied.
120     private boolean mStrongModeEnabled;
121 
122     // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
123     // implements Callbacks and passes itself to the DWBC so it can call back into it without
124     // knowing about it.
125     private Callbacks mDisplayPowerControllerCallbacks;
126 
127     /**
128      * @param brightnessSensor
129      *      The sensor used to detect changes in the ambient brightness.
130      * @param brightnessFilter
131      *      The filter used to avergae ambient brightness changes over time, filter out the noise
132      *      and arrive at an estimate of the actual ambient brightness.
133      * @param colorTemperatureSensor
134      *      The sensor used to detect changes in the ambient color temperature.
135      * @param colorTemperatureFilter
136      *      The filter used to average ambient color temperature changes over time, filter out the
137      *      noise and arrive at an estimate of the actual ambient color temperature.
138      * @param throttler
139      *      The throttler used to determine whether the new display color temperature should be
140      *      updated or not.
141      * @param lowLightAmbientBrightnesses
142      *      The ambient brightness used to map the ambient brightnesses to the biases used to
143      *      interpolate to lowLightAmbientColorTemperature.
144      * @param lowLightAmbientBrightnessesStrong
145      *      The ambient brightness used to map the ambient brightnesses to the biases used to
146      *      interpolate to lowLightAmbientColorTemperature.
147      * @param lowLightAmbientBiases
148      *      The biases used to map the ambient brightnesses to the biases used to interpolate to
149      *      lowLightAmbientColorTemperature.
150      * @param lowLightAmbientBiasesStrong
151      *      The biases used to map the ambient brightnesses to the biases used to interpolate to
152      *      lowLightAmbientColorTemperature.
153      * @param lowLightAmbientColorTemperature
154      *      The ambient color temperature to which we interpolate to based on the low light curve.
155      * @param highLightAmbientBrightnesses
156      *      The ambient brightness used to map the ambient brightnesses to the biases used to
157      *      interpolate to highLightAmbientColorTemperature.
158      * @param highLightAmbientBrightnessesStrong
159      *      The ambient brightness used to map the ambient brightnesses to the biases used to
160      *      interpolate to highLightAmbientColorTemperature.
161      * @param highLightAmbientBiases
162      *      The biases used to map the ambient brightnesses to the biases used to interpolate to
163      *      highLightAmbientColorTemperature.
164      * @param highLightAmbientBiasesStrong
165      *      The biases used to map the ambient brightnesses to the biases used to interpolate to
166      *      highLightAmbientColorTemperature.
167      * @param highLightAmbientColorTemperature
168      *      The ambient color temperature to which we interpolate to based on the high light curve.
169      * @param ambientColorTemperatures
170      *      The ambient color tempeartures used to map the ambient color temperature to the display
171      *      color temperature (or null if no mapping is necessary).
172      * @param displayColorTemperatures
173      *      The display color temperatures used to map the ambient color temperature to the display
174      *      color temperature (or null if no mapping is necessary).
175      * @param lightModeAllowed
176      *      Whether a lighter version should be applied when Strong Mode is not enabled.
177      *
178      * @throws NullPointerException
179      *      - brightnessSensor is null;
180      *      - brightnessFilter is null;
181      *      - colorTemperatureSensor is null;
182      *      - colorTemperatureFilter is null;
183      *      - throttler is null.
184      */
DisplayWhiteBalanceController( @onNull AmbientSensor.AmbientBrightnessSensor brightnessSensor, @NonNull AmbientFilter brightnessFilter, @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBrightnessesStrong, float[] lowLightAmbientBiases, float[] lowLightAmbientBiasesStrong, float lowLightAmbientColorTemperature, float lowLightAmbientColorTemperatureStrong, float[] highLightAmbientBrightnesses, float[] highLightAmbientBrightnessesStrong, float[] highLightAmbientBiases, float[] highLightAmbientBiasesStrong, float highLightAmbientColorTemperature, float highLightAmbientColorTemperatureStrong, float[] ambientColorTemperatures, float[] displayColorTemperatures, float[] strongAmbientColorTemperatures, float[] strongDisplayColorTemperatures, boolean lightModeAllowed)185     public DisplayWhiteBalanceController(
186             @NonNull AmbientSensor.AmbientBrightnessSensor brightnessSensor,
187             @NonNull AmbientFilter brightnessFilter,
188             @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
189             @NonNull AmbientFilter colorTemperatureFilter,
190             @NonNull DisplayWhiteBalanceThrottler throttler,
191             float[] lowLightAmbientBrightnesses,
192             float[] lowLightAmbientBrightnessesStrong,
193             float[] lowLightAmbientBiases,
194             float[] lowLightAmbientBiasesStrong,
195             float lowLightAmbientColorTemperature,
196             float lowLightAmbientColorTemperatureStrong,
197             float[] highLightAmbientBrightnesses,
198             float[] highLightAmbientBrightnessesStrong,
199             float[] highLightAmbientBiases,
200             float[] highLightAmbientBiasesStrong,
201             float highLightAmbientColorTemperature,
202             float highLightAmbientColorTemperatureStrong,
203             float[] ambientColorTemperatures,
204             float[] displayColorTemperatures,
205             float[] strongAmbientColorTemperatures,
206             float[] strongDisplayColorTemperatures,
207             boolean lightModeAllowed) {
208         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
209                 colorTemperatureFilter, throttler);
210         mBrightnessSensor = brightnessSensor;
211         mBrightnessFilter = brightnessFilter;
212         mColorTemperatureSensor = colorTemperatureSensor;
213         mColorTemperatureFilter = colorTemperatureFilter;
214         mThrottler = throttler;
215         mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
216         mLowLightAmbientColorTemperatureStrong = lowLightAmbientColorTemperatureStrong;
217         mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
218         mHighLightAmbientColorTemperatureStrong = highLightAmbientColorTemperatureStrong;
219         mAmbientColorTemperature = -1.0f;
220         mPendingAmbientColorTemperature = -1.0f;
221         mLastAmbientColorTemperature = -1.0f;
222         mAmbientColorTemperatureHistory = new History(/* size= */ 50);
223         mAmbientColorTemperatureOverride = -1.0f;
224         mLightModeAllowed = lightModeAllowed;
225 
226         try {
227             mLowLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
228                     lowLightAmbientBrightnesses, lowLightAmbientBiases);
229         } catch (Exception e) {
230             Slog.e(TAG, "failed to create low light ambient brightness to bias spline.", e);
231             mLowLightAmbientBrightnessToBiasSpline = null;
232         }
233         if (mLowLightAmbientBrightnessToBiasSpline != null) {
234             if (mLowLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
235                     mLowLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
236                     != 1.0f) {
237                 Slog.d(TAG, "invalid low light ambient brightness to bias spline, "
238                         + "bias must begin at 0.0 and end at 1.0.");
239                 mLowLightAmbientBrightnessToBiasSpline = null;
240             }
241         }
242 
243         try {
244             mLowLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline(
245                     lowLightAmbientBrightnessesStrong, lowLightAmbientBiasesStrong);
246         } catch (Exception e) {
247             Slog.e(TAG, "failed to create strong low light ambient brightness to bias spline.", e);
248             mLowLightAmbientBrightnessToBiasSplineStrong = null;
249         }
250         if (mLowLightAmbientBrightnessToBiasSplineStrong != null) {
251             if (mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f
252                     || mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(
253                     Float.POSITIVE_INFINITY) != 1.0f) {
254                 Slog.d(TAG, "invalid strong low light ambient brightness to bias spline, "
255                         + "bias must begin at 0.0 and end at 1.0.");
256                 mLowLightAmbientBrightnessToBiasSplineStrong = null;
257             }
258         }
259 
260         try {
261             mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
262                     highLightAmbientBrightnesses, highLightAmbientBiases);
263         } catch (Exception e) {
264             Slog.e(TAG, "failed to create high light ambient brightness to bias spline.", e);
265             mHighLightAmbientBrightnessToBiasSpline = null;
266         }
267         if (mHighLightAmbientBrightnessToBiasSpline != null) {
268             if (mHighLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
269                     mHighLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
270                     != 1.0f) {
271                 Slog.d(TAG, "invalid high light ambient brightness to bias spline, "
272                         + "bias must begin at 0.0 and end at 1.0.");
273                 mHighLightAmbientBrightnessToBiasSpline = null;
274             }
275         }
276 
277         try {
278             mHighLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline(
279                     highLightAmbientBrightnessesStrong, highLightAmbientBiasesStrong);
280         } catch (Exception e) {
281             Slog.e(TAG, "failed to create strong high light ambient brightness to bias spline.", e);
282             mHighLightAmbientBrightnessToBiasSplineStrong = null;
283         }
284         if (mHighLightAmbientBrightnessToBiasSplineStrong != null) {
285             if (mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f
286                     || mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(
287                     Float.POSITIVE_INFINITY) != 1.0f) {
288                 Slog.d(TAG, "invalid strong high light ambient brightness to bias spline, "
289                         + "bias must begin at 0.0 and end at 1.0.");
290                 mHighLightAmbientBrightnessToBiasSplineStrong = null;
291             }
292         }
293 
294         if (mLowLightAmbientBrightnessToBiasSpline != null &&
295                 mHighLightAmbientBrightnessToBiasSpline != null) {
296             if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
297                     highLightAmbientBrightnesses[0]) {
298                 Slog.d(TAG, "invalid low light and high light ambient brightness to bias spline "
299                         + "combination, defined domains must not intersect.");
300                 mLowLightAmbientBrightnessToBiasSpline = null;
301                 mHighLightAmbientBrightnessToBiasSpline = null;
302             }
303         }
304 
305         if (mLowLightAmbientBrightnessToBiasSplineStrong != null
306                 && mHighLightAmbientBrightnessToBiasSplineStrong != null) {
307             if (lowLightAmbientBrightnessesStrong[lowLightAmbientBrightnessesStrong.length - 1]
308                     > highLightAmbientBrightnessesStrong[0]) {
309                 Slog.d(TAG,
310                         "invalid strong low light and high light ambient brightness to bias "
311                                 + "spline combination, defined domains must not intersect.");
312                 mLowLightAmbientBrightnessToBiasSplineStrong = null;
313                 mHighLightAmbientBrightnessToBiasSplineStrong = null;
314             }
315         }
316 
317         try {
318             mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
319                     ambientColorTemperatures, displayColorTemperatures);
320         } catch (Exception e) {
321             Slog.e(TAG, "failed to create ambient to display color temperature spline.", e);
322             mAmbientToDisplayColorTemperatureSpline = null;
323         }
324 
325         try {
326             mStrongAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
327                     strongAmbientColorTemperatures, strongDisplayColorTemperatures);
328         } catch (Exception e) {
329             Slog.e(TAG, "Failed to create strong ambient to display color temperature spline", e);
330         }
331 
332         mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
333     }
334 
335     /**
336      * Enable/disable the controller.
337      *
338      * @param enabled
339      *      Whether the controller should be on/off.
340      *
341      * @return Whether the method succeeded or not.
342      */
setEnabled(boolean enabled)343     public boolean setEnabled(boolean enabled) {
344         if (enabled) {
345             return enable();
346         } else {
347             return disable();
348         }
349     }
350 
351     /**
352      * Enable/disable the stronger adjustment option.
353      *
354      * @param enabled whether the stronger adjustment option should be turned on
355      */
setStrongModeEnabled(boolean enabled)356     public void setStrongModeEnabled(boolean enabled) {
357         mStrongModeEnabled = enabled;
358         mColorDisplayServiceInternal.setDisplayWhiteBalanceAllowed(mLightModeAllowed
359                 || mStrongModeEnabled);
360         if (mEnabled) {
361             updateAmbientColorTemperature();
362             updateDisplayColorTemperature();
363         }
364     }
365 
366     /**
367      * Set an object to call back to when the display color temperature should be updated.
368      *
369      * @param callbacks
370      *      The object to call back to.
371      *
372      * @return Whether the method succeeded or not.
373      */
setCallbacks(Callbacks callbacks)374     public boolean setCallbacks(Callbacks callbacks) {
375         if (mDisplayPowerControllerCallbacks == callbacks) {
376             return false;
377         }
378         mDisplayPowerControllerCallbacks = callbacks;
379         return true;
380     }
381 
382     /**
383      * Enable/disable logging.
384      *
385      * @param loggingEnabled
386      *      Whether logging should be on/off.
387      *
388      * @return Whether the method succeeded or not.
389      */
setLoggingEnabled(boolean loggingEnabled)390     public boolean setLoggingEnabled(boolean loggingEnabled) {
391         if (mLoggingEnabled == loggingEnabled) {
392             return false;
393         }
394         mLoggingEnabled = loggingEnabled;
395         mBrightnessSensor.setLoggingEnabled(loggingEnabled);
396         mBrightnessFilter.setLoggingEnabled(loggingEnabled);
397         mColorTemperatureSensor.setLoggingEnabled(loggingEnabled);
398         mColorTemperatureFilter.setLoggingEnabled(loggingEnabled);
399         mThrottler.setLoggingEnabled(loggingEnabled);
400         return true;
401     }
402 
403     /**
404      * Set the ambient color temperature override.
405      *
406      * This is only applied when the ambient color temperature changes or is updated (in which case
407      * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
408      * change the display color temperature immediately.
409      *
410      * @param ambientColorTemperatureOverride
411      *      The ambient color temperature override.
412      *
413      * @return Whether the method succeeded or not.
414      */
setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride)415     public boolean setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride) {
416         if (mAmbientColorTemperatureOverride == ambientColorTemperatureOverride) {
417             return false;
418         }
419         mAmbientColorTemperatureOverride = ambientColorTemperatureOverride;
420         return true;
421     }
422 
423     /**
424      * Dump the state.
425      *
426      * @param writer
427      *      The writer used to dump the state.
428      */
dump(PrintWriter writer)429     public void dump(PrintWriter writer) {
430         writer.println("DisplayWhiteBalanceController");
431         writer.println("  mLoggingEnabled=" + mLoggingEnabled);
432         writer.println("  mEnabled=" + mEnabled);
433         writer.println("  mStrongModeEnabled=" + mStrongModeEnabled);
434         writer.println("  mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks);
435         mBrightnessSensor.dump(writer);
436         mBrightnessFilter.dump(writer);
437         mColorTemperatureSensor.dump(writer);
438         mColorTemperatureFilter.dump(writer);
439         mThrottler.dump(writer);
440         writer.println("  mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
441         writer.println("  mLowLightAmbientColorTemperatureStrong="
442                 + mLowLightAmbientColorTemperatureStrong);
443         writer.println("  mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
444         writer.println("  mHighLightAmbientColorTemperatureStrong="
445                 + mHighLightAmbientColorTemperatureStrong);
446         writer.println("  mAmbientColorTemperature=" + mAmbientColorTemperature);
447         writer.println("  mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
448         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
449         writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
450         writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
451         writer.println("  mAmbientToDisplayColorTemperatureSpline="
452                 + mAmbientToDisplayColorTemperatureSpline);
453         writer.println("  mStrongAmbientToDisplayColorTemperatureSpline="
454                 + mStrongAmbientToDisplayColorTemperatureSpline);
455         writer.println("  mLowLightAmbientBrightnessToBiasSpline="
456                 + mLowLightAmbientBrightnessToBiasSpline);
457         writer.println("  mLowLightAmbientBrightnessToBiasSplineStrong="
458                 + mLowLightAmbientBrightnessToBiasSplineStrong);
459         writer.println("  mHighLightAmbientBrightnessToBiasSpline="
460                 + mHighLightAmbientBrightnessToBiasSpline);
461         writer.println("  mHighLightAmbientBrightnessToBiasSplineStrong="
462                 + mHighLightAmbientBrightnessToBiasSplineStrong);
463     }
464 
465     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
onAmbientBrightnessChanged(float value)466     public void onAmbientBrightnessChanged(float value) {
467         final long time = System.currentTimeMillis();
468         mBrightnessFilter.addValue(time, value);
469         updateAmbientColorTemperature();
470     }
471 
472     @Override // AmbientSensor.AmbientColorTemperatureSensor.Callbacks
onAmbientColorTemperatureChanged(float value)473     public void onAmbientColorTemperatureChanged(float value) {
474         final long time = System.currentTimeMillis();
475         mColorTemperatureFilter.addValue(time, value);
476         updateAmbientColorTemperature();
477     }
478 
479     /**
480      * Updates the ambient color temperature.
481      */
updateAmbientColorTemperature()482     public void updateAmbientColorTemperature() {
483         final long time = System.currentTimeMillis();
484         final float lowLightAmbientColorTemperature = mStrongModeEnabled
485                 ? mLowLightAmbientColorTemperatureStrong : mLowLightAmbientColorTemperature;
486         final float highLightAmbientColorTemperature = mStrongModeEnabled
487                 ? mHighLightAmbientColorTemperatureStrong : mHighLightAmbientColorTemperature;
488         final Spline.LinearSpline lowLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
489                 ? mLowLightAmbientBrightnessToBiasSplineStrong
490                 : mLowLightAmbientBrightnessToBiasSpline;
491         final Spline.LinearSpline highLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
492                 ? mHighLightAmbientBrightnessToBiasSplineStrong
493                 : mHighLightAmbientBrightnessToBiasSpline;
494 
495         float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
496         mLatestAmbientColorTemperature = ambientColorTemperature;
497 
498         if (mStrongModeEnabled) {
499             if (mStrongAmbientToDisplayColorTemperatureSpline != null
500                     && ambientColorTemperature != -1.0f) {
501                 ambientColorTemperature =
502                         mStrongAmbientToDisplayColorTemperatureSpline.interpolate(
503                                 ambientColorTemperature);
504             }
505         } else {
506             if (mAmbientToDisplayColorTemperatureSpline != null
507                     && ambientColorTemperature != -1.0f) {
508                 ambientColorTemperature =
509                         mAmbientToDisplayColorTemperatureSpline.interpolate(
510                                 ambientColorTemperature);
511             }
512         }
513 
514         float ambientBrightness = mBrightnessFilter.getEstimate(time);
515         mLatestAmbientBrightness = ambientBrightness;
516 
517         if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
518                 && lowLightAmbientBrightnessToBiasSpline != null) {
519             float bias = lowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
520             ambientColorTemperature =
521                     bias * ambientColorTemperature + (1.0f - bias)
522                     * lowLightAmbientColorTemperature;
523             mLatestLowLightBias = bias;
524         }
525         if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
526                 && highLightAmbientBrightnessToBiasSpline != null) {
527             float bias = highLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
528             ambientColorTemperature =
529                     (1.0f - bias) * ambientColorTemperature + bias
530                     * highLightAmbientColorTemperature;
531             mLatestHighLightBias = bias;
532         }
533 
534         if (mAmbientColorTemperatureOverride != -1.0f) {
535             if (mLoggingEnabled) {
536                 Slog.d(TAG, "override ambient color temperature: " + ambientColorTemperature
537                         + " => " + mAmbientColorTemperatureOverride);
538             }
539             ambientColorTemperature = mAmbientColorTemperatureOverride;
540         }
541 
542         // When the display color temperature needs to be updated, we call DisplayPowerController to
543         // call our updateColorTemperature. The reason we don't call it directly is that we want
544         // all changes to the system to happen in a predictable order in DPC's main loop
545         // (updatePowerState).
546         if (ambientColorTemperature == -1.0f || mThrottler.throttle(ambientColorTemperature)) {
547             return;
548         }
549 
550         if (mLoggingEnabled) {
551             Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
552         }
553         mPendingAmbientColorTemperature = ambientColorTemperature;
554         if (mDisplayPowerControllerCallbacks != null) {
555             mDisplayPowerControllerCallbacks.updateWhiteBalance();
556         }
557     }
558 
559     /**
560      * Updates the display color temperature.
561      */
updateDisplayColorTemperature()562     public void updateDisplayColorTemperature() {
563         float ambientColorTemperature = -1.0f;
564 
565         // If both the pending and the current ambient color temperatures are -1, it means the DWBC
566         // was just enabled, and we use the last ambient color temperature until new sensor events
567         // give us a better estimate.
568         if (mAmbientColorTemperature == -1.0f && mPendingAmbientColorTemperature == -1.0f) {
569             ambientColorTemperature = mLastAmbientColorTemperature;
570         }
571 
572         // Otherwise, we use the pending ambient color temperature, but only if it's non-trivial
573         // and different than the current one.
574         if (mPendingAmbientColorTemperature != -1.0f
575                 && mPendingAmbientColorTemperature != mAmbientColorTemperature) {
576             ambientColorTemperature = mPendingAmbientColorTemperature;
577         }
578 
579         if (ambientColorTemperature == -1.0f) {
580             return;
581         }
582 
583         mAmbientColorTemperature = ambientColorTemperature;
584         if (mLoggingEnabled) {
585             Slog.d(TAG, "ambient color temperature: " + mAmbientColorTemperature);
586         }
587         mPendingAmbientColorTemperature = -1.0f;
588         mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
589         Slog.d(TAG, "Display cct: " + mAmbientColorTemperature
590                 + " Latest ambient cct: " + mLatestAmbientColorTemperature
591                 + " Latest ambient lux: " + mLatestAmbientBrightness
592                 + " Latest low light bias: " + mLatestLowLightBias
593                 + " Latest high light bias: " + mLatestHighLightBias);
594         mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
595                 (int) mAmbientColorTemperature);
596         mLastAmbientColorTemperature = mAmbientColorTemperature;
597     }
598 
599     /**
600      * Calculate the adjusted brightness, in nits, due to the DWB color adaptation
601      *
602      * @param requestedBrightnessNits brightness the framework requires to be output
603      * @return the adjusted brightness the framework needs to output to counter the drop in
604      *         brightness due to DWB, or the requestedBrightnessNits if an adjustment cannot be made
605      */
calculateAdjustedBrightnessNits(float requestedBrightnessNits)606     public float calculateAdjustedBrightnessNits(float requestedBrightnessNits) {
607         float luminance = mColorDisplayServiceInternal.getDisplayWhiteBalanceLuminance();
608         if (luminance == -1) {
609             return requestedBrightnessNits;
610         }
611         float effectiveBrightness = requestedBrightnessNits * luminance;
612         return (requestedBrightnessNits - effectiveBrightness) + requestedBrightnessNits;
613     }
614 
615     /**
616      * The DisplayWhiteBalanceController decouples itself from its parent (DisplayPowerController)
617      * by providing this interface to implement (and a method to set its callbacks object), and
618      * calling these methods.
619      */
620     public interface Callbacks {
621 
622         /**
623          * Called whenever the display white-balance state has changed.
624          *
625          * Usually, this means the estimated ambient color temperature has changed enough, and the
626          * display color temperature should be updated; but it is also called if settings change.
627          */
updateWhiteBalance()628         void updateWhiteBalance();
629     }
630 
validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor, AmbientFilter brightnessFilter, AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, AmbientFilter colorTemperatureFilter, DisplayWhiteBalanceThrottler throttler)631     private void validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor,
632             AmbientFilter brightnessFilter,
633             AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
634             AmbientFilter colorTemperatureFilter,
635             DisplayWhiteBalanceThrottler throttler) {
636         Objects.requireNonNull(brightnessSensor, "brightnessSensor must not be null");
637         Objects.requireNonNull(brightnessFilter, "brightnessFilter must not be null");
638         Objects.requireNonNull(colorTemperatureSensor,
639                 "colorTemperatureSensor must not be null");
640         Objects.requireNonNull(colorTemperatureFilter,
641                 "colorTemperatureFilter must not be null");
642         Objects.requireNonNull(throttler, "throttler cannot be null");
643     }
644 
enable()645     private boolean enable() {
646         if (mEnabled) {
647             return false;
648         }
649         if (mLoggingEnabled) {
650             Slog.d(TAG, "enabling");
651         }
652         mEnabled = true;
653         mBrightnessSensor.setEnabled(true);
654         mColorTemperatureSensor.setEnabled(true);
655         return true;
656     }
657 
disable()658     private boolean disable() {
659         if (!mEnabled) {
660             return false;
661         }
662         if (mLoggingEnabled) {
663             Slog.d(TAG, "disabling");
664         }
665         mEnabled = false;
666         mBrightnessSensor.setEnabled(false);
667         mBrightnessFilter.clear();
668         mColorTemperatureSensor.setEnabled(false);
669         mColorTemperatureFilter.clear();
670         mThrottler.clear();
671         mAmbientColorTemperature = -1.0f;
672         mPendingAmbientColorTemperature = -1.0f;
673         mColorDisplayServiceInternal.resetDisplayWhiteBalanceColorTemperature();
674         return true;
675     }
676 
677 }
678