• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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;
18 
19 import static android.text.TextUtils.formatSimple;
20 
21 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR;
22 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
23 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
24 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
25 
26 import android.annotation.Nullable;
27 import android.content.Context;
28 import android.content.pm.ApplicationInfo;
29 import android.content.res.TypedArray;
30 import android.hardware.display.BrightnessConfiguration;
31 import android.hardware.display.BrightnessCorrection;
32 import android.os.PowerManager;
33 import android.os.UserHandle;
34 import android.provider.Settings;
35 import android.util.LongArray;
36 import android.util.MathUtils;
37 import android.util.Pair;
38 import android.util.Slog;
39 import android.util.Spline;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.internal.display.BrightnessUtils;
43 import com.android.internal.util.Preconditions;
44 import com.android.server.display.utils.Plog;
45 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
46 
47 import java.io.PrintWriter;
48 import java.text.SimpleDateFormat;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Date;
52 import java.util.List;
53 import java.util.Locale;
54 import java.util.Objects;
55 
56 /**
57  * A utility to map from an ambient brightness to a display's "backlight" brightness based on the
58  * available display information and brightness configuration.
59  *
60  * Note that without a mapping from the nits to a display backlight level, any
61  * {@link BrightnessConfiguration}s that are set are just ignored.
62  */
63 public abstract class BrightnessMappingStrategy {
64     private static final String TAG = "BrightnessMappingStrategy";
65 
66     public static final float INVALID_LUX = -1;
67     public static final float INVALID_NITS = -1;
68 
69     private static final float LUX_GRAD_SMOOTHING = 0.25f;
70     private static final float MAX_GRAD = 1.0f;
71     private static final float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
72 
73     // Constant that ensures that each step of the curve can increase by up to at least
74     // MIN_PERMISSABLE_INCREASE. Otherwise when the brightness is set to 0, the curve will never
75     // increase and will always be 0.
76     private static final float MIN_PERMISSABLE_INCREASE =  0.004f;
77 
78     protected boolean mLoggingEnabled;
79 
80     private static final Plog PLOG = Plog.createSystemPlog(TAG);
81 
82     /**
83      * Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle
84      * mode.
85      *
86      * @param context
87      * @param displayDeviceConfig
88      * @param mode The auto-brightness mode. Different modes use different brightness curves
89      * @param displayWhiteBalanceController
90      * @return the BrightnessMappingStrategy
91      */
92     @Nullable
create(Context context, DisplayDeviceConfig displayDeviceConfig, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, @Nullable DisplayWhiteBalanceController displayWhiteBalanceController)93     static BrightnessMappingStrategy create(Context context,
94             DisplayDeviceConfig displayDeviceConfig,
95             @AutomaticBrightnessController.AutomaticBrightnessMode int mode,
96             @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
97 
98         // Display independent, mode dependent values
99         float[] brightnessLevelsNits = null;
100         float[] brightnessLevels = null;
101         float[] luxLevels = null;
102         int preset = Settings.System.getIntForUser(context.getContentResolver(),
103                 Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
104                 Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT);
105         switch (mode) {
106             case AUTO_BRIGHTNESS_MODE_DEFAULT -> {
107                 brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
108                 luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
109                 brightnessLevels =
110                         displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
111             }
112             case AUTO_BRIGHTNESS_MODE_IDLE -> {
113                 brightnessLevelsNits = getFloatArray(context.getResources().obtainTypedArray(
114                         com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle));
115                 luxLevels = getLuxLevels(context.getResources().getIntArray(
116                         com.android.internal.R.array.config_autoBrightnessLevelsIdle));
117             }
118             case AUTO_BRIGHTNESS_MODE_DOZE, AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR -> {
119                 luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
120                 brightnessLevels =
121                         displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
122             }
123         }
124 
125         // Display independent, mode independent values
126         float autoBrightnessAdjustmentMaxGamma = context.getResources().getFraction(
127                 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
128                 1, 1);
129         long shortTermModelTimeout = context.getResources().getInteger(
130                 com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
131 
132         // Display dependent values - used for physical mapping strategy nits -> brightness
133         final float[] nitsRange = displayDeviceConfig.getNits();
134         final float[] brightnessRange = displayDeviceConfig.getBrightness();
135 
136         if (isValidMapping(nitsRange, brightnessRange)
137                 && isValidMapping(luxLevels, brightnessLevelsNits)) {
138             BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
139                     luxLevels, brightnessLevelsNits);
140             builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);
141             builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
142             builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
143             return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
144                     autoBrightnessAdjustmentMaxGamma, mode, preset, displayWhiteBalanceController);
145         } else if (isValidMapping(luxLevels, brightnessLevels)) {
146             return new SimpleMappingStrategy(luxLevels, brightnessLevels,
147                     autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout, mode, preset);
148         } else {
149             return null;
150         }
151     }
152 
getLuxLevels(int[] lux)153     private static float[] getLuxLevels(int[] lux) {
154         // The first control point is implicit and always at 0 lux.
155         float[] levels = new float[lux.length + 1];
156         for (int i = 0; i < lux.length; i++) {
157             levels[i + 1] = (float) lux[i];
158         }
159         return levels;
160     }
161 
162     /**
163      * Extracts a float array from the specified {@link TypedArray}.
164      *
165      * @param array The array to convert.
166      * @return the given array as a float array.
167      */
getFloatArray(TypedArray array)168     public static float[] getFloatArray(TypedArray array) {
169         final int N = array.length();
170         float[] vals = new float[N];
171         for (int i = 0; i < N; i++) {
172             vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT);
173         }
174         array.recycle();
175         return vals;
176     }
177 
isValidMapping(float[] x, float[] y)178     private static boolean isValidMapping(float[] x, float[] y) {
179         if (x == null || y == null || x.length == 0 || y.length == 0) {
180             return false;
181         }
182         if (x.length != y.length) {
183             return false;
184         }
185         final int N = x.length;
186         float prevX = x[0];
187         float prevY = y[0];
188         if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) {
189             return false;
190         }
191         for (int i = 1; i < N; i++) {
192             if (prevX >= x[i] || prevY > y[i]) {
193                 return false;
194             }
195             if (Float.isNaN(x[i]) || Float.isNaN(y[i])) {
196                 return false;
197             }
198             prevX = x[i];
199             prevY = y[i];
200         }
201         return true;
202     }
203 
isValidMapping(float[] x, int[] y)204     private static boolean isValidMapping(float[] x, int[] y) {
205         if (x == null || y == null || x.length == 0 || y.length == 0) {
206             return false;
207         }
208         if (x.length != y.length) {
209             return false;
210         }
211         final int N = x.length;
212         float prevX = x[0];
213         int prevY = y[0];
214         if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) {
215             return false;
216         }
217         for (int i = 1; i < N; i++) {
218             if (prevX >= x[i] || prevY > y[i]) {
219                 return false;
220             }
221             if (Float.isNaN(x[i])) {
222                 return false;
223             }
224             prevX = x[i];
225             prevY = y[i];
226         }
227         return true;
228     }
229 
230     /**
231      * Enable/disable logging.
232      *
233      * @param loggingEnabled
234      *      Whether logging should be on/off.
235      *
236      * @return Whether the method succeeded or not.
237      */
setLoggingEnabled(boolean loggingEnabled)238     public boolean setLoggingEnabled(boolean loggingEnabled) {
239         if (mLoggingEnabled == loggingEnabled) {
240             return false;
241         }
242         mLoggingEnabled = loggingEnabled;
243         return true;
244     }
245 
246     /**
247      * Sets the {@link BrightnessConfiguration}.
248      *
249      * @param config The new configuration. If {@code null} is passed, the default configuration is
250      *               used.
251      * @return Whether the brightness configuration has changed.
252      */
setBrightnessConfiguration(@ullable BrightnessConfiguration config)253     public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
254 
255     /**
256      * Gets the current {@link BrightnessConfiguration}.
257      */
258     @Nullable
getBrightnessConfiguration()259     public abstract BrightnessConfiguration getBrightnessConfiguration();
260 
261     /**
262      * Returns the desired brightness of the display based on the current ambient lux, including
263      * any context-related corrections.
264      *
265      * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
266      * brightness and 0 is the display at minimum brightness.
267      *
268      * @param lux The current ambient brightness in lux.
269      * @param packageName the foreground app package name.
270      * @param category the foreground app package category.
271      * @return The desired brightness of the display normalized to the range [0, 1.0].
272      */
getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)273     public abstract float getBrightness(float lux, String packageName,
274             @ApplicationInfo.Category int category);
275 
276     /**
277      * Returns the desired brightness of the display based on the current ambient lux.
278      *
279      * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
280      * brightness and 0 is the display at minimum brightness.
281      *
282      * @param lux The current ambient brightness in lux.
283      *
284      * @return The desired brightness of the display normalized to the range [0, 1.0].
285      */
getBrightness(float lux)286     public float getBrightness(float lux) {
287         return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
288     }
289 
290     /**
291      * Returns the current auto-brightness adjustment.
292      *
293      * The returned adjustment is a value in the range [-1.0, 1.0] such that
294      * {@code config_autoBrightnessAdjustmentMaxGamma<sup>-adjustment</sup>} is used to gamma
295      * correct the brightness curve.
296      */
getAutoBrightnessAdjustment()297     public abstract float getAutoBrightnessAdjustment();
298 
299     /**
300      * Sets the auto-brightness adjustment.
301      *
302      * @param adjustment The desired auto-brightness adjustment.
303      * @return Whether the auto-brightness adjustment has changed.
304      *
305      * @Deprecated The auto-brightness adjustment should not be set directly, but rather inferred
306      * from user data points.
307      */
setAutoBrightnessAdjustment(float adjustment)308     public abstract boolean setAutoBrightnessAdjustment(float adjustment);
309 
310     /**
311      * Converts the provided brightness value to nits if possible.
312      *
313      * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits.
314      */
convertToNits(float brightness)315     public abstract float convertToNits(float brightness);
316 
317     /**
318      * Converts the provided brightness value to nits if possible. Adjustments, such as RBC are
319      * applied.
320      *
321      * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits.
322      */
convertToAdjustedNits(float brightness)323     public abstract float convertToAdjustedNits(float brightness);
324 
325     /**
326      * Converts the provided nit value to a float scale value if possible.
327      *
328      * Returns {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if there's no available mapping for
329      * the nits to float scale.
330      */
getBrightnessFromNits(float nits)331     public abstract float getBrightnessFromNits(float nits);
332 
333     /**
334      * Adds a user interaction data point to the brightness mapping.
335      *
336      * This data point <b>must</b> exist on the brightness curve as a result of this call. This is
337      * so that the next time we come to query what the screen brightness should be, we get what the
338      * user requested rather than immediately changing to some other value.
339      *
340      * Currently, we only keep track of one of these at a time to constrain what can happen to the
341      * curve.
342      */
addUserDataPoint(float lux, float brightness)343     public abstract void addUserDataPoint(float lux, float brightness);
344 
345     /**
346      * Removes any short term adjustments made to the curve from user interactions.
347      *
348      * Note that this does *not* reset the mapping to its initial state, any brightness
349      * configurations that have been applied will continue to be in effect. This solely removes the
350      * effects of user interactions on the model.
351      */
clearUserDataPoints()352     public abstract void clearUserDataPoints();
353 
354     /** @return True if there are any short term adjustments applied to the curve. */
hasUserDataPoints()355     public abstract boolean hasUserDataPoints();
356 
357     /** @return True if the current brightness configuration is the default one. */
isDefaultConfig()358     public abstract boolean isDefaultConfig();
359 
360     /** @return The default brightness configuration. */
getDefaultConfig()361     public abstract BrightnessConfiguration getDefaultConfig();
362 
363     /** Recalculates the backlight-to-nits and nits-to-backlight splines. */
recalculateSplines(boolean applyAdjustment, float[] adjustment)364     public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment);
365 
366     /**
367      * Returns the timeout, in milliseconds for the short term model
368      *
369      * Timeout after which we remove the effects any user interactions might've had on the
370      * brightness mapping. This timeout doesn't start until we transition to a non-interactive
371      * display policy so that we don't reset while users are using their devices, but also so that
372      * we don't erroneously keep the short-term model if the device is dozing but the
373      * display is fully on.
374      *
375      * This timeout is also used when the device switches from interactive screen brightness mode
376      * to idle screen brightness mode, to preserve the user's preference when they resume usage of
377      * the device, within the specified timeframe.
378      */
getShortTermModelTimeout()379     public abstract long getShortTermModelTimeout();
380 
381     /**
382      * Prints dump output for display dumpsys.
383      */
dump(PrintWriter pw, float hbmTransition)384     public abstract void dump(PrintWriter pw, float hbmTransition);
385 
getUserLux()386     abstract float getUserLux();
387 
getUserBrightness()388     abstract float getUserBrightness();
389 
390     /**
391      * @return The auto-brightness mode of this mapping strategy. Different modes use different
392      * brightness curves.
393      */
394     @AutomaticBrightnessController.AutomaticBrightnessMode
getMode()395     abstract int getMode();
396 
397     /**
398      * @return The preset for this mapping strategy. Presets are used on devices that allow users
399      * to choose from a set of predefined options in display auto-brightness settings.
400      */
getPreset()401     abstract int getPreset();
402 
403     /**
404      * Check if the short term model should be reset given the anchor lux the last
405      * brightness change was made at and the current ambient lux.
406      */
shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor)407     public boolean shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor) {
408         BrightnessConfiguration config = getBrightnessConfiguration();
409         float minThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO;
410         float maxThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO;
411         if (config != null) {
412             if (!Float.isNaN(config.getShortTermModelLowerLuxMultiplier())) {
413                 minThresholdRatio = config.getShortTermModelLowerLuxMultiplier();
414             }
415             if (!Float.isNaN(config.getShortTermModelUpperLuxMultiplier())) {
416                 maxThresholdRatio = config.getShortTermModelUpperLuxMultiplier();
417             }
418         }
419         final float minAmbientLux =
420                 shortTermModelAnchor - shortTermModelAnchor * minThresholdRatio;
421         final float maxAmbientLux =
422                 shortTermModelAnchor + shortTermModelAnchor * maxThresholdRatio;
423         if (minAmbientLux < ambientLux && ambientLux <= maxAmbientLux) {
424             if (mLoggingEnabled) {
425                 Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is "
426                         + minAmbientLux + " < " + ambientLux + " < " + maxAmbientLux);
427             }
428             return false;
429         } else {
430             Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + ambientLux
431                     + "(" + minAmbientLux + ", " + maxAmbientLux + ")");
432             return true;
433         }
434     }
435 
insertControlPoint( float[] luxLevels, float[] brightnessLevels, float lux, float brightness)436     private Pair<float[], float[]> insertControlPoint(
437             float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
438         final int idx = findInsertionPoint(luxLevels, lux);
439         final float[] newLuxLevels;
440         final float[] newBrightnessLevels;
441         if (idx == luxLevels.length) {
442             newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1);
443             newBrightnessLevels  = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1);
444             newLuxLevels[idx] = lux;
445             newBrightnessLevels[idx] = brightness;
446         } else if (luxLevels[idx] == lux) {
447             newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length);
448             newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length);
449             newBrightnessLevels[idx] = brightness;
450         } else {
451             newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1);
452             System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+1, luxLevels.length - idx);
453             newLuxLevels[idx] = lux;
454             newBrightnessLevels  = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1);
455             System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+1,
456                     brightnessLevels.length - idx);
457             newBrightnessLevels[idx] = brightness;
458         }
459         smoothCurve(newLuxLevels, newBrightnessLevels, idx);
460         return Pair.create(newLuxLevels, newBrightnessLevels);
461     }
462 
463     /**
464      * Returns the index of the first value that's less than or equal to {@code val}.
465      *
466      * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
467      * than val, then it will return the length of arr as the insertion point.
468      */
findInsertionPoint(float[] arr, float val)469     private int findInsertionPoint(float[] arr, float val) {
470         for (int i = 0; i < arr.length; i++) {
471             if (val <= arr[i]) {
472                 return i;
473             }
474         }
475         return arr.length;
476     }
477 
smoothCurve(float[] lux, float[] brightness, int idx)478     private void smoothCurve(float[] lux, float[] brightness, int idx) {
479         if (mLoggingEnabled) {
480             PLOG.logCurve("unsmoothed curve", lux, brightness);
481         }
482         float prevLux = lux[idx];
483         float prevBrightness = brightness[idx];
484         // Smooth curve for data points above the newly introduced point
485         for (int i = idx+1; i < lux.length; i++) {
486             float currLux = lux[i];
487             float currBrightness = brightness[i];
488             float maxBrightness = MathUtils.max(
489                     prevBrightness * permissibleRatio(currLux, prevLux),
490                     prevBrightness + MIN_PERMISSABLE_INCREASE);
491             float newBrightness = MathUtils.constrain(
492                     currBrightness, prevBrightness, maxBrightness);
493             if (newBrightness == currBrightness) {
494                 break;
495             }
496             prevLux = currLux;
497             prevBrightness = newBrightness;
498             brightness[i] = newBrightness;
499         }
500         // Smooth curve for data points below the newly introduced point
501         prevLux = lux[idx];
502         prevBrightness = brightness[idx];
503         for (int i = idx-1; i >= 0; i--) {
504             float currLux = lux[i];
505             float currBrightness = brightness[i];
506             float minBrightness = prevBrightness * permissibleRatio(currLux, prevLux);
507             float newBrightness = MathUtils.constrain(
508                     currBrightness, minBrightness, prevBrightness);
509             if (newBrightness == currBrightness) {
510                 break;
511             }
512             prevLux = currLux;
513             prevBrightness = newBrightness;
514             brightness[i] = newBrightness;
515         }
516         if (mLoggingEnabled) {
517             PLOG.logCurve("smoothed curve", lux, brightness);
518         }
519     }
520 
permissibleRatio(float currLux, float prevLux)521     private float permissibleRatio(float currLux, float prevLux) {
522         return MathUtils.pow((currLux + LUX_GRAD_SMOOTHING)
523                 / (prevLux + LUX_GRAD_SMOOTHING), MAX_GRAD);
524     }
525 
inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness, float currentBrightness)526     protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
527             float currentBrightness) {
528         float adjustment = 0;
529         float gamma = Float.NaN;
530         // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
531         // affects the curve rather drastically.
532         if (currentBrightness <= 0.1f || currentBrightness >= 0.9f) {
533             adjustment = (desiredBrightness - currentBrightness);
534         // Edge case: darkest adjustment possible.
535         } else if (desiredBrightness == 0) {
536             adjustment = -1;
537         // Edge case: brightest adjustment possible.
538         } else if (desiredBrightness == 1) {
539             adjustment = +1;
540         } else {
541             // current^gamma = desired => gamma = log[current](desired)
542             gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness);
543             // max^-adjustment = gamma => adjustment = -log[max](gamma)
544             adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
545         }
546         adjustment = MathUtils.constrain(adjustment, -1, +1);
547         if (mLoggingEnabled) {
548             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
549                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
550             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
551                     MathUtils.pow(currentBrightness, gamma) + " == " + desiredBrightness);
552         }
553         return adjustment;
554     }
555 
getAdjustedCurve(float[] lux, float[] brightness, float userLux, float userBrightness, float adjustment, float maxGamma)556     protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
557             float userLux, float userBrightness, float adjustment, float maxGamma) {
558         float[] newLux = lux;
559         float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
560         if (mLoggingEnabled) {
561             PLOG.logCurve("unadjusted curve", newLux, newBrightness);
562         }
563         adjustment = MathUtils.constrain(adjustment, -1, 1);
564         float gamma = MathUtils.pow(maxGamma, -adjustment);
565         if (mLoggingEnabled) {
566             Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
567                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
568         }
569         if (gamma != 1) {
570             for (int i = 0; i < newBrightness.length; i++) {
571                 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
572             }
573         }
574         if (mLoggingEnabled) {
575             PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
576         }
577         if (userLux != INVALID_LUX) {
578             Pair<float[], float[]> curve = insertControlPoint(newLux, newBrightness, userLux,
579                     userBrightness);
580             newLux = curve.first;
581             newBrightness = curve.second;
582             if (mLoggingEnabled) {
583                 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
584                 // This is done for comparison.
585                 curve = insertControlPoint(lux, brightness, userLux, userBrightness);
586                 PLOG.logCurve("user adjusted curve", curve.first ,curve.second);
587             }
588         }
589         return Pair.create(newLux, newBrightness);
590     }
591 
592     /**
593      * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the
594      * backlight of the display.
595      *
596      * Since we don't have information about the display's physical brightness, any brightness
597      * configurations that are set are just ignored.
598      */
599     private static class SimpleMappingStrategy extends BrightnessMappingStrategy {
600         // Lux control points
601         private final float[] mLux;
602         // Brightness control points normalized to [0, 1]
603         private final float[] mBrightness;
604 
605         @AutomaticBrightnessController.AutomaticBrightnessMode
606         private final int mMode;
607 
608         private final int mPreset;
609 
610         private Spline mSpline;
611         private float mMaxGamma;
612         private float mAutoBrightnessAdjustment;
613         private float mUserLux;
614         private float mUserBrightness;
615         private long mShortTermModelTimeout;
616 
SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma, long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset)617         private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma,
618                 long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode,
619                 int preset) {
620             Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
621                     "Lux and brightness arrays must not be empty!");
622             Preconditions.checkArgument(lux.length == brightness.length,
623                     "Lux and brightness arrays must be the same length!");
624             Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux");
625             Preconditions.checkArrayElementsInRange(brightness,
626                     0, Integer.MAX_VALUE, "brightness");
627 
628             final int N = brightness.length;
629             mLux = new float[N];
630             mBrightness = new float[N];
631             for (int i = 0; i < N; i++) {
632                 mLux[i] = lux[i];
633                 mBrightness[i] = brightness[i];
634             }
635 
636             mMaxGamma = maxGamma;
637             mAutoBrightnessAdjustment = 0;
638             mUserLux = INVALID_LUX;
639             mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
640             if (mLoggingEnabled) {
641                 PLOG.start("simple mapping strategy");
642             }
643             computeSpline();
644             mShortTermModelTimeout = timeout;
645             mMode = mode;
646             mPreset = preset;
647         }
648 
649         @Override
getShortTermModelTimeout()650         public long getShortTermModelTimeout() {
651             return mShortTermModelTimeout;
652         }
653 
654         @Override
setBrightnessConfiguration(@ullable BrightnessConfiguration config)655         public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
656             return false;
657         }
658 
659         @Override
getBrightnessConfiguration()660         public BrightnessConfiguration getBrightnessConfiguration() {
661             return null;
662         }
663 
664         @Override
getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)665         public float getBrightness(float lux, String packageName,
666                 @ApplicationInfo.Category int category) {
667             return mSpline.interpolate(lux);
668         }
669 
670         @Override
getAutoBrightnessAdjustment()671         public float getAutoBrightnessAdjustment() {
672             return mAutoBrightnessAdjustment;
673         }
674 
675         @Override
setAutoBrightnessAdjustment(float adjustment)676         public boolean setAutoBrightnessAdjustment(float adjustment) {
677             adjustment = MathUtils.constrain(adjustment, -1, 1);
678             if (adjustment == mAutoBrightnessAdjustment) {
679                 return false;
680             }
681             if (mLoggingEnabled) {
682                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
683                         adjustment);
684                 PLOG.start("auto-brightness adjustment");
685             }
686             mAutoBrightnessAdjustment = adjustment;
687             computeSpline();
688             return true;
689         }
690 
691         @Override
convertToNits(float brightness)692         public float convertToNits(float brightness) {
693             return INVALID_NITS;
694         }
695 
696         @Override
convertToAdjustedNits(float brightness)697         public float convertToAdjustedNits(float brightness) {
698             return INVALID_NITS;
699         }
700 
701         @Override
getBrightnessFromNits(float nits)702         public float getBrightnessFromNits(float nits) {
703             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
704         }
705 
706         @Override
addUserDataPoint(float lux, float brightness)707         public void addUserDataPoint(float lux, float brightness) {
708             float unadjustedBrightness = getUnadjustedBrightness(lux);
709             if (mLoggingEnabled) {
710                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
711                 PLOG.start("add user data point")
712                         .logPoint("user data point", lux, brightness)
713                         .logPoint("current brightness", lux, unadjustedBrightness);
714             }
715             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
716                     brightness /* desiredBrightness */,
717                     unadjustedBrightness /* currentBrightness */);
718             if (mLoggingEnabled) {
719                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
720                         adjustment);
721             }
722             mAutoBrightnessAdjustment = adjustment;
723             mUserLux = lux;
724             mUserBrightness = brightness;
725             computeSpline();
726         }
727 
728         @Override
clearUserDataPoints()729         public void clearUserDataPoints() {
730             if (mUserLux != INVALID_LUX) {
731                 if (mLoggingEnabled) {
732                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
733                     PLOG.start("clear user data points")
734                             .logPoint("user data point", mUserLux, mUserBrightness);
735                 }
736                 mAutoBrightnessAdjustment = 0;
737                 mUserLux = INVALID_LUX;
738                 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
739                 computeSpline();
740             }
741         }
742 
743         @Override
hasUserDataPoints()744         public boolean hasUserDataPoints() {
745             return mUserLux != INVALID_LUX;
746         }
747 
748         @Override
isDefaultConfig()749         public boolean isDefaultConfig() {
750             return true;
751         }
752 
753         @Override
getDefaultConfig()754         public BrightnessConfiguration getDefaultConfig() {
755             return null;
756         }
757 
758         @Override
recalculateSplines(boolean applyAdjustment, float[] adjustment)759         public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
760             // Do nothing.
761         }
762 
763         @Override
dump(PrintWriter pw, float hbmTransition)764         public void dump(PrintWriter pw, float hbmTransition) {
765             pw.println("SimpleMappingStrategy");
766             pw.println("  mSpline=" + mSpline);
767             pw.println("  mMaxGamma=" + mMaxGamma);
768             pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
769             pw.println("  mUserLux=" + mUserLux);
770             pw.println("  mUserBrightness=" + mUserBrightness);
771             pw.println("  mShortTermModelTimeout=" + mShortTermModelTimeout);
772         }
773 
774         @Override
getMode()775         int getMode() {
776             return mMode;
777         }
778 
779         @Override
getPreset()780         int getPreset() {
781             return mPreset;
782         }
783 
784         @Override
getUserLux()785         float getUserLux() {
786             return mUserLux;
787         }
788 
789         @Override
getUserBrightness()790         float getUserBrightness() {
791             return mUserBrightness;
792         }
793 
computeSpline()794         private void computeSpline() {
795             Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux,
796                     mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
797             mSpline = Spline.createSpline(curve.first, curve.second);
798         }
799 
getUnadjustedBrightness(float lux)800         private float getUnadjustedBrightness(float lux) {
801             Spline spline = Spline.createSpline(mLux, mBrightness);
802             return spline.interpolate(lux);
803         }
804     }
805 
806     /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical
807      * range of the display, rather than to the range of the backlight control (typically 0-255).
808      *
809      * By mapping through the physical brightness, the curve becomes portable across devices and
810      * gives us more resolution in the resulting mapping.
811      */
812     @VisibleForTesting
813     static class PhysicalMappingStrategy extends BrightnessMappingStrategy {
814         // The current brightness configuration.
815         private BrightnessConfiguration mConfig;
816 
817         // A spline mapping from the current ambient light in lux to the desired display brightness
818         // in nits.
819         private Spline mBrightnessSpline;
820 
821         // A spline mapping from nits to the corresponding brightness value, normalized to the range
822         // [0, 1.0].
823         private Spline mNitsToBrightnessSpline;
824 
825         // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to
826         // a brightness in nits.
827         private Spline mBrightnessToNitsSpline;
828 
829         // A spline mapping from nits with adjustments applied to the corresponding brightness
830         // value, normalized to the range [0, 1.0].
831         private Spline mAdjustedNitsToBrightnessSpline;
832 
833         // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to
834         // a brightness in nits with adjustments applied.
835         private Spline mBrightnessToAdjustedNitsSpline;
836 
837         // The default brightness configuration.
838         private final BrightnessConfiguration mDefaultConfig;
839 
840         private final float[] mNits;
841         private final float[] mBrightness;
842 
843         private boolean mBrightnessRangeAdjustmentApplied;
844 
845         private final float mMaxGamma;
846         private float mAutoBrightnessAdjustment;
847         private float mUserLux;
848         private float mUserBrightness;
849 
850         @Nullable
851         private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
852 
853         @AutomaticBrightnessController.AutomaticBrightnessMode
854         private final int mMode;
855 
856         private final int mPreset;
857 
858         // Previous short-term models and the times that they were computed stored for debugging
859         // purposes
860         private List<Spline> mPreviousBrightnessSplines = new ArrayList<>();
861         private LongArray mBrightnessSplineChangeTimes = new LongArray();
862         private static final int NO_OF_PREVIOUS_CONFIGS_TO_LOG = 5;
863         private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
864 
PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, float[] brightness, float maxGamma, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset, @Nullable DisplayWhiteBalanceController displayWhiteBalanceController)865         public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits,
866                 float[] brightness, float maxGamma,
867                 @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset,
868                 @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) {
869 
870             Preconditions.checkArgument(nits.length != 0 && brightness.length != 0,
871                     "Nits and brightness arrays must not be empty!");
872 
873             Preconditions.checkArgument(nits.length == brightness.length,
874                     "Nits and brightness arrays must be the same length!");
875             Objects.requireNonNull(config);
876             Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
877             Preconditions.checkArrayElementsInRange(brightness,
878                     PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness");
879 
880             mMode = mode;
881             mPreset = preset;
882             mMaxGamma = maxGamma;
883             mAutoBrightnessAdjustment = 0;
884             mUserLux = INVALID_LUX;
885             mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
886             mDisplayWhiteBalanceController = displayWhiteBalanceController;
887 
888             mNits = nits;
889             mBrightness = brightness;
890             computeNitsBrightnessSplines(mNits);
891             mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline;
892             mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline;
893 
894             mDefaultConfig = config;
895             if (mLoggingEnabled) {
896                 PLOG.start("physical mapping strategy");
897             }
898             mConfig = config;
899             computeSpline();
900         }
901 
902         @Override
getShortTermModelTimeout()903         public long getShortTermModelTimeout() {
904             if (mConfig.getShortTermModelTimeoutMillis() >= 0) {
905                 return mConfig.getShortTermModelTimeoutMillis();
906             } else {
907                 return mDefaultConfig.getShortTermModelTimeoutMillis();
908             }
909         }
910 
911         @Override
setBrightnessConfiguration(@ullable BrightnessConfiguration config)912         public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
913             if (config == null) {
914                 config = mDefaultConfig;
915             }
916             if (config.equals(mConfig)) {
917                 return false;
918             }
919             if (mLoggingEnabled) {
920                 PLOG.start("brightness configuration");
921             }
922             mConfig = config;
923             computeSpline();
924             return true;
925         }
926 
927         @Override
getBrightnessConfiguration()928         public BrightnessConfiguration getBrightnessConfiguration() {
929             return mConfig;
930         }
931 
932         @Override
getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)933         public float getBrightness(float lux, String packageName,
934                 @ApplicationInfo.Category int category) {
935             float nits = mBrightnessSpline.interpolate(lux);
936 
937             // Adjust nits to compensate for display white balance colour strength.
938             if (mDisplayWhiteBalanceController != null) {
939                 nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits);
940             }
941 
942             float brightness = mAdjustedNitsToBrightnessSpline.interpolate(nits);
943             // Correct the brightness according to the current application and its category, but
944             // only if no user data point is set (as this will override the user setting).
945             if (mUserLux == -1) {
946                 brightness = correctBrightness(brightness, packageName, category);
947             } else if (mLoggingEnabled) {
948                 Slog.d(TAG, "user point set, correction not applied");
949             }
950             return brightness;
951         }
952 
953         @Override
getAutoBrightnessAdjustment()954         public float getAutoBrightnessAdjustment() {
955             return mAutoBrightnessAdjustment;
956         }
957 
958         @Override
setAutoBrightnessAdjustment(float adjustment)959         public boolean setAutoBrightnessAdjustment(float adjustment) {
960             adjustment = MathUtils.constrain(adjustment, -1, 1);
961             if (adjustment == mAutoBrightnessAdjustment) {
962                 return false;
963             }
964             if (mLoggingEnabled) {
965                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
966                         adjustment);
967                 PLOG.start("auto-brightness adjustment");
968             }
969             mAutoBrightnessAdjustment = adjustment;
970             computeSpline();
971             return true;
972         }
973 
974         @Override
convertToNits(float brightness)975         public float convertToNits(float brightness) {
976             return mBrightnessToNitsSpline.interpolate(brightness);
977         }
978 
979         @Override
convertToAdjustedNits(float brightness)980         public float convertToAdjustedNits(float brightness) {
981             return mBrightnessToAdjustedNitsSpline.interpolate(brightness);
982         }
983 
984         @Override
getBrightnessFromNits(float nits)985         public float getBrightnessFromNits(float nits) {
986             return mNitsToBrightnessSpline.interpolate(nits);
987         }
988 
989         @Override
addUserDataPoint(float lux, float brightness)990         public void addUserDataPoint(float lux, float brightness) {
991             float unadjustedBrightness = getUnadjustedBrightness(lux);
992             if (mLoggingEnabled) {
993                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
994                 PLOG.start("add user data point")
995                         .logPoint("user data point", lux, brightness)
996                         .logPoint("current brightness", lux, unadjustedBrightness);
997             }
998             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
999                     brightness /* desiredBrightness */,
1000                     unadjustedBrightness /* currentBrightness */);
1001             if (mLoggingEnabled) {
1002                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
1003                         adjustment);
1004             }
1005             mAutoBrightnessAdjustment = adjustment;
1006             mUserLux = lux;
1007             mUserBrightness = brightness;
1008             computeSpline();
1009 
1010             if (mPreviousBrightnessSplines.size() == NO_OF_PREVIOUS_CONFIGS_TO_LOG) {
1011                 mPreviousBrightnessSplines.remove(0);
1012                 mBrightnessSplineChangeTimes.remove(0);
1013             }
1014             mPreviousBrightnessSplines.add(mBrightnessSpline);
1015             mBrightnessSplineChangeTimes.add(System.currentTimeMillis());
1016         }
1017 
1018         @Override
clearUserDataPoints()1019         public void clearUserDataPoints() {
1020             if (mUserLux != -1) {
1021                 if (mLoggingEnabled) {
1022                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
1023                     PLOG.start("clear user data points")
1024                             .logPoint("user data point", mUserLux, mUserBrightness);
1025                 }
1026                 mAutoBrightnessAdjustment = 0;
1027                 mUserLux = INVALID_LUX;
1028                 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
1029                 computeSpline();
1030             }
1031         }
1032 
1033         @Override
hasUserDataPoints()1034         public boolean hasUserDataPoints() {
1035             return mUserLux != INVALID_LUX;
1036         }
1037 
1038         @Override
isDefaultConfig()1039         public boolean isDefaultConfig() {
1040             return mDefaultConfig.equals(mConfig);
1041         }
1042 
1043         @Override
getDefaultConfig()1044         public BrightnessConfiguration getDefaultConfig() {
1045             return mDefaultConfig;
1046         }
1047 
1048         @Override
recalculateSplines(boolean applyAdjustment, float[] adjustedNits)1049         public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) {
1050             mBrightnessRangeAdjustmentApplied = applyAdjustment;
1051             if (applyAdjustment) {
1052                 mAdjustedNitsToBrightnessSpline = Spline.createSpline(adjustedNits, mBrightness);
1053                 mBrightnessToAdjustedNitsSpline = Spline.createSpline(mBrightness, adjustedNits);
1054             } else {
1055                 mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline;
1056                 mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline;
1057             }
1058         }
1059 
1060         @Override
dump(PrintWriter pw, float hbmTransition)1061         public void dump(PrintWriter pw, float hbmTransition) {
1062             pw.println("PhysicalMappingStrategy");
1063             pw.println("  mConfig=" + mConfig);
1064             pw.println("  mBrightnessSpline=" + mBrightnessSpline);
1065             pw.println("  mNitsToBrightnessSpline=" + mNitsToBrightnessSpline);
1066             pw.println("  mBrightnessToNitsSpline=" + mBrightnessToNitsSpline);
1067             pw.println("  mAdjustedNitsToBrightnessSpline=" + mAdjustedNitsToBrightnessSpline);
1068             pw.println("  mAdjustedBrightnessToNitsSpline=" + mBrightnessToAdjustedNitsSpline);
1069             pw.println("  mMaxGamma=" + mMaxGamma);
1070             pw.println("  mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
1071             pw.println("  mUserLux=" + mUserLux);
1072             pw.println("  mUserBrightness=" + mUserBrightness);
1073             pw.println("  mDefaultConfig=" + mDefaultConfig);
1074             pw.println("  mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
1075             pw.println("  shortTermModelTimeout=" + getShortTermModelTimeout());
1076 
1077             if (!mPreviousBrightnessSplines.isEmpty()) {
1078                 pw.println("  Previous short-term models (oldest to newest): ");
1079             }
1080             for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) {
1081                 pw.println("  Computed at "
1082                         + FORMAT.format(new Date(mBrightnessSplineChangeTimes.get(i))) + ": ");
1083                 dumpConfigDiff(pw, hbmTransition, mPreviousBrightnessSplines.get(i),
1084                         /* shortTermModelOnly= */ true);
1085             }
1086 
1087             pw.println("  Difference between current config and default: ");
1088             dumpConfigDiff(pw, hbmTransition, mBrightnessSpline, /* shortTermModelOnly= */ false);
1089         }
1090 
1091         @Override
getMode()1092         int getMode() {
1093             return mMode;
1094         }
1095 
1096         @Override
getPreset()1097         int getPreset() {
1098             return mPreset;
1099         }
1100 
1101         @Override
getUserLux()1102         float getUserLux() {
1103             return mUserLux;
1104         }
1105 
1106         @Override
getUserBrightness()1107         float getUserBrightness() {
1108             return mUserBrightness;
1109         }
1110 
1111         /**
1112          * Prints out the default curve and how it differs from the long-term curve
1113          * and the current curve (in case the current curve includes short-term adjustments).
1114          *
1115          * @param pw The print-writer to write to.
1116          */
dumpConfigDiff(PrintWriter pw, float hbmTransition, Spline brightnessSpline, boolean shortTermModelOnly)1117         private void dumpConfigDiff(PrintWriter pw, float hbmTransition, Spline brightnessSpline,
1118                 boolean shortTermModelOnly) {
1119             Pair<float[], float[]> currentCurve = mConfig.getCurve();
1120             Spline currSpline = Spline.createSpline(currentCurve.first, currentCurve.second);
1121 
1122             Pair<float[], float[]> defaultCurve = mDefaultConfig.getCurve();
1123             Spline defaultSpline = Spline.createSpline(defaultCurve.first, defaultCurve.second);
1124 
1125             // Add the short-term curve lux point if present
1126             float[] luxes = currentCurve.first;
1127             if (mUserLux >= 0) {
1128                 luxes = Arrays.copyOf(currentCurve.first, currentCurve.first.length + 1);
1129                 luxes[luxes.length - 1] = mUserLux;
1130                 Arrays.sort(luxes);
1131             }
1132 
1133             StringBuilder sbLux = null;
1134             StringBuilder sbNits = null;
1135             StringBuilder sbLong = null;
1136             StringBuilder sbShort = null;
1137             StringBuilder sbBrightness = null;
1138             StringBuilder sbPercent = null;
1139             StringBuilder sbPercentHbm = null;
1140             boolean needsHeaders = true;
1141             String separator = "";
1142             for (int i = 0; i < luxes.length; i++) {
1143                 float lux = luxes[i];
1144                 if (needsHeaders) {
1145                     sbLux = new StringBuilder("            lux: ");
1146                     sbNits = new StringBuilder("        default: ");
1147                     sbLong = new StringBuilder("      long-term: ");
1148                     sbShort = new StringBuilder("        current: ");
1149                     sbBrightness = new StringBuilder("    current(bl): ");
1150                     sbPercent = new StringBuilder("     current(%): ");
1151                     sbPercentHbm = new StringBuilder("  current(hbm%): ");
1152                     needsHeaders = false;
1153                 }
1154 
1155                 float defaultNits = defaultSpline.interpolate(lux);
1156                 float longTermNits = currSpline.interpolate(lux);
1157                 float shortTermNits = brightnessSpline.interpolate(lux);
1158                 float brightness = mAdjustedNitsToBrightnessSpline.interpolate(shortTermNits);
1159 
1160                 String luxPrefix = (lux == mUserLux ? "^" : "");
1161                 String strLux = luxPrefix + toStrFloatForDump(lux);
1162                 String strNits = toStrFloatForDump(defaultNits);
1163                 String strLong = toStrFloatForDump(longTermNits);
1164                 String strShort = toStrFloatForDump(shortTermNits);
1165                 String strBrightness = toStrFloatForDump(brightness);
1166                 String strPercent = String.valueOf(
1167                         Math.round(100.0f * BrightnessUtils.convertLinearToGamma(
1168                             (brightness / hbmTransition))));
1169                 String strPercentHbm = String.valueOf(
1170                         Math.round(100.0f * BrightnessUtils.convertLinearToGamma(brightness)));
1171 
1172                 int maxLen = Math.max(strLux.length(),
1173                         Math.max(strNits.length(),
1174                         Math.max(strBrightness.length(),
1175                         Math.max(strPercent.length(),
1176                         Math.max(strPercentHbm.length(),
1177                         Math.max(strLong.length(), strShort.length()))))));
1178                 String format = separator + "%" + maxLen + "s";
1179                 separator = ", ";
1180 
1181                 sbLux.append(formatSimple(format, strLux));
1182                 sbNits.append(formatSimple(format, strNits));
1183                 sbLong.append(formatSimple(format, strLong));
1184                 sbShort.append(formatSimple(format, strShort));
1185                 sbBrightness.append(formatSimple(format, strBrightness));
1186                 sbPercent.append(formatSimple(format, strPercent));
1187                 sbPercentHbm.append(formatSimple(format, strPercentHbm));
1188 
1189                 // At 80 chars, start another row
1190                 if (sbLux.length() > 80 || (i == luxes.length - 1)) {
1191                     pw.println(sbLux);
1192                     if (!shortTermModelOnly) {
1193                         pw.println(sbNits);
1194                         pw.println(sbLong);
1195                     }
1196                     pw.println(sbShort);
1197                     pw.println(sbBrightness);
1198                     pw.println(sbPercent);
1199                     if (hbmTransition < PowerManager.BRIGHTNESS_MAX) {
1200                         pw.println(sbPercentHbm);
1201                     }
1202                     pw.println("");
1203                     needsHeaders = true;
1204                     separator = "";
1205                 }
1206             }
1207         }
1208 
toStrFloatForDump(float value)1209         private String toStrFloatForDump(float value) {
1210             if (value == 0.0f) {
1211                 return "0";
1212             } else if (value < 0.1f) {
1213                 return String.format(Locale.US, "%.3f", value);
1214             } else if (value < 1) {
1215                 return String.format(Locale.US, "%.2f", value);
1216             } else if (value < 10) {
1217                 return String.format(Locale.US, "%.1f", value);
1218             } else {
1219                 return formatSimple("%d", Math.round(value));
1220             }
1221         }
1222 
computeNitsBrightnessSplines(float[] nits)1223         private void computeNitsBrightnessSplines(float[] nits) {
1224             mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness);
1225             mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits);
1226         }
1227 
computeSpline()1228         private void computeSpline() {
1229             Pair<float[], float[]> defaultCurve = mConfig.getCurve();
1230             float[] defaultLux = defaultCurve.first;
1231             float[] defaultNits = defaultCurve.second;
1232             float[] defaultBrightness = new float[defaultNits.length];
1233             for (int i = 0; i < defaultBrightness.length; i++) {
1234                 defaultBrightness[i] = mAdjustedNitsToBrightnessSpline.interpolate(defaultNits[i]);
1235             }
1236             Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux,
1237                     mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
1238             float[] lux = curve.first;
1239             float[] brightness = curve.second;
1240             float[] nits = new float[brightness.length];
1241             for (int i = 0; i < nits.length; i++) {
1242                 nits[i] = mBrightnessToAdjustedNitsSpline.interpolate(brightness[i]);
1243             }
1244             mBrightnessSpline = Spline.createSpline(lux, nits);
1245         }
1246 
getUnadjustedBrightness(float lux)1247         private float getUnadjustedBrightness(float lux) {
1248             Pair<float[], float[]> curve = mConfig.getCurve();
1249             Spline spline = Spline.createSpline(curve.first, curve.second);
1250             return mAdjustedNitsToBrightnessSpline.interpolate(spline.interpolate(lux));
1251         }
1252 
correctBrightness(float brightness, String packageName, int category)1253         private float correctBrightness(float brightness, String packageName, int category) {
1254             if (packageName != null) {
1255                 BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
1256                 if (correction != null) {
1257                     return correction.apply(brightness);
1258                 }
1259             }
1260             if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
1261                 BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
1262                 if (correction != null) {
1263                     return correction.apply(brightness);
1264                 }
1265             }
1266             return brightness;
1267         }
1268     }
1269 }
1270