blob: d95e92b7135780b5f0c5af5910b39ffe65518d7d [file] [log] [blame]
Dan Gittik8dbd7e92018-12-03 15:35:53 +00001/*
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
17package com.android.server.display.whitebalance;
18
19import android.annotation.NonNull;
20import android.util.Slog;
Daniel Solomonfb393e52019-02-08 20:26:27 -080021import android.util.Spline;
Dan Gittik8dbd7e92018-12-03 15:35:53 +000022
23import com.android.internal.util.Preconditions;
24import com.android.server.LocalServices;
25import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
26import com.android.server.display.utils.History;
27
28import java.io.PrintWriter;
29
30/**
31 * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
Dan Gittik1151ac02019-02-13 14:05:37 +000032 * display color temperature depending on the ambient color temperature).
Dan Gittik8dbd7e92018-12-03 15:35:53 +000033 *
34 * The DisplayWhiteBalanceController:
35 * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
36 * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
37 * noise, and arrive at an estimate of the actual ambient color temperature;
Dan Gittik1151ac02019-02-13 14:05:37 +000038 * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should
39 * be updated, suppressing changes that are too frequent or too minor.
Dan Gittik8dbd7e92018-12-03 15:35:53 +000040 */
41public class DisplayWhiteBalanceController implements
42 AmbientSensor.AmbientBrightnessSensor.Callbacks,
43 AmbientSensor.AmbientColorTemperatureSensor.Callbacks {
44
45 protected static final String TAG = "DisplayWhiteBalanceController";
46 protected boolean mLoggingEnabled;
47
48 private boolean mEnabled;
49
50 // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
51 // implements Callbacks and passes itself to the DWBC so it can call back into it without
52 // knowing about it.
53 private Callbacks mCallbacks;
54
55 private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
56 private AmbientFilter mBrightnessFilter;
57 private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
58 private AmbientFilter mColorTemperatureFilter;
59 private DisplayWhiteBalanceThrottler mThrottler;
60
61 // When the brightness drops below a certain threshold, it affects the color temperature
62 // accuracy, so we fall back to a fixed ambient color temperature.
63 private final float mLowLightAmbientBrightnessThreshold;
64 private final float mLowLightAmbientColorTemperature;
65
66 private float mAmbientColorTemperature;
67 private float mPendingAmbientColorTemperature;
68 private float mLastAmbientColorTemperature;
69
70 private ColorDisplayServiceInternal mColorDisplayServiceInternal;
71
72 // The most recent ambient color temperature values are kept for debugging purposes.
73 private static final int HISTORY_SIZE = 50;
74 private History mAmbientColorTemperatureHistory;
75
76 // Override the ambient color temperature for debugging purposes.
77 private float mAmbientColorTemperatureOverride;
78
Dan Gittik1151ac02019-02-13 14:05:37 +000079 // A piecewise linear relationship between ambient and display color temperatures.
80 private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
Daniel Solomonfb393e52019-02-08 20:26:27 -080081
Dan Gittik8dbd7e92018-12-03 15:35:53 +000082 /**
83 * @param brightnessSensor
84 * The sensor used to detect changes in the ambient brightness.
85 * @param brightnessFilter
86 * The filter used to avergae ambient brightness changes over time, filter out the noise
87 * and arrive at an estimate of the actual ambient brightness.
88 * @param colorTemperatureSensor
89 * The sensor used to detect changes in the ambient color temperature.
90 * @param colorTemperatureFilter
91 * The filter used to average ambient color temperature changes over time, filter out the
92 * noise and arrive at an estimate of the actual ambient color temperature.
93 * @param throttler
Dan Gittik1151ac02019-02-13 14:05:37 +000094 * The throttler used to determine whether the new display color temperature should be
Dan Gittik8dbd7e92018-12-03 15:35:53 +000095 * updated or not.
96 * @param lowLightAmbientBrightnessThreshold
97 * The ambient brightness threshold beneath which we fall back to a fixed ambient color
98 * temperature.
99 * @param lowLightAmbientColorTemperature
100 * The ambient color temperature to which we fall back when the ambient brightness drops
101 * beneath a certain threshold.
Dan Gittik1151ac02019-02-13 14:05:37 +0000102 * @param ambientColorTemperatures
103 * The ambient color tempeartures used to map the ambient color temperature to the display
104 * color temperature (or null if no mapping is necessary).
105 * @param displayColorTemperatures
106 * The display color temperatures used to map the ambient color temperature to the display
107 * color temperature (or null if no mapping is necessary).
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000108 *
109 * @throws NullPointerException
110 * - brightnessSensor is null;
111 * - brightnessFilter is null;
112 * - colorTemperatureSensor is null;
113 * - colorTemperatureFilter is null;
114 * - throttler is null.
115 */
116 public DisplayWhiteBalanceController(
117 @NonNull AmbientSensor.AmbientBrightnessSensor brightnessSensor,
118 @NonNull AmbientFilter brightnessFilter,
119 @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
120 @NonNull AmbientFilter colorTemperatureFilter,
121 @NonNull DisplayWhiteBalanceThrottler throttler,
Daniel Solomonfb393e52019-02-08 20:26:27 -0800122 float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature,
Dan Gittik1151ac02019-02-13 14:05:37 +0000123 float[] ambientColorTemperatures, float[] displayColorTemperatures) {
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000124 validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
125 colorTemperatureFilter, throttler);
126 mLoggingEnabled = false;
127 mEnabled = false;
128 mCallbacks = null;
129 mBrightnessSensor = brightnessSensor;
130 mBrightnessFilter = brightnessFilter;
131 mColorTemperatureSensor = colorTemperatureSensor;
132 mColorTemperatureFilter = colorTemperatureFilter;
133 mThrottler = throttler;
134 mLowLightAmbientBrightnessThreshold = lowLightAmbientBrightnessThreshold;
135 mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
136 mAmbientColorTemperature = -1.0f;
137 mPendingAmbientColorTemperature = -1.0f;
138 mLastAmbientColorTemperature = -1.0f;
139 mAmbientColorTemperatureHistory = new History(HISTORY_SIZE);
140 mAmbientColorTemperatureOverride = -1.0f;
Daniel Solomonfb393e52019-02-08 20:26:27 -0800141
142 try {
Dan Gittik1151ac02019-02-13 14:05:37 +0000143 mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
144 ambientColorTemperatures, displayColorTemperatures);
Daniel Solomonfb393e52019-02-08 20:26:27 -0800145 } catch (Exception e) {
Dan Gittik1151ac02019-02-13 14:05:37 +0000146 mAmbientToDisplayColorTemperatureSpline = null;
Daniel Solomonfb393e52019-02-08 20:26:27 -0800147 }
148
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000149 mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
150 }
151
152 /**
153 * Enable/disable the controller.
154 *
155 * @param enabled
156 * Whether the controller should be on/off.
157 *
158 * @return Whether the method succeeded or not.
159 */
160 public boolean setEnabled(boolean enabled) {
161 if (enabled) {
162 return enable();
163 } else {
164 return disable();
165 }
166 }
167
168 /**
Dan Gittik1151ac02019-02-13 14:05:37 +0000169 * Set an object to call back to when the display color temperature should be updated.
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000170 *
171 * @param callbacks
172 * The object to call back to.
173 *
174 * @return Whether the method succeeded or not.
175 */
176 public boolean setCallbacks(Callbacks callbacks) {
177 if (mCallbacks == callbacks) {
178 return false;
179 }
180 mCallbacks = callbacks;
181 return true;
182 }
183
184 /**
185 * Enable/disable logging.
186 *
187 * @param loggingEnabled
188 * Whether logging should be on/off.
189 *
190 * @return Whether the method succeeded or not.
191 */
192 public boolean setLoggingEnabled(boolean loggingEnabled) {
193 if (mLoggingEnabled == loggingEnabled) {
194 return false;
195 }
196 mLoggingEnabled = loggingEnabled;
197 mBrightnessSensor.setLoggingEnabled(loggingEnabled);
198 mBrightnessFilter.setLoggingEnabled(loggingEnabled);
199 mColorTemperatureSensor.setLoggingEnabled(loggingEnabled);
200 mColorTemperatureFilter.setLoggingEnabled(loggingEnabled);
201 mThrottler.setLoggingEnabled(loggingEnabled);
202 return true;
203 }
204
205 /**
206 * Set the ambient color temperature override.
207 *
208 * This is only applied when the ambient color temperature changes or is updated (in which case
209 * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
Dan Gittik1151ac02019-02-13 14:05:37 +0000210 * change the display color temperature immediately.
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000211 *
212 * @param ambientColorTemperatureOverride
213 * The ambient color temperature override.
214 *
215 * @return Whether the method succeeded or not.
216 */
217 public boolean setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride) {
218 if (mAmbientColorTemperatureOverride == ambientColorTemperatureOverride) {
219 return false;
220 }
221 mAmbientColorTemperatureOverride = ambientColorTemperatureOverride;
222 return true;
223 }
224
225 /**
226 * Dump the state.
227 *
228 * @param writer
229 * The writer used to dump the state.
230 */
231 public void dump(PrintWriter writer) {
232 writer.println("DisplayWhiteBalanceController");
233 writer.println(" mLoggingEnabled=" + mLoggingEnabled);
234 writer.println(" mEnabled=" + mEnabled);
235 writer.println(" mCallbacks=" + mCallbacks);
236 mBrightnessSensor.dump(writer);
237 mBrightnessFilter.dump(writer);
238 mColorTemperatureSensor.dump(writer);
239 mColorTemperatureFilter.dump(writer);
240 mThrottler.dump(writer);
241 writer.println(" mLowLightAmbientBrightnessThreshold="
242 + mLowLightAmbientBrightnessThreshold);
243 writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
244 writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature);
245 writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
246 writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
247 writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
248 writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
Dan Gittik1151ac02019-02-13 14:05:37 +0000249 writer.println(" mAmbientToDisplayColorTemperatureSpline="
250 + mAmbientToDisplayColorTemperatureSpline);
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000251 }
252
253 @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
254 public void onAmbientBrightnessChanged(float value) {
255 final long time = System.currentTimeMillis();
256 mBrightnessFilter.addValue(time, value);
257 updateAmbientColorTemperature();
258 }
259
260 @Override // AmbientSensor.AmbientColorTemperatureSensor.Callbacks
261 public void onAmbientColorTemperatureChanged(float value) {
262 final long time = System.currentTimeMillis();
263 mColorTemperatureFilter.addValue(time, value);
264 updateAmbientColorTemperature();
265 }
266
267 /**
268 * Updates the ambient color temperature.
269 */
270 public void updateAmbientColorTemperature() {
271 final long time = System.currentTimeMillis();
272 float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
273
Dan Gittik1151ac02019-02-13 14:05:37 +0000274 if (mAmbientToDisplayColorTemperatureSpline != null) {
Daniel Solomonfb393e52019-02-08 20:26:27 -0800275 ambientColorTemperature =
Dan Gittik1151ac02019-02-13 14:05:37 +0000276 mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
Daniel Solomonfb393e52019-02-08 20:26:27 -0800277 }
278
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000279 final float ambientBrightness = mBrightnessFilter.getEstimate(time);
280 if (ambientBrightness < mLowLightAmbientBrightnessThreshold) {
281 if (mLoggingEnabled) {
282 Slog.d(TAG, "low light ambient brightness: " + ambientBrightness + " < "
283 + mLowLightAmbientBrightnessThreshold
284 + ", falling back to fixed ambient color temperature: "
285 + ambientColorTemperature + " => " + mLowLightAmbientColorTemperature);
286 }
287 ambientColorTemperature = mLowLightAmbientColorTemperature;
288 }
289
290 if (mAmbientColorTemperatureOverride != -1.0f) {
291 if (mLoggingEnabled) {
292 Slog.d(TAG, "override ambient color temperature: " + ambientColorTemperature
293 + " => " + mAmbientColorTemperatureOverride);
294 }
295 ambientColorTemperature = mAmbientColorTemperatureOverride;
296 }
297
Dan Gittik1151ac02019-02-13 14:05:37 +0000298 // When the display color temperature needs to be updated, we call DisplayPowerController to
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000299 // call our updateColorTemperature. The reason we don't call it directly is that we want
300 // all changes to the system to happen in a predictable order in DPC's main loop
301 // (updatePowerState).
302 if (ambientColorTemperature == -1.0f || mThrottler.throttle(ambientColorTemperature)) {
303 return;
304 }
305
306 if (mLoggingEnabled) {
307 Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
308 }
309 mPendingAmbientColorTemperature = ambientColorTemperature;
310 if (mCallbacks != null) {
311 mCallbacks.updateWhiteBalance();
312 }
313 }
314
315 /**
Dan Gittik1151ac02019-02-13 14:05:37 +0000316 * Updates the display color temperature.
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000317 */
Dan Gittik1151ac02019-02-13 14:05:37 +0000318 public void updateDisplayColorTemperature() {
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000319 float ambientColorTemperature = -1.0f;
320
321 // If both the pending and the current ambient color temperatures are -1, it means the DWBC
322 // was just enabled, and we use the last ambient color temperature until new sensor events
323 // give us a better estimate.
324 if (mAmbientColorTemperature == -1.0f && mPendingAmbientColorTemperature == -1.0f) {
325 ambientColorTemperature = mLastAmbientColorTemperature;
326 }
327
328 // Otherwise, we use the pending ambient color temperature, but only if it's non-trivial
329 // and different than the current one.
330 if (mPendingAmbientColorTemperature != -1.0f
331 && mPendingAmbientColorTemperature != mAmbientColorTemperature) {
332 ambientColorTemperature = mPendingAmbientColorTemperature;
333 }
334
335 if (ambientColorTemperature == -1.0f) {
336 return;
337 }
338
339 mAmbientColorTemperature = ambientColorTemperature;
340 if (mLoggingEnabled) {
341 Slog.d(TAG, "ambient color temperature: " + mAmbientColorTemperature);
342 }
343 mPendingAmbientColorTemperature = -1.0f;
344 mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
345 mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
346 (int) mAmbientColorTemperature);
347 mLastAmbientColorTemperature = mAmbientColorTemperature;
348 }
349
350 /**
351 * The DisplayWhiteBalanceController decouples itself from its parent (DisplayPowerController)
352 * by providing this interface to implement (and a method to set its callbacks object), and
353 * calling these methods.
354 */
355 public interface Callbacks {
356
357 /**
358 * Called whenever the display white-balance state has changed.
359 *
360 * Usually, this means the estimated ambient color temperature has changed enough, and the
Dan Gittik1151ac02019-02-13 14:05:37 +0000361 * display color temperature should be updated; but it is also called if settings change.
Dan Gittik8dbd7e92018-12-03 15:35:53 +0000362 */
363 void updateWhiteBalance();
364 }
365
366 private void validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor,
367 AmbientFilter brightnessFilter,
368 AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
369 AmbientFilter colorTemperatureFilter,
370 DisplayWhiteBalanceThrottler throttler) {
371 Preconditions.checkNotNull(brightnessSensor, "brightnessSensor must not be null");
372 Preconditions.checkNotNull(brightnessFilter, "brightnessFilter must not be null");
373 Preconditions.checkNotNull(colorTemperatureSensor,
374 "colorTemperatureSensor must not be null");
375 Preconditions.checkNotNull(colorTemperatureFilter,
376 "colorTemperatureFilter must not be null");
377 Preconditions.checkNotNull(throttler, "throttler cannot be null");
378 }
379
380 private boolean enable() {
381 if (mEnabled) {
382 return false;
383 }
384 if (mLoggingEnabled) {
385 Slog.d(TAG, "enabling");
386 }
387 mEnabled = true;
388 mBrightnessSensor.setEnabled(true);
389 mColorTemperatureSensor.setEnabled(true);
390 return true;
391 }
392
393 private boolean disable() {
394 if (!mEnabled) {
395 return false;
396 }
397 if (mLoggingEnabled) {
398 Slog.d(TAG, "disabling");
399 }
400 mEnabled = false;
401 mBrightnessSensor.setEnabled(false);
402 mBrightnessFilter.clear();
403 mColorTemperatureSensor.setEnabled(false);
404 mColorTemperatureFilter.clear();
405 mThrottler.clear();
406 mAmbientColorTemperature = -1.0f;
407 mPendingAmbientColorTemperature = -1.0f;
408 return true;
409 }
410
411}