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     // In high brightness conditions certain color temperatures can cause peak display
67     // brightness to drop. This fixed color temperature can be used to compensate for
68     // this effect.
69     private final float mHighLightAmbientColorTemperature;
70 
71     private final boolean mLightModeAllowed;
72 
73     private float mAmbientColorTemperature;
74     @VisibleForTesting
75     float mPendingAmbientColorTemperature;
76     private float mLastAmbientColorTemperature;
77 
78     // The most recent ambient color temperature values are kept for debugging purposes.
79     private final History mAmbientColorTemperatureHistory;
80 
81     // Override the ambient color temperature for debugging purposes.
82     private float mAmbientColorTemperatureOverride;
83 
84     // A piecewise linear relationship between ambient and display color temperatures.
85     private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
86 
87     // A piecewise linear relationship between ambient and display color temperatures, with a
88     // stronger change between the two sets of values.
89     private Spline.LinearSpline mStrongAmbientToDisplayColorTemperatureSpline;
90 
91     // In very low or very high brightness conditions Display White Balance should
92     // be to set to a default instead of using mAmbientToDisplayColorTemperatureSpline.
93     // However, setting Display White Balance based on thresholds can cause the
94     // display to rapidly change color temperature. To solve this,
95     // mLowLightAmbientBrightnessToBiasSpline and
96     // mHighLightAmbientBrightnessToBiasSpline are used to smoothly interpolate from
97     // ambient color temperature to the defaults. A piecewise linear relationship
98     // between low light brightness and low light bias.
99     private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
100 
101     // A piecewise linear relationship between high light brightness and high light bias.
102     private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
103 
104     private float mLatestAmbientColorTemperature;
105     private float mLatestAmbientBrightness;
106     private float mLatestLowLightBias;
107     private float mLatestHighLightBias;
108 
109     private boolean mEnabled;
110 
111     // Whether a higher-strength adjustment should be applied; this must be enabled in addition to
112     // mEnabled in order to be applied.
113     private boolean mStrongModeEnabled;
114 
115     // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
116     // implements Callbacks and passes itself to the DWBC so it can call back into it without
117     // knowing about it.
118     private Callbacks mDisplayPowerControllerCallbacks;
119 
120     /**
121      * @param brightnessSensor
122      *      The sensor used to detect changes in the ambient brightness.
123      * @param brightnessFilter
124      *      The filter used to avergae ambient brightness changes over time, filter out the noise
125      *      and arrive at an estimate of the actual ambient brightness.
126      * @param colorTemperatureSensor
127      *      The sensor used to detect changes in the ambient color temperature.
128      * @param colorTemperatureFilter
129      *      The filter used to average ambient color temperature changes over time, filter out the
130      *      noise and arrive at an estimate of the actual ambient color temperature.
131      * @param throttler
132      *      The throttler used to determine whether the new display color temperature should be
133      *      updated or not.
134      * @param lowLightAmbientBrightnesses
135      *      The ambient brightness used to map the ambient brightnesses to the biases used to
136      *      interpolate to lowLightAmbientColorTemperature.
137      * @param lowLightAmbientBiases
138      *      The biases used to map the ambient brightnesses to the biases used to interpolate to
139      *      lowLightAmbientColorTemperature.
140      * @param lowLightAmbientColorTemperature
141      *      The ambient color temperature to which we interpolate to based on the low light curve.
142      * @param highLightAmbientBrightnesses
143      *      The ambient brightness used to map the ambient brightnesses to the biases used to
144      *      interpolate to highLightAmbientColorTemperature.
145      * @param highLightAmbientBiases
146      *      The biases used to map the ambient brightnesses to the biases used to interpolate to
147      *      highLightAmbientColorTemperature.
148      * @param highLightAmbientColorTemperature
149      *      The ambient color temperature to which we interpolate to based on the high light curve.
150      * @param ambientColorTemperatures
151      *      The ambient color tempeartures used to map the ambient color temperature to the display
152      *      color temperature (or null if no mapping is necessary).
153      * @param displayColorTemperatures
154      *      The display color temperatures used to map the ambient color temperature to the display
155      *      color temperature (or null if no mapping is necessary).
156      * @param lightModeAllowed
157      *      Whether a lighter version should be applied when Strong Mode is not enabled.
158      *
159      * @throws NullPointerException
160      *      - brightnessSensor is null;
161      *      - brightnessFilter is null;
162      *      - colorTemperatureSensor is null;
163      *      - colorTemperatureFilter is null;
164      *      - throttler is null.
165      */
DisplayWhiteBalanceController( @onNull AmbientSensor.AmbientBrightnessSensor brightnessSensor, @NonNull AmbientFilter brightnessFilter, @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases, float lowLightAmbientColorTemperature, float[] highLightAmbientBrightnesses, float[] highLightAmbientBiases, float highLightAmbientColorTemperature, float[] ambientColorTemperatures, float[] displayColorTemperatures, float[] strongAmbientColorTemperatures, float[] strongDisplayColorTemperatures, boolean lightModeAllowed)166     public DisplayWhiteBalanceController(
167             @NonNull AmbientSensor.AmbientBrightnessSensor brightnessSensor,
168             @NonNull AmbientFilter brightnessFilter,
169             @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
170             @NonNull AmbientFilter colorTemperatureFilter,
171             @NonNull DisplayWhiteBalanceThrottler throttler,
172             float[] lowLightAmbientBrightnesses,
173             float[] lowLightAmbientBiases,
174             float lowLightAmbientColorTemperature,
175             float[] highLightAmbientBrightnesses,
176             float[] highLightAmbientBiases,
177             float highLightAmbientColorTemperature,
178             float[] ambientColorTemperatures,
179             float[] displayColorTemperatures,
180             float[] strongAmbientColorTemperatures,
181             float[] strongDisplayColorTemperatures,
182             boolean lightModeAllowed) {
183         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
184                 colorTemperatureFilter, throttler);
185         mBrightnessSensor = brightnessSensor;
186         mBrightnessFilter = brightnessFilter;
187         mColorTemperatureSensor = colorTemperatureSensor;
188         mColorTemperatureFilter = colorTemperatureFilter;
189         mThrottler = throttler;
190         mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
191         mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
192         mAmbientColorTemperature = -1.0f;
193         mPendingAmbientColorTemperature = -1.0f;
194         mLastAmbientColorTemperature = -1.0f;
195         mAmbientColorTemperatureHistory = new History(/* size= */ 50);
196         mAmbientColorTemperatureOverride = -1.0f;
197         mLightModeAllowed = lightModeAllowed;
198 
199         try {
200             mLowLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
201                     lowLightAmbientBrightnesses, lowLightAmbientBiases);
202         } catch (Exception e) {
203             Slog.e(TAG, "failed to create low light ambient brightness to bias spline.", e);
204             mLowLightAmbientBrightnessToBiasSpline = null;
205         }
206         if (mLowLightAmbientBrightnessToBiasSpline != null) {
207             if (mLowLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
208                     mLowLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
209                     != 1.0f) {
210                 Slog.d(TAG, "invalid low light ambient brightness to bias spline, "
211                         + "bias must begin at 0.0 and end at 1.0.");
212                 mLowLightAmbientBrightnessToBiasSpline = null;
213             }
214         }
215 
216         try {
217             mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
218                     highLightAmbientBrightnesses, highLightAmbientBiases);
219         } catch (Exception e) {
220             Slog.e(TAG, "failed to create high light ambient brightness to bias spline.", e);
221             mHighLightAmbientBrightnessToBiasSpline = null;
222         }
223         if (mHighLightAmbientBrightnessToBiasSpline != null) {
224             if (mHighLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f ||
225                     mHighLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY)
226                     != 1.0f) {
227                 Slog.d(TAG, "invalid high light ambient brightness to bias spline, "
228                         + "bias must begin at 0.0 and end at 1.0.");
229                 mHighLightAmbientBrightnessToBiasSpline = null;
230             }
231         }
232 
233         if (mLowLightAmbientBrightnessToBiasSpline != null &&
234                 mHighLightAmbientBrightnessToBiasSpline != null) {
235             if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
236                     highLightAmbientBrightnesses[0]) {
237                 Slog.d(TAG, "invalid low light and high light ambient brightness to bias spline "
238                         + "combination, defined domains must not intersect.");
239                 mLowLightAmbientBrightnessToBiasSpline = null;
240                 mHighLightAmbientBrightnessToBiasSpline = null;
241             }
242         }
243 
244         try {
245             mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
246                     ambientColorTemperatures, displayColorTemperatures);
247         } catch (Exception e) {
248             Slog.e(TAG, "failed to create ambient to display color temperature spline.", e);
249             mAmbientToDisplayColorTemperatureSpline = null;
250         }
251 
252         try {
253             mStrongAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
254                     strongAmbientColorTemperatures, strongDisplayColorTemperatures);
255         } catch (Exception e) {
256             Slog.e(TAG, "Failed to create strong ambient to display color temperature spline", e);
257         }
258 
259         mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
260     }
261 
262     /**
263      * Enable/disable the controller.
264      *
265      * @param enabled
266      *      Whether the controller should be on/off.
267      *
268      * @return Whether the method succeeded or not.
269      */
setEnabled(boolean enabled)270     public boolean setEnabled(boolean enabled) {
271         if (enabled) {
272             return enable();
273         } else {
274             return disable();
275         }
276     }
277 
278     /**
279      * Enable/disable the stronger adjustment option.
280      *
281      * @param enabled whether the stronger adjustment option should be turned on
282      */
setStrongModeEnabled(boolean enabled)283     public void setStrongModeEnabled(boolean enabled) {
284         mStrongModeEnabled = enabled;
285         mColorDisplayServiceInternal.setDisplayWhiteBalanceAllowed(mLightModeAllowed
286                 || mStrongModeEnabled);
287         if (mEnabled) {
288             updateAmbientColorTemperature();
289             updateDisplayColorTemperature();
290         }
291     }
292 
293     /**
294      * Set an object to call back to when the display color temperature should be updated.
295      *
296      * @param callbacks
297      *      The object to call back to.
298      *
299      * @return Whether the method succeeded or not.
300      */
setCallbacks(Callbacks callbacks)301     public boolean setCallbacks(Callbacks callbacks) {
302         if (mDisplayPowerControllerCallbacks == callbacks) {
303             return false;
304         }
305         mDisplayPowerControllerCallbacks = callbacks;
306         return true;
307     }
308 
309     /**
310      * Enable/disable logging.
311      *
312      * @param loggingEnabled
313      *      Whether logging should be on/off.
314      *
315      * @return Whether the method succeeded or not.
316      */
setLoggingEnabled(boolean loggingEnabled)317     public boolean setLoggingEnabled(boolean loggingEnabled) {
318         if (mLoggingEnabled == loggingEnabled) {
319             return false;
320         }
321         mLoggingEnabled = loggingEnabled;
322         mBrightnessSensor.setLoggingEnabled(loggingEnabled);
323         mBrightnessFilter.setLoggingEnabled(loggingEnabled);
324         mColorTemperatureSensor.setLoggingEnabled(loggingEnabled);
325         mColorTemperatureFilter.setLoggingEnabled(loggingEnabled);
326         mThrottler.setLoggingEnabled(loggingEnabled);
327         return true;
328     }
329 
330     /**
331      * Set the ambient color temperature override.
332      *
333      * This is only applied when the ambient color temperature changes or is updated (in which case
334      * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
335      * change the display color temperature immediately.
336      *
337      * @param ambientColorTemperatureOverride
338      *      The ambient color temperature override.
339      *
340      * @return Whether the method succeeded or not.
341      */
setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride)342     public boolean setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride) {
343         if (mAmbientColorTemperatureOverride == ambientColorTemperatureOverride) {
344             return false;
345         }
346         mAmbientColorTemperatureOverride = ambientColorTemperatureOverride;
347         return true;
348     }
349 
350     /**
351      * Dump the state.
352      *
353      * @param writer
354      *      The writer used to dump the state.
355      */
dump(PrintWriter writer)356     public void dump(PrintWriter writer) {
357         writer.println("DisplayWhiteBalanceController");
358         writer.println("  mLoggingEnabled=" + mLoggingEnabled);
359         writer.println("  mEnabled=" + mEnabled);
360         writer.println("  mStrongModeEnabled=" + mStrongModeEnabled);
361         writer.println("  mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks);
362         mBrightnessSensor.dump(writer);
363         mBrightnessFilter.dump(writer);
364         mColorTemperatureSensor.dump(writer);
365         mColorTemperatureFilter.dump(writer);
366         mThrottler.dump(writer);
367         writer.println("  mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
368         writer.println("  mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
369         writer.println("  mAmbientColorTemperature=" + mAmbientColorTemperature);
370         writer.println("  mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
371         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
372         writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
373         writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
374         writer.println("  mAmbientToDisplayColorTemperatureSpline="
375                 + mAmbientToDisplayColorTemperatureSpline);
376         writer.println("  mStrongAmbientToDisplayColorTemperatureSpline="
377                 + mStrongAmbientToDisplayColorTemperatureSpline);
378         writer.println("  mLowLightAmbientBrightnessToBiasSpline="
379                 + mLowLightAmbientBrightnessToBiasSpline);
380         writer.println("  mHighLightAmbientBrightnessToBiasSpline="
381                 + mHighLightAmbientBrightnessToBiasSpline);
382     }
383 
384     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
onAmbientBrightnessChanged(float value)385     public void onAmbientBrightnessChanged(float value) {
386         final long time = System.currentTimeMillis();
387         mBrightnessFilter.addValue(time, value);
388         updateAmbientColorTemperature();
389     }
390 
391     @Override // AmbientSensor.AmbientColorTemperatureSensor.Callbacks
onAmbientColorTemperatureChanged(float value)392     public void onAmbientColorTemperatureChanged(float value) {
393         final long time = System.currentTimeMillis();
394         mColorTemperatureFilter.addValue(time, value);
395         updateAmbientColorTemperature();
396     }
397 
398     /**
399      * Updates the ambient color temperature.
400      */
updateAmbientColorTemperature()401     public void updateAmbientColorTemperature() {
402         final long time = System.currentTimeMillis();
403         float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
404         mLatestAmbientColorTemperature = ambientColorTemperature;
405 
406         if (mStrongModeEnabled) {
407             if (mStrongAmbientToDisplayColorTemperatureSpline != null
408                     && ambientColorTemperature != -1.0f) {
409                 ambientColorTemperature =
410                         mStrongAmbientToDisplayColorTemperatureSpline.interpolate(
411                                 ambientColorTemperature);
412             }
413         } else {
414             if (mAmbientToDisplayColorTemperatureSpline != null
415                     && ambientColorTemperature != -1.0f) {
416                 ambientColorTemperature =
417                         mAmbientToDisplayColorTemperatureSpline.interpolate(
418                                 ambientColorTemperature);
419             }
420         }
421 
422         float ambientBrightness = mBrightnessFilter.getEstimate(time);
423         mLatestAmbientBrightness = ambientBrightness;
424 
425         if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
426                 && mLowLightAmbientBrightnessToBiasSpline != null) {
427             float bias = mLowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
428             ambientColorTemperature =
429                     bias * ambientColorTemperature + (1.0f - bias)
430                     * mLowLightAmbientColorTemperature;
431             mLatestLowLightBias = bias;
432         }
433         if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
434                 && mHighLightAmbientBrightnessToBiasSpline != null) {
435             float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
436             ambientColorTemperature =
437                     (1.0f - bias) * ambientColorTemperature + bias
438                     * mHighLightAmbientColorTemperature;
439             mLatestHighLightBias = bias;
440         }
441 
442         if (mAmbientColorTemperatureOverride != -1.0f) {
443             if (mLoggingEnabled) {
444                 Slog.d(TAG, "override ambient color temperature: " + ambientColorTemperature
445                         + " => " + mAmbientColorTemperatureOverride);
446             }
447             ambientColorTemperature = mAmbientColorTemperatureOverride;
448         }
449 
450         // When the display color temperature needs to be updated, we call DisplayPowerController to
451         // call our updateColorTemperature. The reason we don't call it directly is that we want
452         // all changes to the system to happen in a predictable order in DPC's main loop
453         // (updatePowerState).
454         if (ambientColorTemperature == -1.0f || mThrottler.throttle(ambientColorTemperature)) {
455             return;
456         }
457 
458         if (mLoggingEnabled) {
459             Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
460         }
461         mPendingAmbientColorTemperature = ambientColorTemperature;
462         if (mDisplayPowerControllerCallbacks != null) {
463             mDisplayPowerControllerCallbacks.updateWhiteBalance();
464         }
465     }
466 
467     /**
468      * Updates the display color temperature.
469      */
updateDisplayColorTemperature()470     public void updateDisplayColorTemperature() {
471         float ambientColorTemperature = -1.0f;
472 
473         // If both the pending and the current ambient color temperatures are -1, it means the DWBC
474         // was just enabled, and we use the last ambient color temperature until new sensor events
475         // give us a better estimate.
476         if (mAmbientColorTemperature == -1.0f && mPendingAmbientColorTemperature == -1.0f) {
477             ambientColorTemperature = mLastAmbientColorTemperature;
478         }
479 
480         // Otherwise, we use the pending ambient color temperature, but only if it's non-trivial
481         // and different than the current one.
482         if (mPendingAmbientColorTemperature != -1.0f
483                 && mPendingAmbientColorTemperature != mAmbientColorTemperature) {
484             ambientColorTemperature = mPendingAmbientColorTemperature;
485         }
486 
487         if (ambientColorTemperature == -1.0f) {
488             return;
489         }
490 
491         mAmbientColorTemperature = ambientColorTemperature;
492         if (mLoggingEnabled) {
493             Slog.d(TAG, "ambient color temperature: " + mAmbientColorTemperature);
494         }
495         mPendingAmbientColorTemperature = -1.0f;
496         mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
497         Slog.d(TAG, "Display cct: " + mAmbientColorTemperature
498                 + " Latest ambient cct: " + mLatestAmbientColorTemperature
499                 + " Latest ambient lux: " + mLatestAmbientBrightness
500                 + " Latest low light bias: " + mLatestLowLightBias
501                 + " Latest high light bias: " + mLatestHighLightBias);
502         mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
503                 (int) mAmbientColorTemperature);
504         mLastAmbientColorTemperature = mAmbientColorTemperature;
505     }
506 
507     /**
508      * Calculate the adjusted brightness, in nits, due to the DWB color adaptation
509      *
510      * @param requestedBrightnessNits brightness the framework requires to be output
511      * @return the adjusted brightness the framework needs to output to counter the drop in
512      *         brightness due to DWB, or the requestedBrightnessNits if an adjustment cannot be made
513      */
calculateAdjustedBrightnessNits(float requestedBrightnessNits)514     public float calculateAdjustedBrightnessNits(float requestedBrightnessNits) {
515         float luminance = mColorDisplayServiceInternal.getDisplayWhiteBalanceLuminance();
516         if (luminance == -1) {
517             return requestedBrightnessNits;
518         }
519         float effectiveBrightness = requestedBrightnessNits * luminance;
520         return (requestedBrightnessNits - effectiveBrightness) + requestedBrightnessNits;
521     }
522 
523     /**
524      * The DisplayWhiteBalanceController decouples itself from its parent (DisplayPowerController)
525      * by providing this interface to implement (and a method to set its callbacks object), and
526      * calling these methods.
527      */
528     public interface Callbacks {
529 
530         /**
531          * Called whenever the display white-balance state has changed.
532          *
533          * Usually, this means the estimated ambient color temperature has changed enough, and the
534          * display color temperature should be updated; but it is also called if settings change.
535          */
updateWhiteBalance()536         void updateWhiteBalance();
537     }
538 
validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor, AmbientFilter brightnessFilter, AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, AmbientFilter colorTemperatureFilter, DisplayWhiteBalanceThrottler throttler)539     private void validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor,
540             AmbientFilter brightnessFilter,
541             AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
542             AmbientFilter colorTemperatureFilter,
543             DisplayWhiteBalanceThrottler throttler) {
544         Objects.requireNonNull(brightnessSensor, "brightnessSensor must not be null");
545         Objects.requireNonNull(brightnessFilter, "brightnessFilter must not be null");
546         Objects.requireNonNull(colorTemperatureSensor,
547                 "colorTemperatureSensor must not be null");
548         Objects.requireNonNull(colorTemperatureFilter,
549                 "colorTemperatureFilter must not be null");
550         Objects.requireNonNull(throttler, "throttler cannot be null");
551     }
552 
enable()553     private boolean enable() {
554         if (mEnabled) {
555             return false;
556         }
557         if (mLoggingEnabled) {
558             Slog.d(TAG, "enabling");
559         }
560         mEnabled = true;
561         mBrightnessSensor.setEnabled(true);
562         mColorTemperatureSensor.setEnabled(true);
563         return true;
564     }
565 
disable()566     private boolean disable() {
567         if (!mEnabled) {
568             return false;
569         }
570         if (mLoggingEnabled) {
571             Slog.d(TAG, "disabling");
572         }
573         mEnabled = false;
574         mBrightnessSensor.setEnabled(false);
575         mBrightnessFilter.clear();
576         mColorTemperatureSensor.setEnabled(false);
577         mColorTemperatureFilter.clear();
578         mThrottler.clear();
579         mAmbientColorTemperature = -1.0f;
580         mPendingAmbientColorTemperature = -1.0f;
581         mColorDisplayServiceInternal.resetDisplayWhiteBalanceColorTemperature();
582         return true;
583     }
584 
585 }
586