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 nit value to a float scale value if possible. 326 * 327 * Returns {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if there's no available mapping for 328 * the nits to float scale. 329 */ convertToFloatScale(float nits)330 public abstract float convertToFloatScale(float nits); 331 332 /** 333 * Adds a user interaction data point to the brightness mapping. 334 * 335 * This data point <b>must</b> exist on the brightness curve as a result of this call. This is 336 * so that the next time we come to query what the screen brightness should be, we get what the 337 * user requested rather than immediately changing to some other value. 338 * 339 * Currently, we only keep track of one of these at a time to constrain what can happen to the 340 * curve. 341 */ addUserDataPoint(float lux, float brightness)342 public abstract void addUserDataPoint(float lux, float brightness); 343 344 /** 345 * Removes any short term adjustments made to the curve from user interactions. 346 * 347 * Note that this does *not* reset the mapping to its initial state, any brightness 348 * configurations that have been applied will continue to be in effect. This solely removes the 349 * effects of user interactions on the model. 350 */ clearUserDataPoints()351 public abstract void clearUserDataPoints(); 352 353 /** @return True if there are any short term adjustments applied to the curve. */ hasUserDataPoints()354 public abstract boolean hasUserDataPoints(); 355 356 /** @return True if the current brightness configuration is the default one. */ isDefaultConfig()357 public abstract boolean isDefaultConfig(); 358 359 /** @return The default brightness configuration. */ getDefaultConfig()360 public abstract BrightnessConfiguration getDefaultConfig(); 361 362 /** Recalculates the backlight-to-nits and nits-to-backlight splines. */ recalculateSplines(boolean applyAdjustment, float[] adjustment)363 public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment); 364 365 /** 366 * Returns the timeout for the short term model 367 * 368 * Timeout after which we remove the effects any user interactions might've had on the 369 * brightness mapping. This timeout doesn't start until we transition to a non-interactive 370 * display policy so that we don't reset while users are using their devices, but also so that 371 * we don't erroneously keep the short-term model if the device is dozing but the 372 * display is fully on. 373 */ getShortTermModelTimeout()374 public abstract long getShortTermModelTimeout(); 375 376 /** 377 * Prints dump output for display dumpsys. 378 */ dump(PrintWriter pw, float hbmTransition)379 public abstract void dump(PrintWriter pw, float hbmTransition); 380 381 /** 382 * We can designate a mapping strategy to be used for idle screen brightness mode. 383 * @return whether this mapping strategy is to be used for idle screen brightness mode. 384 */ isForIdleMode()385 public abstract boolean isForIdleMode(); 386 getUserLux()387 abstract float getUserLux(); 388 getUserBrightness()389 abstract float getUserBrightness(); 390 391 /** 392 * Check if the short term model should be reset given the anchor lux the last 393 * brightness change was made at and the current ambient lux. 394 */ shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor)395 public boolean shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor) { 396 BrightnessConfiguration config = getBrightnessConfiguration(); 397 float minThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO; 398 float maxThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO; 399 if (config != null) { 400 if (!Float.isNaN(config.getShortTermModelLowerLuxMultiplier())) { 401 minThresholdRatio = config.getShortTermModelLowerLuxMultiplier(); 402 } 403 if (!Float.isNaN(config.getShortTermModelUpperLuxMultiplier())) { 404 maxThresholdRatio = config.getShortTermModelUpperLuxMultiplier(); 405 } 406 } 407 final float minAmbientLux = 408 shortTermModelAnchor - shortTermModelAnchor * minThresholdRatio; 409 final float maxAmbientLux = 410 shortTermModelAnchor + shortTermModelAnchor * maxThresholdRatio; 411 if (minAmbientLux < ambientLux && ambientLux <= maxAmbientLux) { 412 if (mLoggingEnabled) { 413 Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " 414 + minAmbientLux + " < " + ambientLux + " < " + maxAmbientLux); 415 } 416 return false; 417 } else { 418 Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + ambientLux 419 + "(" + minAmbientLux + ", " + maxAmbientLux + ")"); 420 return true; 421 } 422 } 423 424 // Normalize entire brightness range to 0 - 1. normalizeAbsoluteBrightness(int brightness)425 protected static float normalizeAbsoluteBrightness(int brightness) { 426 return BrightnessSynchronizer.brightnessIntToFloat(brightness); 427 } 428 insertControlPoint( float[] luxLevels, float[] brightnessLevels, float lux, float brightness)429 private Pair<float[], float[]> insertControlPoint( 430 float[] luxLevels, float[] brightnessLevels, float lux, float brightness) { 431 final int idx = findInsertionPoint(luxLevels, lux); 432 final float[] newLuxLevels; 433 final float[] newBrightnessLevels; 434 if (idx == luxLevels.length) { 435 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); 436 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); 437 newLuxLevels[idx] = lux; 438 newBrightnessLevels[idx] = brightness; 439 } else if (luxLevels[idx] == lux) { 440 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length); 441 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length); 442 newBrightnessLevels[idx] = brightness; 443 } else { 444 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); 445 System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+1, luxLevels.length - idx); 446 newLuxLevels[idx] = lux; 447 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); 448 System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+1, 449 brightnessLevels.length - idx); 450 newBrightnessLevels[idx] = brightness; 451 } 452 smoothCurve(newLuxLevels, newBrightnessLevels, idx); 453 return Pair.create(newLuxLevels, newBrightnessLevels); 454 } 455 456 /** 457 * Returns the index of the first value that's less than or equal to {@code val}. 458 * 459 * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater 460 * than val, then it will return the length of arr as the insertion point. 461 */ findInsertionPoint(float[] arr, float val)462 private int findInsertionPoint(float[] arr, float val) { 463 for (int i = 0; i < arr.length; i++) { 464 if (val <= arr[i]) { 465 return i; 466 } 467 } 468 return arr.length; 469 } 470 smoothCurve(float[] lux, float[] brightness, int idx)471 private void smoothCurve(float[] lux, float[] brightness, int idx) { 472 if (mLoggingEnabled) { 473 PLOG.logCurve("unsmoothed curve", lux, brightness); 474 } 475 float prevLux = lux[idx]; 476 float prevBrightness = brightness[idx]; 477 // Smooth curve for data points above the newly introduced point 478 for (int i = idx+1; i < lux.length; i++) { 479 float currLux = lux[i]; 480 float currBrightness = brightness[i]; 481 float maxBrightness = MathUtils.max( 482 prevBrightness * permissibleRatio(currLux, prevLux), 483 prevBrightness + MIN_PERMISSABLE_INCREASE); 484 float newBrightness = MathUtils.constrain( 485 currBrightness, prevBrightness, maxBrightness); 486 if (newBrightness == currBrightness) { 487 break; 488 } 489 prevLux = currLux; 490 prevBrightness = newBrightness; 491 brightness[i] = newBrightness; 492 } 493 // Smooth curve for data points below the newly introduced point 494 prevLux = lux[idx]; 495 prevBrightness = brightness[idx]; 496 for (int i = idx-1; i >= 0; i--) { 497 float currLux = lux[i]; 498 float currBrightness = brightness[i]; 499 float minBrightness = prevBrightness * permissibleRatio(currLux, prevLux); 500 float newBrightness = MathUtils.constrain( 501 currBrightness, minBrightness, prevBrightness); 502 if (newBrightness == currBrightness) { 503 break; 504 } 505 prevLux = currLux; 506 prevBrightness = newBrightness; 507 brightness[i] = newBrightness; 508 } 509 if (mLoggingEnabled) { 510 PLOG.logCurve("smoothed curve", lux, brightness); 511 } 512 } 513 permissibleRatio(float currLux, float prevLux)514 private float permissibleRatio(float currLux, float prevLux) { 515 return MathUtils.pow((currLux + LUX_GRAD_SMOOTHING) 516 / (prevLux + LUX_GRAD_SMOOTHING), MAX_GRAD); 517 } 518 inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness, float currentBrightness)519 protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness, 520 float currentBrightness) { 521 float adjustment = 0; 522 float gamma = Float.NaN; 523 // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges 524 // affects the curve rather drastically. 525 if (currentBrightness <= 0.1f || currentBrightness >= 0.9f) { 526 adjustment = (desiredBrightness - currentBrightness); 527 // Edge case: darkest adjustment possible. 528 } else if (desiredBrightness == 0) { 529 adjustment = -1; 530 // Edge case: brightest adjustment possible. 531 } else if (desiredBrightness == 1) { 532 adjustment = +1; 533 } else { 534 // current^gamma = desired => gamma = log[current](desired) 535 gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness); 536 // max^-adjustment = gamma => adjustment = -log[max](gamma) 537 adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma); 538 } 539 adjustment = MathUtils.constrain(adjustment, -1, +1); 540 if (mLoggingEnabled) { 541 Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" + 542 MathUtils.pow(maxGamma, -adjustment) + " == " + gamma); 543 Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" + 544 MathUtils.pow(currentBrightness, gamma) + " == " + desiredBrightness); 545 } 546 return adjustment; 547 } 548 getAdjustedCurve(float[] lux, float[] brightness, float userLux, float userBrightness, float adjustment, float maxGamma)549 protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness, 550 float userLux, float userBrightness, float adjustment, float maxGamma) { 551 float[] newLux = lux; 552 float[] newBrightness = Arrays.copyOf(brightness, brightness.length); 553 if (mLoggingEnabled) { 554 PLOG.logCurve("unadjusted curve", newLux, newBrightness); 555 } 556 adjustment = MathUtils.constrain(adjustment, -1, 1); 557 float gamma = MathUtils.pow(maxGamma, -adjustment); 558 if (mLoggingEnabled) { 559 Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" + 560 MathUtils.pow(maxGamma, -adjustment) + " == " + gamma); 561 } 562 if (gamma != 1) { 563 for (int i = 0; i < newBrightness.length; i++) { 564 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma); 565 } 566 } 567 if (mLoggingEnabled) { 568 PLOG.logCurve("gamma adjusted curve", newLux, newBrightness); 569 } 570 if (userLux != -1) { 571 Pair<float[], float[]> curve = insertControlPoint(newLux, newBrightness, userLux, 572 userBrightness); 573 newLux = curve.first; 574 newBrightness = curve.second; 575 if (mLoggingEnabled) { 576 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness); 577 // This is done for comparison. 578 curve = insertControlPoint(lux, brightness, userLux, userBrightness); 579 PLOG.logCurve("user adjusted curve", curve.first ,curve.second); 580 } 581 } 582 return Pair.create(newLux, newBrightness); 583 } 584 585 /** 586 * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the 587 * backlight of the display. 588 * 589 * Since we don't have information about the display's physical brightness, any brightness 590 * configurations that are set are just ignored. 591 */ 592 private static class SimpleMappingStrategy extends BrightnessMappingStrategy { 593 // Lux control points 594 private final float[] mLux; 595 // Brightness control points normalized to [0, 1] 596 private final float[] mBrightness; 597 598 private Spline mSpline; 599 private float mMaxGamma; 600 private float mAutoBrightnessAdjustment; 601 private float mUserLux; 602 private float mUserBrightness; 603 private long mShortTermModelTimeout; 604 SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma, long timeout)605 private SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma, 606 long timeout) { 607 Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, 608 "Lux and brightness arrays must not be empty!"); 609 Preconditions.checkArgument(lux.length == brightness.length, 610 "Lux and brightness arrays must be the same length!"); 611 Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); 612 Preconditions.checkArrayElementsInRange(brightness, 613 0, Integer.MAX_VALUE, "brightness"); 614 615 final int N = brightness.length; 616 mLux = new float[N]; 617 mBrightness = new float[N]; 618 for (int i = 0; i < N; i++) { 619 mLux[i] = lux[i]; 620 mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]); 621 } 622 623 mMaxGamma = maxGamma; 624 mAutoBrightnessAdjustment = 0; 625 mUserLux = NO_USER_LUX; 626 mUserBrightness = NO_USER_BRIGHTNESS; 627 if (mLoggingEnabled) { 628 PLOG.start("simple mapping strategy"); 629 } 630 computeSpline(); 631 mShortTermModelTimeout = timeout; 632 } 633 634 @Override getShortTermModelTimeout()635 public long getShortTermModelTimeout() { 636 return mShortTermModelTimeout; 637 } 638 639 @Override setBrightnessConfiguration(@ullable BrightnessConfiguration config)640 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 641 return false; 642 } 643 644 @Override getBrightnessConfiguration()645 public BrightnessConfiguration getBrightnessConfiguration() { 646 return null; 647 } 648 649 @Override getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)650 public float getBrightness(float lux, String packageName, 651 @ApplicationInfo.Category int category) { 652 return mSpline.interpolate(lux); 653 } 654 655 @Override getAutoBrightnessAdjustment()656 public float getAutoBrightnessAdjustment() { 657 return mAutoBrightnessAdjustment; 658 } 659 660 @Override setAutoBrightnessAdjustment(float adjustment)661 public boolean setAutoBrightnessAdjustment(float adjustment) { 662 adjustment = MathUtils.constrain(adjustment, -1, 1); 663 if (adjustment == mAutoBrightnessAdjustment) { 664 return false; 665 } 666 if (mLoggingEnabled) { 667 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 668 adjustment); 669 PLOG.start("auto-brightness adjustment"); 670 } 671 mAutoBrightnessAdjustment = adjustment; 672 computeSpline(); 673 return true; 674 } 675 676 @Override convertToNits(float brightness)677 public float convertToNits(float brightness) { 678 return -1.0f; 679 } 680 681 @Override convertToFloatScale(float nits)682 public float convertToFloatScale(float nits) { 683 return PowerManager.BRIGHTNESS_INVALID_FLOAT; 684 } 685 686 @Override addUserDataPoint(float lux, float brightness)687 public void addUserDataPoint(float lux, float brightness) { 688 float unadjustedBrightness = getUnadjustedBrightness(lux); 689 if (mLoggingEnabled) { 690 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 691 PLOG.start("add user data point") 692 .logPoint("user data point", lux, brightness) 693 .logPoint("current brightness", lux, unadjustedBrightness); 694 } 695 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 696 brightness /* desiredBrightness */, 697 unadjustedBrightness /* currentBrightness */); 698 if (mLoggingEnabled) { 699 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 700 adjustment); 701 } 702 mAutoBrightnessAdjustment = adjustment; 703 mUserLux = lux; 704 mUserBrightness = brightness; 705 computeSpline(); 706 } 707 708 @Override clearUserDataPoints()709 public void clearUserDataPoints() { 710 if (mUserLux != -1) { 711 if (mLoggingEnabled) { 712 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 713 PLOG.start("clear user data points") 714 .logPoint("user data point", mUserLux, mUserBrightness); 715 } 716 mAutoBrightnessAdjustment = 0; 717 mUserLux = -1; 718 mUserBrightness = -1; 719 computeSpline(); 720 } 721 } 722 723 @Override hasUserDataPoints()724 public boolean hasUserDataPoints() { 725 return mUserLux != -1; 726 } 727 728 @Override isDefaultConfig()729 public boolean isDefaultConfig() { 730 return true; 731 } 732 733 @Override getDefaultConfig()734 public BrightnessConfiguration getDefaultConfig() { 735 return null; 736 } 737 738 @Override recalculateSplines(boolean applyAdjustment, float[] adjustment)739 public void recalculateSplines(boolean applyAdjustment, float[] adjustment) { 740 // Do nothing. 741 } 742 743 @Override dump(PrintWriter pw, float hbmTransition)744 public void dump(PrintWriter pw, float hbmTransition) { 745 pw.println("SimpleMappingStrategy"); 746 pw.println(" mSpline=" + mSpline); 747 pw.println(" mMaxGamma=" + mMaxGamma); 748 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 749 pw.println(" mUserLux=" + mUserLux); 750 pw.println(" mUserBrightness=" + mUserBrightness); 751 } 752 753 @Override isForIdleMode()754 public boolean isForIdleMode() { 755 return false; 756 } 757 758 @Override getUserLux()759 float getUserLux() { 760 return mUserLux; 761 } 762 763 @Override getUserBrightness()764 float getUserBrightness() { 765 return mUserBrightness; 766 } 767 computeSpline()768 private void computeSpline() { 769 Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux, 770 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 771 mSpline = Spline.createSpline(curve.first, curve.second); 772 } 773 getUnadjustedBrightness(float lux)774 private float getUnadjustedBrightness(float lux) { 775 Spline spline = Spline.createSpline(mLux, mBrightness); 776 return spline.interpolate(lux); 777 } 778 } 779 780 /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical 781 * range of the display, rather than to the range of the backlight control (typically 0-255). 782 * 783 * By mapping through the physical brightness, the curve becomes portable across devices and 784 * gives us more resolution in the resulting mapping. 785 */ 786 @VisibleForTesting 787 static class PhysicalMappingStrategy extends BrightnessMappingStrategy { 788 // The current brightness configuration. 789 private BrightnessConfiguration mConfig; 790 791 // A spline mapping from the current ambient light in lux to the desired display brightness 792 // in nits. 793 private Spline mBrightnessSpline; 794 795 // A spline mapping from nits to the corresponding brightness value, normalized to the range 796 // [0, 1.0]. 797 private Spline mNitsToBrightnessSpline; 798 799 // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to 800 // a brightness in nits. 801 private Spline mBrightnessToNitsSpline; 802 803 // The default brightness configuration. 804 private final BrightnessConfiguration mDefaultConfig; 805 806 private final float[] mNits; 807 private final float[] mBrightness; 808 809 private boolean mBrightnessRangeAdjustmentApplied; 810 811 private final float mMaxGamma; 812 private float mAutoBrightnessAdjustment; 813 private float mUserLux; 814 private float mUserBrightness; 815 private final boolean mIsForIdleMode; 816 private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; 817 PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, float[] brightness, float maxGamma, boolean isForIdleMode, DisplayWhiteBalanceController displayWhiteBalanceController)818 public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, 819 float[] brightness, float maxGamma, boolean isForIdleMode, 820 DisplayWhiteBalanceController displayWhiteBalanceController) { 821 822 Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, 823 "Nits and brightness arrays must not be empty!"); 824 825 Preconditions.checkArgument(nits.length == brightness.length, 826 "Nits and brightness arrays must be the same length!"); 827 Objects.requireNonNull(config); 828 Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); 829 Preconditions.checkArrayElementsInRange(brightness, 830 PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness"); 831 832 mIsForIdleMode = isForIdleMode; 833 mMaxGamma = maxGamma; 834 mAutoBrightnessAdjustment = 0; 835 mUserLux = NO_USER_LUX; 836 mUserBrightness = NO_USER_BRIGHTNESS; 837 mDisplayWhiteBalanceController = displayWhiteBalanceController; 838 839 mNits = nits; 840 mBrightness = brightness; 841 computeNitsBrightnessSplines(mNits); 842 843 mDefaultConfig = config; 844 if (mLoggingEnabled) { 845 PLOG.start("physical mapping strategy"); 846 } 847 mConfig = config; 848 computeSpline(); 849 } 850 851 @Override getShortTermModelTimeout()852 public long getShortTermModelTimeout() { 853 if (mConfig.getShortTermModelTimeoutMillis() >= 0) { 854 return mConfig.getShortTermModelTimeoutMillis(); 855 } else { 856 return mDefaultConfig.getShortTermModelTimeoutMillis(); 857 } 858 } 859 860 @Override setBrightnessConfiguration(@ullable BrightnessConfiguration config)861 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 862 if (config == null) { 863 config = mDefaultConfig; 864 } 865 if (config.equals(mConfig)) { 866 return false; 867 } 868 if (mLoggingEnabled) { 869 PLOG.start("brightness configuration"); 870 } 871 mConfig = config; 872 computeSpline(); 873 return true; 874 } 875 876 @Override getBrightnessConfiguration()877 public BrightnessConfiguration getBrightnessConfiguration() { 878 return mConfig; 879 } 880 881 @Override getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)882 public float getBrightness(float lux, String packageName, 883 @ApplicationInfo.Category int category) { 884 float nits = mBrightnessSpline.interpolate(lux); 885 886 // Adjust nits to compensate for display white balance colour strength. 887 if (mDisplayWhiteBalanceController != null) { 888 nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits); 889 } 890 891 float brightness = mNitsToBrightnessSpline.interpolate(nits); 892 // Correct the brightness according to the current application and its category, but 893 // only if no user data point is set (as this will override the user setting). 894 if (mUserLux == -1) { 895 brightness = correctBrightness(brightness, packageName, category); 896 } else if (mLoggingEnabled) { 897 Slog.d(TAG, "user point set, correction not applied"); 898 } 899 return brightness; 900 } 901 902 @Override getAutoBrightnessAdjustment()903 public float getAutoBrightnessAdjustment() { 904 return mAutoBrightnessAdjustment; 905 } 906 907 @Override setAutoBrightnessAdjustment(float adjustment)908 public boolean setAutoBrightnessAdjustment(float adjustment) { 909 adjustment = MathUtils.constrain(adjustment, -1, 1); 910 if (adjustment == mAutoBrightnessAdjustment) { 911 return false; 912 } 913 if (mLoggingEnabled) { 914 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 915 adjustment); 916 PLOG.start("auto-brightness adjustment"); 917 } 918 mAutoBrightnessAdjustment = adjustment; 919 computeSpline(); 920 return true; 921 } 922 923 @Override convertToNits(float brightness)924 public float convertToNits(float brightness) { 925 return mBrightnessToNitsSpline.interpolate(brightness); 926 } 927 928 @Override convertToFloatScale(float nits)929 public float convertToFloatScale(float nits) { 930 return mNitsToBrightnessSpline.interpolate(nits); 931 } 932 933 @Override addUserDataPoint(float lux, float brightness)934 public void addUserDataPoint(float lux, float brightness) { 935 float unadjustedBrightness = getUnadjustedBrightness(lux); 936 if (mLoggingEnabled) { 937 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 938 PLOG.start("add user data point") 939 .logPoint("user data point", lux, brightness) 940 .logPoint("current brightness", lux, unadjustedBrightness); 941 } 942 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 943 brightness /* desiredBrightness */, 944 unadjustedBrightness /* currentBrightness */); 945 if (mLoggingEnabled) { 946 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 947 adjustment); 948 } 949 mAutoBrightnessAdjustment = adjustment; 950 mUserLux = lux; 951 mUserBrightness = brightness; 952 computeSpline(); 953 } 954 955 @Override clearUserDataPoints()956 public void clearUserDataPoints() { 957 if (mUserLux != -1) { 958 if (mLoggingEnabled) { 959 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 960 PLOG.start("clear user data points") 961 .logPoint("user data point", mUserLux, mUserBrightness); 962 } 963 mAutoBrightnessAdjustment = 0; 964 mUserLux = -1; 965 mUserBrightness = -1; 966 computeSpline(); 967 } 968 } 969 970 @Override hasUserDataPoints()971 public boolean hasUserDataPoints() { 972 return mUserLux != -1; 973 } 974 975 @Override isDefaultConfig()976 public boolean isDefaultConfig() { 977 return mDefaultConfig.equals(mConfig); 978 } 979 980 @Override getDefaultConfig()981 public BrightnessConfiguration getDefaultConfig() { 982 return mDefaultConfig; 983 } 984 985 @Override recalculateSplines(boolean applyAdjustment, float[] adjustedNits)986 public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) { 987 mBrightnessRangeAdjustmentApplied = applyAdjustment; 988 computeNitsBrightnessSplines(mBrightnessRangeAdjustmentApplied ? adjustedNits : mNits); 989 } 990 991 @Override dump(PrintWriter pw, float hbmTransition)992 public void dump(PrintWriter pw, float hbmTransition) { 993 pw.println("PhysicalMappingStrategy"); 994 pw.println(" mConfig=" + mConfig); 995 pw.println(" mBrightnessSpline=" + mBrightnessSpline); 996 pw.println(" mNitsToBrightnessSpline=" + mNitsToBrightnessSpline); 997 pw.println(" mBrightnessToNitsSpline=" + mBrightnessToNitsSpline); 998 pw.println(" mMaxGamma=" + mMaxGamma); 999 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 1000 pw.println(" mUserLux=" + mUserLux); 1001 pw.println(" mUserBrightness=" + mUserBrightness); 1002 pw.println(" mDefaultConfig=" + mDefaultConfig); 1003 pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied); 1004 1005 dumpConfigDiff(pw, hbmTransition); 1006 } 1007 1008 @Override isForIdleMode()1009 public boolean isForIdleMode() { 1010 return mIsForIdleMode; 1011 } 1012 1013 @Override getUserLux()1014 float getUserLux() { 1015 return mUserLux; 1016 } 1017 1018 @Override getUserBrightness()1019 float getUserBrightness() { 1020 return mUserBrightness; 1021 } 1022 1023 /** 1024 * Prints out the default curve and how it differs from the long-term curve 1025 * and the current curve (in case the current curve includes short-term adjustments). 1026 * 1027 * @param pw The print-writer to write to. 1028 */ dumpConfigDiff(PrintWriter pw, float hbmTransition)1029 private void dumpConfigDiff(PrintWriter pw, float hbmTransition) { 1030 pw.println(" Difference between current config and default: "); 1031 1032 Pair<float[], float[]> currentCurve = mConfig.getCurve(); 1033 Spline currSpline = Spline.createSpline(currentCurve.first, currentCurve.second); 1034 1035 Pair<float[], float[]> defaultCurve = mDefaultConfig.getCurve(); 1036 Spline defaultSpline = Spline.createSpline(defaultCurve.first, defaultCurve.second); 1037 1038 // Add the short-term curve lux point if present 1039 float[] luxes = currentCurve.first; 1040 if (mUserLux >= 0) { 1041 luxes = Arrays.copyOf(currentCurve.first, currentCurve.first.length + 1); 1042 luxes[luxes.length - 1] = mUserLux; 1043 Arrays.sort(luxes); 1044 } 1045 1046 StringBuilder sbLux = null; 1047 StringBuilder sbNits = null; 1048 StringBuilder sbLong = null; 1049 StringBuilder sbShort = null; 1050 StringBuilder sbBrightness = null; 1051 StringBuilder sbPercent = null; 1052 StringBuilder sbPercentHbm = null; 1053 boolean needsHeaders = true; 1054 String separator = ""; 1055 for (int i = 0; i < luxes.length; i++) { 1056 float lux = luxes[i]; 1057 if (needsHeaders) { 1058 sbLux = new StringBuilder(" lux: "); 1059 sbNits = new StringBuilder(" default: "); 1060 sbLong = new StringBuilder(" long-term: "); 1061 sbShort = new StringBuilder(" current: "); 1062 sbBrightness = new StringBuilder(" current(bl): "); 1063 sbPercent = new StringBuilder(" current(%): "); 1064 sbPercentHbm = new StringBuilder(" current(hbm%): "); 1065 needsHeaders = false; 1066 } 1067 1068 float defaultNits = defaultSpline.interpolate(lux); 1069 float longTermNits = currSpline.interpolate(lux); 1070 float shortTermNits = mBrightnessSpline.interpolate(lux); 1071 float brightness = mNitsToBrightnessSpline.interpolate(shortTermNits); 1072 1073 String luxPrefix = (lux == mUserLux ? "^" : ""); 1074 String strLux = luxPrefix + toStrFloatForDump(lux); 1075 String strNits = toStrFloatForDump(defaultNits); 1076 String strLong = toStrFloatForDump(longTermNits); 1077 String strShort = toStrFloatForDump(shortTermNits); 1078 String strBrightness = toStrFloatForDump(brightness); 1079 String strPercent = String.valueOf( 1080 Math.round(100.0f * BrightnessUtils.convertLinearToGamma( 1081 (brightness / hbmTransition)))); 1082 String strPercentHbm = String.valueOf( 1083 Math.round(100.0f * BrightnessUtils.convertLinearToGamma(brightness))); 1084 1085 int maxLen = Math.max(strLux.length(), 1086 Math.max(strNits.length(), 1087 Math.max(strBrightness.length(), 1088 Math.max(strPercent.length(), 1089 Math.max(strPercentHbm.length(), 1090 Math.max(strLong.length(), strShort.length())))))); 1091 String format = separator + "%" + maxLen + "s"; 1092 separator = ", "; 1093 1094 sbLux.append(formatSimple(format, strLux)); 1095 sbNits.append(formatSimple(format, strNits)); 1096 sbLong.append(formatSimple(format, strLong)); 1097 sbShort.append(formatSimple(format, strShort)); 1098 sbBrightness.append(formatSimple(format, strBrightness)); 1099 sbPercent.append(formatSimple(format, strPercent)); 1100 sbPercentHbm.append(formatSimple(format, strPercentHbm)); 1101 1102 // At 80 chars, start another row 1103 if (sbLux.length() > 80 || (i == luxes.length - 1)) { 1104 pw.println(sbLux); 1105 pw.println(sbNits); 1106 pw.println(sbLong); 1107 pw.println(sbShort); 1108 pw.println(sbBrightness); 1109 pw.println(sbPercent); 1110 if (hbmTransition < PowerManager.BRIGHTNESS_MAX) { 1111 pw.println(sbPercentHbm); 1112 } 1113 pw.println(""); 1114 needsHeaders = true; 1115 separator = ""; 1116 } 1117 } 1118 } 1119 toStrFloatForDump(float value)1120 private String toStrFloatForDump(float value) { 1121 if (value == 0.0f) { 1122 return "0"; 1123 } else if (value < 0.1f) { 1124 return String.format(Locale.US, "%.3f", value); 1125 } else if (value < 1) { 1126 return String.format(Locale.US, "%.2f", value); 1127 } else if (value < 10) { 1128 return String.format(Locale.US, "%.1f", value); 1129 } else { 1130 return formatSimple("%d", Math.round(value)); 1131 } 1132 } 1133 computeNitsBrightnessSplines(float[] nits)1134 private void computeNitsBrightnessSplines(float[] nits) { 1135 mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness); 1136 mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits); 1137 } 1138 computeSpline()1139 private void computeSpline() { 1140 Pair<float[], float[]> defaultCurve = mConfig.getCurve(); 1141 float[] defaultLux = defaultCurve.first; 1142 float[] defaultNits = defaultCurve.second; 1143 float[] defaultBrightness = new float[defaultNits.length]; 1144 for (int i = 0; i < defaultBrightness.length; i++) { 1145 defaultBrightness[i] = mNitsToBrightnessSpline.interpolate(defaultNits[i]); 1146 } 1147 Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux, 1148 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 1149 float[] lux = curve.first; 1150 float[] brightness = curve.second; 1151 float[] nits = new float[brightness.length]; 1152 for (int i = 0; i < nits.length; i++) { 1153 nits[i] = mBrightnessToNitsSpline.interpolate(brightness[i]); 1154 } 1155 mBrightnessSpline = Spline.createSpline(lux, nits); 1156 } 1157 getUnadjustedBrightness(float lux)1158 private float getUnadjustedBrightness(float lux) { 1159 Pair<float[], float[]> curve = mConfig.getCurve(); 1160 Spline spline = Spline.createSpline(curve.first, curve.second); 1161 return mNitsToBrightnessSpline.interpolate(spline.interpolate(lux)); 1162 } 1163 correctBrightness(float brightness, String packageName, int category)1164 private float correctBrightness(float brightness, String packageName, int category) { 1165 if (packageName != null) { 1166 BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName); 1167 if (correction != null) { 1168 return correction.apply(brightness); 1169 } 1170 } 1171 if (category != ApplicationInfo.CATEGORY_UNDEFINED) { 1172 BrightnessCorrection correction = mConfig.getCorrectionByCategory(category); 1173 if (correction != null) { 1174 return correction.apply(brightness); 1175 } 1176 } 1177 return brightness; 1178 } 1179 } 1180 } 1181