1 /*
2  * Copyright (C) 2023 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.config;
18 
19 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
20 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
21 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
22 
23 import android.content.Context;
24 import android.os.PowerManager;
25 import android.provider.Settings;
26 import android.util.Spline;
27 
28 import com.android.internal.display.BrightnessSynchronizer;
29 import com.android.server.display.AutomaticBrightnessController;
30 import com.android.server.display.DisplayDeviceConfig;
31 import com.android.server.display.feature.DisplayManagerFlags;
32 
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.Map;
36 
37 /**
38  * Provides a mapping between lux and brightness values in order to support auto-brightness.
39  */
40 public class DisplayBrightnessMappingConfig {
41 
42     private static final String DEFAULT_BRIGHTNESS_MAPPING_KEY =
43             AutoBrightnessModeName._default.getRawName() + "_"
44                     + AutoBrightnessSettingName.normal.getRawName();
45 
46     /**
47      * Array of desired screen brightness in nits corresponding to the lux values
48      * in the mBrightnessLevelsLuxMap.get(DEFAULT_ID) array. The display brightness is defined as
49      * the measured brightness of an all-white image. The brightness values must be non-negative and
50      * non-decreasing. This must be overridden in platform specific overlays
51      */
52     private float[] mBrightnessLevelsNits;
53 
54     /**
55      * Map of arrays of desired screen brightness corresponding to the lux values
56      * in mBrightnessLevelsLuxMap, indexed by the auto-brightness mode and the brightness preset.
57      * The brightness values must be non-negative and non-decreasing. They must be between
58      * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
59      *
60      * The keys are a concatenation of the auto-brightness mode and the brightness preset separated
61      * by an underscore, e.g. default_normal, default_dim, default_bright, doze_normal, doze_dim,
62      * doze_bright.
63      *
64      * The presets are used on devices that allow users to choose from a set of predefined options
65      * in display auto-brightness settings.
66      */
67     private final Map<String, float[]> mBrightnessLevelsMap = new HashMap<>();
68 
69     /**
70      * Map of arrays of light sensor lux values to define our levels for auto-brightness support,
71      * indexed by the auto-brightness mode and the brightness preset.
72      *
73      * The first lux value in every array is always 0.
74      *
75      * The control points must be strictly increasing. Each control point corresponds to an entry
76      * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
77      * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
78      * element of the brightness values array).
79      *
80      * Spline interpolation is used to determine the auto-brightness values for lux levels between
81      * these control points.
82      *
83      * The keys are a concatenation of the auto-brightness mode and the brightness preset separated
84      * by an underscore, e.g. default_normal, default_dim, default_bright, doze_normal, doze_dim,
85      * doze_bright.
86      *
87      * The presets are used on devices that allow users to choose from a set of predefined options
88      * in display auto-brightness settings.
89      */
90     private final Map<String, float[]> mBrightnessLevelsLuxMap = new HashMap<>();
91 
92     /**
93      * Loads the auto-brightness display brightness mappings. Internally, this takes care of
94      * loading the value from the display config, and if not present, falls back to config.xml.
95      */
DisplayBrightnessMappingConfig(Context context, DisplayManagerFlags flags, AutoBrightness autoBrightnessConfig, Spline backlightToBrightnessSpline)96     public DisplayBrightnessMappingConfig(Context context, DisplayManagerFlags flags,
97             AutoBrightness autoBrightnessConfig, Spline backlightToBrightnessSpline) {
98         if (flags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
99                 && autoBrightnessConfig.getLuxToBrightnessMapping() != null
100                 && autoBrightnessConfig.getLuxToBrightnessMapping().size() > 0) {
101             for (LuxToBrightnessMapping mapping
102                     : autoBrightnessConfig.getLuxToBrightnessMapping()) {
103                 final int size = mapping.getMap().getPoint().size();
104                 float[] brightnessLevels = new float[size];
105                 float[] brightnessLevelsLux = new float[size];
106                 for (int i = 0; i < size; i++) {
107                     float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
108                     brightnessLevels[i] = backlightToBrightnessSpline.interpolate(backlight);
109                     brightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
110                             .floatValue();
111                 }
112                 if (size == 0) {
113                     throw new IllegalArgumentException(
114                             "A display brightness mapping should not be empty");
115                 }
116                 if (brightnessLevelsLux[0] != 0) {
117                     throw new IllegalArgumentException(
118                             "The first lux value in the display brightness mapping must be 0");
119                 }
120 
121                 String key = (mapping.getMode() == null
122                         ? AutoBrightnessModeName._default.getRawName()
123                         : mapping.getMode().getRawName())
124                         + "_"
125                         + (mapping.getSetting() == null
126                         ? AutoBrightnessSettingName.normal.getRawName()
127                         : mapping.getSetting().getRawName());
128                 if (mBrightnessLevelsMap.containsKey(key)
129                         || mBrightnessLevelsLuxMap.containsKey(key)) {
130                     throw new IllegalArgumentException(
131                             "A display brightness mapping with key " + key + " already exists");
132                 }
133                 mBrightnessLevelsMap.put(key, brightnessLevels);
134                 mBrightnessLevelsLuxMap.put(key, brightnessLevelsLux);
135             }
136         }
137 
138         if (!mBrightnessLevelsMap.containsKey(DEFAULT_BRIGHTNESS_MAPPING_KEY)
139                 || !mBrightnessLevelsLuxMap.containsKey(DEFAULT_BRIGHTNESS_MAPPING_KEY)) {
140             mBrightnessLevelsNits = DisplayDeviceConfig.getFloatArray(context.getResources()
141                     .obtainTypedArray(com.android.internal.R.array
142                             .config_autoBrightnessDisplayValuesNits), PowerManager
143                     .BRIGHTNESS_OFF_FLOAT);
144 
145             float[] brightnessLevelsLux = DisplayDeviceConfig.getLuxLevels(context.getResources()
146                     .getIntArray(com.android.internal.R.array
147                             .config_autoBrightnessLevels));
148             mBrightnessLevelsLuxMap.put(DEFAULT_BRIGHTNESS_MAPPING_KEY, brightnessLevelsLux);
149 
150             // Load the old configuration in the range [0, 255]. The values need to be normalized
151             // to the range [0, 1].
152             int[] brightnessLevels = context.getResources().getIntArray(
153                     com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
154             mBrightnessLevelsMap.put(DEFAULT_BRIGHTNESS_MAPPING_KEY,
155                     brightnessArrayIntToFloat(brightnessLevels, backlightToBrightnessSpline));
156         }
157     }
158 
159     /**
160      * @param mode The auto-brightness mode
161      * @param preset The brightness preset. Presets are used on devices that allow users to choose
162      *               from a set of predefined options in display auto-brightness settings.
163      * @return The default auto-brightness brightening ambient lux levels for the specified mode
164      * and preset
165      */
getLuxArray(@utomaticBrightnessController.AutomaticBrightnessMode int mode, int preset)166     public float[] getLuxArray(@AutomaticBrightnessController.AutomaticBrightnessMode int mode,
167             int preset) {
168         float[] luxArray = mBrightnessLevelsLuxMap.get(
169                 autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset));
170         if (luxArray != null) {
171             return luxArray;
172         }
173 
174         // No array for this preset, fall back to the normal preset
175         return mBrightnessLevelsLuxMap.get(autoBrightnessModeToString(mode) + "_"
176                 + AutoBrightnessSettingName.normal.getRawName());
177     }
178 
179     /**
180      * @return Auto brightness brightening nits levels
181      */
getNitsArray()182     public float[] getNitsArray() {
183         return mBrightnessLevelsNits;
184     }
185 
186     /**
187      * @param mode The auto-brightness mode
188      * @param preset The brightness preset. Presets are used on devices that allow users to choose
189      *               from a set of predefined options in display auto-brightness settings.
190      * @return The default auto-brightness brightening levels for the specified mode and preset
191      */
getBrightnessArray( @utomaticBrightnessController.AutomaticBrightnessMode int mode, int preset)192     public float[] getBrightnessArray(
193             @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset) {
194         float[] brightnessArray = mBrightnessLevelsMap.get(
195                 autoBrightnessModeToString(mode) + "_" + autoBrightnessPresetToString(preset));
196         if (brightnessArray != null) {
197             return brightnessArray;
198         }
199 
200         // No array for this preset, fall back to the normal preset
201         return mBrightnessLevelsMap.get(autoBrightnessModeToString(mode) + "_"
202                 + AutoBrightnessSettingName.normal.getRawName());
203     }
204 
205     @Override
toString()206     public String toString() {
207         StringBuilder brightnessLevelsLuxMapString = new StringBuilder("{");
208         for (Map.Entry<String, float[]> entry : mBrightnessLevelsLuxMap.entrySet()) {
209             brightnessLevelsLuxMapString.append(entry.getKey()).append("=").append(
210                     Arrays.toString(entry.getValue())).append(", ");
211         }
212         if (brightnessLevelsLuxMapString.length() > 2) {
213             brightnessLevelsLuxMapString.delete(brightnessLevelsLuxMapString.length() - 2,
214                     brightnessLevelsLuxMapString.length());
215         }
216         brightnessLevelsLuxMapString.append("}");
217 
218         StringBuilder brightnessLevelsMapString = new StringBuilder("{");
219         for (Map.Entry<String, float[]> entry : mBrightnessLevelsMap.entrySet()) {
220             brightnessLevelsMapString.append(entry.getKey()).append("=").append(
221                     Arrays.toString(entry.getValue())).append(", ");
222         }
223         if (brightnessLevelsMapString.length() > 2) {
224             brightnessLevelsMapString.delete(brightnessLevelsMapString.length() - 2,
225                     brightnessLevelsMapString.length());
226         }
227         brightnessLevelsMapString.append("}");
228 
229         return "mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
230                 + ", mBrightnessLevelsLuxMap= " + brightnessLevelsLuxMapString
231                 + ", mBrightnessLevelsMap= " + brightnessLevelsMapString;
232     }
233 
234     /**
235      * @param mode The auto-brightness mode
236      * @return The string representing the mode
237      */
autoBrightnessModeToString( @utomaticBrightnessController.AutomaticBrightnessMode int mode)238     public static String autoBrightnessModeToString(
239             @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
240         switch (mode) {
241             case AUTO_BRIGHTNESS_MODE_DEFAULT -> {
242                 return AutoBrightnessModeName._default.getRawName();
243             }
244             case AUTO_BRIGHTNESS_MODE_IDLE -> {
245                 return AutoBrightnessModeName.idle.getRawName();
246             }
247             case AUTO_BRIGHTNESS_MODE_DOZE -> {
248                 return AutoBrightnessModeName.doze.getRawName();
249             }
250             default -> throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
251         }
252     }
253 
254     /**
255      * @param preset The brightness preset. Presets are used on devices that allow users to choose
256      *               from a set of predefined options in display auto-brightness settings.
257      * @return The string representing the preset
258      */
autoBrightnessPresetToString(int preset)259     public static String autoBrightnessPresetToString(int preset) {
260         return switch (preset) {
261             case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM ->
262                     AutoBrightnessSettingName.dim.getRawName();
263             case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL ->
264                     AutoBrightnessSettingName.normal.getRawName();
265             case Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT ->
266                     AutoBrightnessSettingName.bright.getRawName();
267             default -> throw new IllegalArgumentException(
268                     "Unknown auto-brightness preset value: " + preset);
269         };
270     }
271 
brightnessArrayIntToFloat(int[] brightnessInt, Spline backlightToBrightnessSpline)272     private float[] brightnessArrayIntToFloat(int[] brightnessInt,
273             Spline backlightToBrightnessSpline) {
274         float[] brightnessFloat = new float[brightnessInt.length];
275         for (int i = 0; i < brightnessInt.length; i++) {
276             brightnessFloat[i] = backlightToBrightnessSpline.interpolate(
277                     BrightnessSynchronizer.brightnessIntToFloat(brightnessInt[i]));
278         }
279         return brightnessFloat;
280     }
281 }
282