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