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