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