• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("------------------------------");
432         writer.println("  mLoggingEnabled=" + mLoggingEnabled);
433         writer.println("  mEnabled=" + mEnabled);
434         writer.println("  mStrongModeEnabled=" + mStrongModeEnabled);
435         writer.println("  mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks);
436         mBrightnessSensor.dump(writer);
437         mBrightnessFilter.dump(writer);
438         mColorTemperatureSensor.dump(writer);
439         mColorTemperatureFilter.dump(writer);
440         mThrottler.dump(writer);
441         writer.println("  mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
442         writer.println("  mLowLightAmbientColorTemperatureStrong="
443                 + mLowLightAmbientColorTemperatureStrong);
444         writer.println("  mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
445         writer.println("  mHighLightAmbientColorTemperatureStrong="
446                 + mHighLightAmbientColorTemperatureStrong);
447         writer.println("  mAmbientColorTemperature=" + mAmbientColorTemperature);
448         writer.println("  mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
449         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
450         writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
451         writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
452         writer.println("  mAmbientToDisplayColorTemperatureSpline="
453                 + mAmbientToDisplayColorTemperatureSpline);
454         writer.println("  mStrongAmbientToDisplayColorTemperatureSpline="
455                 + mStrongAmbientToDisplayColorTemperatureSpline);
456         writer.println("  mLowLightAmbientBrightnessToBiasSpline="
457                 + mLowLightAmbientBrightnessToBiasSpline);
458         writer.println("  mLowLightAmbientBrightnessToBiasSplineStrong="
459                 + mLowLightAmbientBrightnessToBiasSplineStrong);
460         writer.println("  mHighLightAmbientBrightnessToBiasSpline="
461                 + mHighLightAmbientBrightnessToBiasSpline);
462         writer.println("  mHighLightAmbientBrightnessToBiasSplineStrong="
463                 + mHighLightAmbientBrightnessToBiasSplineStrong);
464     }
465 
466     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
onAmbientBrightnessChanged(float value)467     public void onAmbientBrightnessChanged(float value) {
468         final long time = System.currentTimeMillis();
469         mBrightnessFilter.addValue(time, value);
470         updateAmbientColorTemperature();
471     }
472 
473     @Override // AmbientSensor.AmbientColorTemperatureSensor.Callbacks
onAmbientColorTemperatureChanged(float value)474     public void onAmbientColorTemperatureChanged(float value) {
475         final long time = System.currentTimeMillis();
476         mColorTemperatureFilter.addValue(time, value);
477         updateAmbientColorTemperature();
478     }
479 
480     /**
481      * Updates the ambient color temperature.
482      */
updateAmbientColorTemperature()483     public void updateAmbientColorTemperature() {
484         final long time = System.currentTimeMillis();
485         final float lowLightAmbientColorTemperature = mStrongModeEnabled
486                 ? mLowLightAmbientColorTemperatureStrong : mLowLightAmbientColorTemperature;
487         final float highLightAmbientColorTemperature = mStrongModeEnabled
488                 ? mHighLightAmbientColorTemperatureStrong : mHighLightAmbientColorTemperature;
489         final Spline.LinearSpline lowLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
490                 ? mLowLightAmbientBrightnessToBiasSplineStrong
491                 : mLowLightAmbientBrightnessToBiasSpline;
492         final Spline.LinearSpline highLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
493                 ? mHighLightAmbientBrightnessToBiasSplineStrong
494                 : mHighLightAmbientBrightnessToBiasSpline;
495 
496         float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
497         mLatestAmbientColorTemperature = ambientColorTemperature;
498 
499         if (mStrongModeEnabled) {
500             if (mStrongAmbientToDisplayColorTemperatureSpline != null
501                     && ambientColorTemperature != -1.0f) {
502                 ambientColorTemperature =
503                         mStrongAmbientToDisplayColorTemperatureSpline.interpolate(
504                                 ambientColorTemperature);
505             }
506         } else {
507             if (mAmbientToDisplayColorTemperatureSpline != null
508                     && ambientColorTemperature != -1.0f) {
509                 ambientColorTemperature =
510                         mAmbientToDisplayColorTemperatureSpline.interpolate(
511                                 ambientColorTemperature);
512             }
513         }
514 
515         float ambientBrightness = mBrightnessFilter.getEstimate(time);
516         mLatestAmbientBrightness = ambientBrightness;
517 
518         if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
519                 && lowLightAmbientBrightnessToBiasSpline != null) {
520             float bias = lowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
521             ambientColorTemperature =
522                     bias * ambientColorTemperature + (1.0f - bias)
523                     * lowLightAmbientColorTemperature;
524             mLatestLowLightBias = bias;
525         }
526         if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
527                 && highLightAmbientBrightnessToBiasSpline != null) {
528             float bias = highLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
529             ambientColorTemperature =
530                     (1.0f - bias) * ambientColorTemperature + bias
531                     * highLightAmbientColorTemperature;
532             mLatestHighLightBias = bias;
533         }
534 
535         if (mAmbientColorTemperatureOverride != -1.0f) {
536             if (mLoggingEnabled) {
537                 Slog.d(TAG, "override ambient color temperature: " + ambientColorTemperature
538                         + " => " + mAmbientColorTemperatureOverride);
539             }
540             ambientColorTemperature = mAmbientColorTemperatureOverride;
541         }
542 
543         // When the display color temperature needs to be updated, we call DisplayPowerController to
544         // call our updateColorTemperature. The reason we don't call it directly is that we want
545         // all changes to the system to happen in a predictable order in DPC's main loop
546         // (updatePowerState).
547         if (ambientColorTemperature == -1.0f || mThrottler.throttle(ambientColorTemperature)) {
548             return;
549         }
550 
551         if (mLoggingEnabled) {
552             Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
553         }
554         mPendingAmbientColorTemperature = ambientColorTemperature;
555         if (mDisplayPowerControllerCallbacks != null) {
556             mDisplayPowerControllerCallbacks.updateWhiteBalance();
557         }
558     }
559 
560     /**
561      * Updates the display color temperature.
562      */
updateDisplayColorTemperature()563     public void updateDisplayColorTemperature() {
564         float ambientColorTemperature = -1.0f;
565 
566         // If both the pending and the current ambient color temperatures are -1, it means the DWBC
567         // was just enabled, and we use the last ambient color temperature until new sensor events
568         // give us a better estimate.
569         if (mAmbientColorTemperature == -1.0f && mPendingAmbientColorTemperature == -1.0f) {
570             ambientColorTemperature = mLastAmbientColorTemperature;
571         }
572 
573         // Otherwise, we use the pending ambient color temperature, but only if it's non-trivial
574         // and different than the current one.
575         if (mPendingAmbientColorTemperature != -1.0f
576                 && mPendingAmbientColorTemperature != mAmbientColorTemperature) {
577             ambientColorTemperature = mPendingAmbientColorTemperature;
578         }
579 
580         if (ambientColorTemperature == -1.0f) {
581             return;
582         }
583 
584         mAmbientColorTemperature = ambientColorTemperature;
585         if (mLoggingEnabled) {
586             Slog.d(TAG, "ambient color temperature: " + mAmbientColorTemperature);
587         }
588         mPendingAmbientColorTemperature = -1.0f;
589         mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
590         Slog.d(TAG, "Display cct: " + mAmbientColorTemperature
591                 + " Latest ambient cct: " + mLatestAmbientColorTemperature
592                 + " Latest ambient lux: " + mLatestAmbientBrightness
593                 + " Latest low light bias: " + mLatestLowLightBias
594                 + " Latest high light bias: " + mLatestHighLightBias);
595         mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
596                 (int) mAmbientColorTemperature);
597         mLastAmbientColorTemperature = mAmbientColorTemperature;
598     }
599 
600     /**
601      * Calculate the adjusted brightness, in nits, due to the DWB color adaptation
602      *
603      * @param requestedBrightnessNits brightness the framework requires to be output
604      * @return the adjusted brightness the framework needs to output to counter the drop in
605      *         brightness due to DWB, or the requestedBrightnessNits if an adjustment cannot be made
606      */
calculateAdjustedBrightnessNits(float requestedBrightnessNits)607     public float calculateAdjustedBrightnessNits(float requestedBrightnessNits) {
608         float luminance = mColorDisplayServiceInternal.getDisplayWhiteBalanceLuminance();
609         if (luminance == -1) {
610             return requestedBrightnessNits;
611         }
612         float effectiveBrightness = requestedBrightnessNits * luminance;
613         return (requestedBrightnessNits - effectiveBrightness) + requestedBrightnessNits;
614     }
615 
616     /**
617      * The DisplayWhiteBalanceController decouples itself from its parent (DisplayPowerController)
618      * by providing this interface to implement (and a method to set its callbacks object), and
619      * calling these methods.
620      */
621     public interface Callbacks {
622 
623         /**
624          * Called whenever the display white-balance state has changed.
625          *
626          * Usually, this means the estimated ambient color temperature has changed enough, and the
627          * display color temperature should be updated; but it is also called if settings change.
628          */
updateWhiteBalance()629         void updateWhiteBalance();
630     }
631 
validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor, AmbientFilter brightnessFilter, AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, AmbientFilter colorTemperatureFilter, DisplayWhiteBalanceThrottler throttler)632     private void validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor,
633             AmbientFilter brightnessFilter,
634             AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
635             AmbientFilter colorTemperatureFilter,
636             DisplayWhiteBalanceThrottler throttler) {
637         Objects.requireNonNull(brightnessSensor, "brightnessSensor must not be null");
638         Objects.requireNonNull(brightnessFilter, "brightnessFilter must not be null");
639         Objects.requireNonNull(colorTemperatureSensor,
640                 "colorTemperatureSensor must not be null");
641         Objects.requireNonNull(colorTemperatureFilter,
642                 "colorTemperatureFilter must not be null");
643         Objects.requireNonNull(throttler, "throttler cannot be null");
644     }
645 
enable()646     private boolean enable() {
647         if (mEnabled) {
648             return false;
649         }
650         if (mLoggingEnabled) {
651             Slog.d(TAG, "enabling");
652         }
653         mEnabled = true;
654         mBrightnessSensor.setEnabled(true);
655         mColorTemperatureSensor.setEnabled(true);
656         return true;
657     }
658 
disable()659     private boolean disable() {
660         if (!mEnabled) {
661             return false;
662         }
663         if (mLoggingEnabled) {
664             Slog.d(TAG, "disabling");
665         }
666         mEnabled = false;
667         mBrightnessSensor.setEnabled(false);
668         mBrightnessFilter.clear();
669         mColorTemperatureSensor.setEnabled(false);
670         mColorTemperatureFilter.clear();
671         mThrottler.clear();
672         mAmbientColorTemperature = -1.0f;
673         mPendingAmbientColorTemperature = -1.0f;
674         mColorDisplayServiceInternal.resetDisplayWhiteBalanceColorTemperature();
675         return true;
676     }
677 
678 }
679