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 static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; 22 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; 23 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; 24 25 import android.annotation.Nullable; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.res.TypedArray; 29 import android.hardware.display.BrightnessConfiguration; 30 import android.hardware.display.BrightnessCorrection; 31 import android.os.PowerManager; 32 import android.os.UserHandle; 33 import android.provider.Settings; 34 import android.util.LongArray; 35 import android.util.MathUtils; 36 import android.util.Pair; 37 import android.util.Slog; 38 import android.util.Spline; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.display.BrightnessUtils; 42 import com.android.internal.util.Preconditions; 43 import com.android.server.display.utils.Plog; 44 import com.android.server.display.whitebalance.DisplayWhiteBalanceController; 45 46 import java.io.PrintWriter; 47 import java.text.SimpleDateFormat; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.Date; 51 import java.util.List; 52 import java.util.Locale; 53 import java.util.Objects; 54 55 /** 56 * A utility to map from an ambient brightness to a display's "backlight" brightness based on the 57 * available display information and brightness configuration. 58 * 59 * Note that without a mapping from the nits to a display backlight level, any 60 * {@link BrightnessConfiguration}s that are set are just ignored. 61 */ 62 public abstract class BrightnessMappingStrategy { 63 private static final String TAG = "BrightnessMappingStrategy"; 64 65 public static final float INVALID_LUX = -1; 66 public static final float INVALID_NITS = -1; 67 68 private static final float LUX_GRAD_SMOOTHING = 0.25f; 69 private static final float MAX_GRAD = 1.0f; 70 private static final float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f; 71 72 // Constant that ensures that each step of the curve can increase by up to at least 73 // MIN_PERMISSABLE_INCREASE. Otherwise when the brightness is set to 0, the curve will never 74 // increase and will always be 0. 75 private static final float MIN_PERMISSABLE_INCREASE = 0.004f; 76 77 protected boolean mLoggingEnabled; 78 79 private static final Plog PLOG = Plog.createSystemPlog(TAG); 80 81 /** 82 * Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle 83 * mode. 84 * 85 * @param context 86 * @param displayDeviceConfig 87 * @param mode The auto-brightness mode. Different modes use different brightness curves 88 * @param displayWhiteBalanceController 89 * @return the BrightnessMappingStrategy 90 */ 91 @Nullable create(Context context, DisplayDeviceConfig displayDeviceConfig, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, @Nullable DisplayWhiteBalanceController displayWhiteBalanceController)92 static BrightnessMappingStrategy create(Context context, 93 DisplayDeviceConfig displayDeviceConfig, 94 @AutomaticBrightnessController.AutomaticBrightnessMode int mode, 95 @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) { 96 97 // Display independent, mode dependent values 98 float[] brightnessLevelsNits = null; 99 float[] brightnessLevels = null; 100 float[] luxLevels = null; 101 int preset = Settings.System.getIntForUser(context.getContentResolver(), 102 Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, 103 Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); 104 switch (mode) { 105 case AUTO_BRIGHTNESS_MODE_DEFAULT -> { 106 brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(); 107 luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); 108 brightnessLevels = 109 displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); 110 } 111 case AUTO_BRIGHTNESS_MODE_IDLE -> { 112 brightnessLevelsNits = getFloatArray(context.getResources().obtainTypedArray( 113 com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)); 114 luxLevels = getLuxLevels(context.getResources().getIntArray( 115 com.android.internal.R.array.config_autoBrightnessLevelsIdle)); 116 } 117 case AUTO_BRIGHTNESS_MODE_DOZE -> { 118 luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); 119 brightnessLevels = 120 displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); 121 } 122 } 123 124 // Display independent, mode independent values 125 float autoBrightnessAdjustmentMaxGamma = context.getResources().getFraction( 126 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 127 1, 1); 128 long shortTermModelTimeout = context.getResources().getInteger( 129 com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); 130 131 // Display dependent values - used for physical mapping strategy nits -> brightness 132 final float[] nitsRange = displayDeviceConfig.getNits(); 133 final float[] brightnessRange = displayDeviceConfig.getBrightness(); 134 135 if (isValidMapping(nitsRange, brightnessRange) 136 && isValidMapping(luxLevels, brightnessLevelsNits)) { 137 BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( 138 luxLevels, brightnessLevelsNits); 139 builder.setShortTermModelTimeoutMillis(shortTermModelTimeout); 140 builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); 141 builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); 142 return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange, 143 autoBrightnessAdjustmentMaxGamma, mode, displayWhiteBalanceController); 144 } else if (isValidMapping(luxLevels, brightnessLevels)) { 145 return new SimpleMappingStrategy(luxLevels, brightnessLevels, 146 autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout, mode); 147 } else { 148 return null; 149 } 150 } 151 getLuxLevels(int[] lux)152 private static float[] getLuxLevels(int[] lux) { 153 // The first control point is implicit and always at 0 lux. 154 float[] levels = new float[lux.length + 1]; 155 for (int i = 0; i < lux.length; i++) { 156 levels[i + 1] = (float) lux[i]; 157 } 158 return levels; 159 } 160 161 /** 162 * Extracts a float array from the specified {@link TypedArray}. 163 * 164 * @param array The array to convert. 165 * @return the given array as a float array. 166 */ getFloatArray(TypedArray array)167 public static float[] getFloatArray(TypedArray array) { 168 final int N = array.length(); 169 float[] vals = new float[N]; 170 for (int i = 0; i < N; i++) { 171 vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT); 172 } 173 array.recycle(); 174 return vals; 175 } 176 isValidMapping(float[] x, float[] y)177 private static boolean isValidMapping(float[] x, float[] y) { 178 if (x == null || y == null || x.length == 0 || y.length == 0) { 179 return false; 180 } 181 if (x.length != y.length) { 182 return false; 183 } 184 final int N = x.length; 185 float prevX = x[0]; 186 float prevY = y[0]; 187 if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) { 188 return false; 189 } 190 for (int i = 1; i < N; i++) { 191 if (prevX >= x[i] || prevY > y[i]) { 192 return false; 193 } 194 if (Float.isNaN(x[i]) || Float.isNaN(y[i])) { 195 return false; 196 } 197 prevX = x[i]; 198 prevY = y[i]; 199 } 200 return true; 201 } 202 isValidMapping(float[] x, int[] y)203 private static boolean isValidMapping(float[] x, int[] y) { 204 if (x == null || y == null || x.length == 0 || y.length == 0) { 205 return false; 206 } 207 if (x.length != y.length) { 208 return false; 209 } 210 final int N = x.length; 211 float prevX = x[0]; 212 int prevY = y[0]; 213 if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) { 214 return false; 215 } 216 for (int i = 1; i < N; i++) { 217 if (prevX >= x[i] || prevY > y[i]) { 218 return false; 219 } 220 if (Float.isNaN(x[i])) { 221 return false; 222 } 223 prevX = x[i]; 224 prevY = y[i]; 225 } 226 return true; 227 } 228 229 /** 230 * Enable/disable logging. 231 * 232 * @param loggingEnabled 233 * Whether logging should be on/off. 234 * 235 * @return Whether the method succeeded or not. 236 */ setLoggingEnabled(boolean loggingEnabled)237 public boolean setLoggingEnabled(boolean loggingEnabled) { 238 if (mLoggingEnabled == loggingEnabled) { 239 return false; 240 } 241 mLoggingEnabled = loggingEnabled; 242 return true; 243 } 244 245 /** 246 * Sets the {@link BrightnessConfiguration}. 247 * 248 * @param config The new configuration. If {@code null} is passed, the default configuration is 249 * used. 250 * @return Whether the brightness configuration has changed. 251 */ setBrightnessConfiguration(@ullable BrightnessConfiguration config)252 public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config); 253 254 /** 255 * Gets the current {@link BrightnessConfiguration}. 256 */ 257 @Nullable getBrightnessConfiguration()258 public abstract BrightnessConfiguration getBrightnessConfiguration(); 259 260 /** 261 * Returns the desired brightness of the display based on the current ambient lux, including 262 * any context-related corrections. 263 * 264 * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max 265 * brightness and 0 is the display at minimum brightness. 266 * 267 * @param lux The current ambient brightness in lux. 268 * @param packageName the foreground app package name. 269 * @param category the foreground app package category. 270 * @return The desired brightness of the display normalized to the range [0, 1.0]. 271 */ getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)272 public abstract float getBrightness(float lux, String packageName, 273 @ApplicationInfo.Category int category); 274 275 /** 276 * Returns the desired brightness of the display based on the current ambient lux. 277 * 278 * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max 279 * brightness and 0 is the display at minimum brightness. 280 * 281 * @param lux The current ambient brightness in lux. 282 * 283 * @return The desired brightness of the display normalized to the range [0, 1.0]. 284 */ getBrightness(float lux)285 public float getBrightness(float lux) { 286 return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED); 287 } 288 289 /** 290 * Returns the current auto-brightness adjustment. 291 * 292 * The returned adjustment is a value in the range [-1.0, 1.0] such that 293 * {@code config_autoBrightnessAdjustmentMaxGamma<sup>-adjustment</sup>} is used to gamma 294 * correct the brightness curve. 295 */ getAutoBrightnessAdjustment()296 public abstract float getAutoBrightnessAdjustment(); 297 298 /** 299 * Sets the auto-brightness adjustment. 300 * 301 * @param adjustment The desired auto-brightness adjustment. 302 * @return Whether the auto-brightness adjustment has changed. 303 * 304 * @Deprecated The auto-brightness adjustment should not be set directly, but rather inferred 305 * from user data points. 306 */ setAutoBrightnessAdjustment(float adjustment)307 public abstract boolean setAutoBrightnessAdjustment(float adjustment); 308 309 /** 310 * Converts the provided brightness value to nits if possible. 311 * 312 * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits. 313 */ convertToNits(float brightness)314 public abstract float convertToNits(float brightness); 315 316 /** 317 * Converts the provided brightness value to nits if possible. Adjustments, such as RBC are 318 * applied. 319 * 320 * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits. 321 */ convertToAdjustedNits(float brightness)322 public abstract float convertToAdjustedNits(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 */ getBrightnessFromNits(float nits)330 public abstract float getBrightnessFromNits(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, in milliseconds 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 * 374 * This timeout is also used when the device switches from interactive screen brightness mode 375 * to idle screen brightness mode, to preserve the user's preference when they resume usage of 376 * the device, within the specified timeframe. 377 */ getShortTermModelTimeout()378 public abstract long getShortTermModelTimeout(); 379 380 /** 381 * Prints dump output for display dumpsys. 382 */ dump(PrintWriter pw, float hbmTransition)383 public abstract void dump(PrintWriter pw, float hbmTransition); 384 getUserLux()385 abstract float getUserLux(); 386 getUserBrightness()387 abstract float getUserBrightness(); 388 389 /** 390 * @return The auto-brightness mode of this mapping strategy. Different modes use different 391 * brightness curves. 392 */ 393 @AutomaticBrightnessController.AutomaticBrightnessMode getMode()394 abstract int getMode(); 395 396 /** 397 * Check if the short term model should be reset given the anchor lux the last 398 * brightness change was made at and the current ambient lux. 399 */ shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor)400 public boolean shouldResetShortTermModel(float ambientLux, float shortTermModelAnchor) { 401 BrightnessConfiguration config = getBrightnessConfiguration(); 402 float minThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO; 403 float maxThresholdRatio = SHORT_TERM_MODEL_THRESHOLD_RATIO; 404 if (config != null) { 405 if (!Float.isNaN(config.getShortTermModelLowerLuxMultiplier())) { 406 minThresholdRatio = config.getShortTermModelLowerLuxMultiplier(); 407 } 408 if (!Float.isNaN(config.getShortTermModelUpperLuxMultiplier())) { 409 maxThresholdRatio = config.getShortTermModelUpperLuxMultiplier(); 410 } 411 } 412 final float minAmbientLux = 413 shortTermModelAnchor - shortTermModelAnchor * minThresholdRatio; 414 final float maxAmbientLux = 415 shortTermModelAnchor + shortTermModelAnchor * maxThresholdRatio; 416 if (minAmbientLux < ambientLux && ambientLux <= maxAmbientLux) { 417 if (mLoggingEnabled) { 418 Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " 419 + minAmbientLux + " < " + ambientLux + " < " + maxAmbientLux); 420 } 421 return false; 422 } else { 423 Slog.d(TAG, "ShortTermModel: reset data, ambient lux is " + ambientLux 424 + "(" + minAmbientLux + ", " + maxAmbientLux + ")"); 425 return true; 426 } 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 != INVALID_LUX) { 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 @AutomaticBrightnessController.AutomaticBrightnessMode 599 private final int mMode; 600 601 private Spline mSpline; 602 private float mMaxGamma; 603 private float mAutoBrightnessAdjustment; 604 private float mUserLux; 605 private float mUserBrightness; 606 private long mShortTermModelTimeout; 607 SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma, long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode)608 private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma, 609 long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode) { 610 Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, 611 "Lux and brightness arrays must not be empty!"); 612 Preconditions.checkArgument(lux.length == brightness.length, 613 "Lux and brightness arrays must be the same length!"); 614 Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); 615 Preconditions.checkArrayElementsInRange(brightness, 616 0, Integer.MAX_VALUE, "brightness"); 617 618 final int N = brightness.length; 619 mLux = new float[N]; 620 mBrightness = new float[N]; 621 for (int i = 0; i < N; i++) { 622 mLux[i] = lux[i]; 623 mBrightness[i] = brightness[i]; 624 } 625 626 mMaxGamma = maxGamma; 627 mAutoBrightnessAdjustment = 0; 628 mUserLux = INVALID_LUX; 629 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 630 if (mLoggingEnabled) { 631 PLOG.start("simple mapping strategy"); 632 } 633 computeSpline(); 634 mShortTermModelTimeout = timeout; 635 mMode = mode; 636 } 637 638 @Override getShortTermModelTimeout()639 public long getShortTermModelTimeout() { 640 return mShortTermModelTimeout; 641 } 642 643 @Override setBrightnessConfiguration(@ullable BrightnessConfiguration config)644 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 645 return false; 646 } 647 648 @Override getBrightnessConfiguration()649 public BrightnessConfiguration getBrightnessConfiguration() { 650 return null; 651 } 652 653 @Override getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)654 public float getBrightness(float lux, String packageName, 655 @ApplicationInfo.Category int category) { 656 return mSpline.interpolate(lux); 657 } 658 659 @Override getAutoBrightnessAdjustment()660 public float getAutoBrightnessAdjustment() { 661 return mAutoBrightnessAdjustment; 662 } 663 664 @Override setAutoBrightnessAdjustment(float adjustment)665 public boolean setAutoBrightnessAdjustment(float adjustment) { 666 adjustment = MathUtils.constrain(adjustment, -1, 1); 667 if (adjustment == mAutoBrightnessAdjustment) { 668 return false; 669 } 670 if (mLoggingEnabled) { 671 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 672 adjustment); 673 PLOG.start("auto-brightness adjustment"); 674 } 675 mAutoBrightnessAdjustment = adjustment; 676 computeSpline(); 677 return true; 678 } 679 680 @Override convertToNits(float brightness)681 public float convertToNits(float brightness) { 682 return INVALID_NITS; 683 } 684 685 @Override convertToAdjustedNits(float brightness)686 public float convertToAdjustedNits(float brightness) { 687 return INVALID_NITS; 688 } 689 690 @Override getBrightnessFromNits(float nits)691 public float getBrightnessFromNits(float nits) { 692 return PowerManager.BRIGHTNESS_INVALID_FLOAT; 693 } 694 695 @Override addUserDataPoint(float lux, float brightness)696 public void addUserDataPoint(float lux, float brightness) { 697 float unadjustedBrightness = getUnadjustedBrightness(lux); 698 if (mLoggingEnabled) { 699 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 700 PLOG.start("add user data point") 701 .logPoint("user data point", lux, brightness) 702 .logPoint("current brightness", lux, unadjustedBrightness); 703 } 704 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 705 brightness /* desiredBrightness */, 706 unadjustedBrightness /* currentBrightness */); 707 if (mLoggingEnabled) { 708 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 709 adjustment); 710 } 711 mAutoBrightnessAdjustment = adjustment; 712 mUserLux = lux; 713 mUserBrightness = brightness; 714 computeSpline(); 715 } 716 717 @Override clearUserDataPoints()718 public void clearUserDataPoints() { 719 if (mUserLux != INVALID_LUX) { 720 if (mLoggingEnabled) { 721 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 722 PLOG.start("clear user data points") 723 .logPoint("user data point", mUserLux, mUserBrightness); 724 } 725 mAutoBrightnessAdjustment = 0; 726 mUserLux = INVALID_LUX; 727 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 728 computeSpline(); 729 } 730 } 731 732 @Override hasUserDataPoints()733 public boolean hasUserDataPoints() { 734 return mUserLux != INVALID_LUX; 735 } 736 737 @Override isDefaultConfig()738 public boolean isDefaultConfig() { 739 return true; 740 } 741 742 @Override getDefaultConfig()743 public BrightnessConfiguration getDefaultConfig() { 744 return null; 745 } 746 747 @Override recalculateSplines(boolean applyAdjustment, float[] adjustment)748 public void recalculateSplines(boolean applyAdjustment, float[] adjustment) { 749 // Do nothing. 750 } 751 752 @Override dump(PrintWriter pw, float hbmTransition)753 public void dump(PrintWriter pw, float hbmTransition) { 754 pw.println("SimpleMappingStrategy"); 755 pw.println(" mSpline=" + mSpline); 756 pw.println(" mMaxGamma=" + mMaxGamma); 757 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 758 pw.println(" mUserLux=" + mUserLux); 759 pw.println(" mUserBrightness=" + mUserBrightness); 760 pw.println(" mShortTermModelTimeout=" + mShortTermModelTimeout); 761 } 762 763 @Override getMode()764 int getMode() { 765 return mMode; 766 } 767 768 @Override getUserLux()769 float getUserLux() { 770 return mUserLux; 771 } 772 773 @Override getUserBrightness()774 float getUserBrightness() { 775 return mUserBrightness; 776 } 777 computeSpline()778 private void computeSpline() { 779 Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux, 780 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 781 mSpline = Spline.createSpline(curve.first, curve.second); 782 } 783 getUnadjustedBrightness(float lux)784 private float getUnadjustedBrightness(float lux) { 785 Spline spline = Spline.createSpline(mLux, mBrightness); 786 return spline.interpolate(lux); 787 } 788 } 789 790 /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical 791 * range of the display, rather than to the range of the backlight control (typically 0-255). 792 * 793 * By mapping through the physical brightness, the curve becomes portable across devices and 794 * gives us more resolution in the resulting mapping. 795 */ 796 @VisibleForTesting 797 static class PhysicalMappingStrategy extends BrightnessMappingStrategy { 798 // The current brightness configuration. 799 private BrightnessConfiguration mConfig; 800 801 // A spline mapping from the current ambient light in lux to the desired display brightness 802 // in nits. 803 private Spline mBrightnessSpline; 804 805 // A spline mapping from nits to the corresponding brightness value, normalized to the range 806 // [0, 1.0]. 807 private Spline mNitsToBrightnessSpline; 808 809 // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to 810 // a brightness in nits. 811 private Spline mBrightnessToNitsSpline; 812 813 // A spline mapping from nits with adjustments applied to the corresponding brightness 814 // value, normalized to the range [0, 1.0]. 815 private Spline mAdjustedNitsToBrightnessSpline; 816 817 // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to 818 // a brightness in nits with adjustments applied. 819 private Spline mBrightnessToAdjustedNitsSpline; 820 821 // The default brightness configuration. 822 private final BrightnessConfiguration mDefaultConfig; 823 824 private final float[] mNits; 825 private final float[] mBrightness; 826 827 private boolean mBrightnessRangeAdjustmentApplied; 828 829 private final float mMaxGamma; 830 private float mAutoBrightnessAdjustment; 831 private float mUserLux; 832 private float mUserBrightness; 833 834 @Nullable 835 private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; 836 837 @AutomaticBrightnessController.AutomaticBrightnessMode 838 private final int mMode; 839 840 // Previous short-term models and the times that they were computed stored for debugging 841 // purposes 842 private List<Spline> mPreviousBrightnessSplines = new ArrayList<>(); 843 private LongArray mBrightnessSplineChangeTimes = new LongArray(); 844 private static final int NO_OF_PREVIOUS_CONFIGS_TO_LOG = 5; 845 private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 846 PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, float[] brightness, float maxGamma, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, @Nullable DisplayWhiteBalanceController displayWhiteBalanceController)847 public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, 848 float[] brightness, float maxGamma, 849 @AutomaticBrightnessController.AutomaticBrightnessMode int mode, 850 @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) { 851 852 Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, 853 "Nits and brightness arrays must not be empty!"); 854 855 Preconditions.checkArgument(nits.length == brightness.length, 856 "Nits and brightness arrays must be the same length!"); 857 Objects.requireNonNull(config); 858 Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); 859 Preconditions.checkArrayElementsInRange(brightness, 860 PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness"); 861 862 mMode = mode; 863 mMaxGamma = maxGamma; 864 mAutoBrightnessAdjustment = 0; 865 mUserLux = INVALID_LUX; 866 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 867 mDisplayWhiteBalanceController = displayWhiteBalanceController; 868 869 mNits = nits; 870 mBrightness = brightness; 871 computeNitsBrightnessSplines(mNits); 872 mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline; 873 mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline; 874 875 mDefaultConfig = config; 876 if (mLoggingEnabled) { 877 PLOG.start("physical mapping strategy"); 878 } 879 mConfig = config; 880 computeSpline(); 881 } 882 883 @Override getShortTermModelTimeout()884 public long getShortTermModelTimeout() { 885 if (mConfig.getShortTermModelTimeoutMillis() >= 0) { 886 return mConfig.getShortTermModelTimeoutMillis(); 887 } else { 888 return mDefaultConfig.getShortTermModelTimeoutMillis(); 889 } 890 } 891 892 @Override setBrightnessConfiguration(@ullable BrightnessConfiguration config)893 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 894 if (config == null) { 895 config = mDefaultConfig; 896 } 897 if (config.equals(mConfig)) { 898 return false; 899 } 900 if (mLoggingEnabled) { 901 PLOG.start("brightness configuration"); 902 } 903 mConfig = config; 904 computeSpline(); 905 return true; 906 } 907 908 @Override getBrightnessConfiguration()909 public BrightnessConfiguration getBrightnessConfiguration() { 910 return mConfig; 911 } 912 913 @Override getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)914 public float getBrightness(float lux, String packageName, 915 @ApplicationInfo.Category int category) { 916 float nits = mBrightnessSpline.interpolate(lux); 917 918 // Adjust nits to compensate for display white balance colour strength. 919 if (mDisplayWhiteBalanceController != null) { 920 nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits); 921 } 922 923 float brightness = mAdjustedNitsToBrightnessSpline.interpolate(nits); 924 // Correct the brightness according to the current application and its category, but 925 // only if no user data point is set (as this will override the user setting). 926 if (mUserLux == -1) { 927 brightness = correctBrightness(brightness, packageName, category); 928 } else if (mLoggingEnabled) { 929 Slog.d(TAG, "user point set, correction not applied"); 930 } 931 return brightness; 932 } 933 934 @Override getAutoBrightnessAdjustment()935 public float getAutoBrightnessAdjustment() { 936 return mAutoBrightnessAdjustment; 937 } 938 939 @Override setAutoBrightnessAdjustment(float adjustment)940 public boolean setAutoBrightnessAdjustment(float adjustment) { 941 adjustment = MathUtils.constrain(adjustment, -1, 1); 942 if (adjustment == mAutoBrightnessAdjustment) { 943 return false; 944 } 945 if (mLoggingEnabled) { 946 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 947 adjustment); 948 PLOG.start("auto-brightness adjustment"); 949 } 950 mAutoBrightnessAdjustment = adjustment; 951 computeSpline(); 952 return true; 953 } 954 955 @Override convertToNits(float brightness)956 public float convertToNits(float brightness) { 957 return mBrightnessToNitsSpline.interpolate(brightness); 958 } 959 960 @Override convertToAdjustedNits(float brightness)961 public float convertToAdjustedNits(float brightness) { 962 return mBrightnessToAdjustedNitsSpline.interpolate(brightness); 963 } 964 965 @Override getBrightnessFromNits(float nits)966 public float getBrightnessFromNits(float nits) { 967 return mNitsToBrightnessSpline.interpolate(nits); 968 } 969 970 @Override addUserDataPoint(float lux, float brightness)971 public void addUserDataPoint(float lux, float brightness) { 972 float unadjustedBrightness = getUnadjustedBrightness(lux); 973 if (mLoggingEnabled) { 974 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 975 PLOG.start("add user data point") 976 .logPoint("user data point", lux, brightness) 977 .logPoint("current brightness", lux, unadjustedBrightness); 978 } 979 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 980 brightness /* desiredBrightness */, 981 unadjustedBrightness /* currentBrightness */); 982 if (mLoggingEnabled) { 983 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 984 adjustment); 985 } 986 mAutoBrightnessAdjustment = adjustment; 987 mUserLux = lux; 988 mUserBrightness = brightness; 989 computeSpline(); 990 991 if (mPreviousBrightnessSplines.size() == NO_OF_PREVIOUS_CONFIGS_TO_LOG) { 992 mPreviousBrightnessSplines.remove(0); 993 mBrightnessSplineChangeTimes.remove(0); 994 } 995 mPreviousBrightnessSplines.add(mBrightnessSpline); 996 mBrightnessSplineChangeTimes.add(System.currentTimeMillis()); 997 } 998 999 @Override clearUserDataPoints()1000 public void clearUserDataPoints() { 1001 if (mUserLux != -1) { 1002 if (mLoggingEnabled) { 1003 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 1004 PLOG.start("clear user data points") 1005 .logPoint("user data point", mUserLux, mUserBrightness); 1006 } 1007 mAutoBrightnessAdjustment = 0; 1008 mUserLux = INVALID_LUX; 1009 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 1010 computeSpline(); 1011 } 1012 } 1013 1014 @Override hasUserDataPoints()1015 public boolean hasUserDataPoints() { 1016 return mUserLux != INVALID_LUX; 1017 } 1018 1019 @Override isDefaultConfig()1020 public boolean isDefaultConfig() { 1021 return mDefaultConfig.equals(mConfig); 1022 } 1023 1024 @Override getDefaultConfig()1025 public BrightnessConfiguration getDefaultConfig() { 1026 return mDefaultConfig; 1027 } 1028 1029 @Override recalculateSplines(boolean applyAdjustment, float[] adjustedNits)1030 public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) { 1031 mBrightnessRangeAdjustmentApplied = applyAdjustment; 1032 if (applyAdjustment) { 1033 mAdjustedNitsToBrightnessSpline = Spline.createSpline(adjustedNits, mBrightness); 1034 mBrightnessToAdjustedNitsSpline = Spline.createSpline(mBrightness, adjustedNits); 1035 } else { 1036 mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline; 1037 mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline; 1038 } 1039 } 1040 1041 @Override dump(PrintWriter pw, float hbmTransition)1042 public void dump(PrintWriter pw, float hbmTransition) { 1043 pw.println("PhysicalMappingStrategy"); 1044 pw.println(" mConfig=" + mConfig); 1045 pw.println(" mBrightnessSpline=" + mBrightnessSpline); 1046 pw.println(" mNitsToBrightnessSpline=" + mNitsToBrightnessSpline); 1047 pw.println(" mBrightnessToNitsSpline=" + mBrightnessToNitsSpline); 1048 pw.println(" mAdjustedNitsToBrightnessSpline=" + mAdjustedNitsToBrightnessSpline); 1049 pw.println(" mAdjustedBrightnessToNitsSpline=" + mBrightnessToAdjustedNitsSpline); 1050 pw.println(" mMaxGamma=" + mMaxGamma); 1051 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 1052 pw.println(" mUserLux=" + mUserLux); 1053 pw.println(" mUserBrightness=" + mUserBrightness); 1054 pw.println(" mDefaultConfig=" + mDefaultConfig); 1055 pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied); 1056 pw.println(" shortTermModelTimeout=" + getShortTermModelTimeout()); 1057 1058 pw.println(" Previous short-term models (oldest to newest): "); 1059 for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) { 1060 pw.println(" Computed at " 1061 + FORMAT.format(new Date(mBrightnessSplineChangeTimes.get(i))) + ": "); 1062 dumpConfigDiff(pw, hbmTransition, mPreviousBrightnessSplines.get(i), 1063 /* shortTermModelOnly= */ true); 1064 } 1065 1066 pw.println(" Difference between current config and default: "); 1067 dumpConfigDiff(pw, hbmTransition, mBrightnessSpline, /* shortTermModelOnly= */ false); 1068 } 1069 1070 @Override getMode()1071 int getMode() { 1072 return mMode; 1073 } 1074 1075 @Override getUserLux()1076 float getUserLux() { 1077 return mUserLux; 1078 } 1079 1080 @Override getUserBrightness()1081 float getUserBrightness() { 1082 return mUserBrightness; 1083 } 1084 1085 /** 1086 * Prints out the default curve and how it differs from the long-term curve 1087 * and the current curve (in case the current curve includes short-term adjustments). 1088 * 1089 * @param pw The print-writer to write to. 1090 */ dumpConfigDiff(PrintWriter pw, float hbmTransition, Spline brightnessSpline, boolean shortTermModelOnly)1091 private void dumpConfigDiff(PrintWriter pw, float hbmTransition, Spline brightnessSpline, 1092 boolean shortTermModelOnly) { 1093 Pair<float[], float[]> currentCurve = mConfig.getCurve(); 1094 Spline currSpline = Spline.createSpline(currentCurve.first, currentCurve.second); 1095 1096 Pair<float[], float[]> defaultCurve = mDefaultConfig.getCurve(); 1097 Spline defaultSpline = Spline.createSpline(defaultCurve.first, defaultCurve.second); 1098 1099 // Add the short-term curve lux point if present 1100 float[] luxes = currentCurve.first; 1101 if (mUserLux >= 0) { 1102 luxes = Arrays.copyOf(currentCurve.first, currentCurve.first.length + 1); 1103 luxes[luxes.length - 1] = mUserLux; 1104 Arrays.sort(luxes); 1105 } 1106 1107 StringBuilder sbLux = null; 1108 StringBuilder sbNits = null; 1109 StringBuilder sbLong = null; 1110 StringBuilder sbShort = null; 1111 StringBuilder sbBrightness = null; 1112 StringBuilder sbPercent = null; 1113 StringBuilder sbPercentHbm = null; 1114 boolean needsHeaders = true; 1115 String separator = ""; 1116 for (int i = 0; i < luxes.length; i++) { 1117 float lux = luxes[i]; 1118 if (needsHeaders) { 1119 sbLux = new StringBuilder(" lux: "); 1120 sbNits = new StringBuilder(" default: "); 1121 sbLong = new StringBuilder(" long-term: "); 1122 sbShort = new StringBuilder(" current: "); 1123 sbBrightness = new StringBuilder(" current(bl): "); 1124 sbPercent = new StringBuilder(" current(%): "); 1125 sbPercentHbm = new StringBuilder(" current(hbm%): "); 1126 needsHeaders = false; 1127 } 1128 1129 float defaultNits = defaultSpline.interpolate(lux); 1130 float longTermNits = currSpline.interpolate(lux); 1131 float shortTermNits = brightnessSpline.interpolate(lux); 1132 float brightness = mAdjustedNitsToBrightnessSpline.interpolate(shortTermNits); 1133 1134 String luxPrefix = (lux == mUserLux ? "^" : ""); 1135 String strLux = luxPrefix + toStrFloatForDump(lux); 1136 String strNits = toStrFloatForDump(defaultNits); 1137 String strLong = toStrFloatForDump(longTermNits); 1138 String strShort = toStrFloatForDump(shortTermNits); 1139 String strBrightness = toStrFloatForDump(brightness); 1140 String strPercent = String.valueOf( 1141 Math.round(100.0f * BrightnessUtils.convertLinearToGamma( 1142 (brightness / hbmTransition)))); 1143 String strPercentHbm = String.valueOf( 1144 Math.round(100.0f * BrightnessUtils.convertLinearToGamma(brightness))); 1145 1146 int maxLen = Math.max(strLux.length(), 1147 Math.max(strNits.length(), 1148 Math.max(strBrightness.length(), 1149 Math.max(strPercent.length(), 1150 Math.max(strPercentHbm.length(), 1151 Math.max(strLong.length(), strShort.length())))))); 1152 String format = separator + "%" + maxLen + "s"; 1153 separator = ", "; 1154 1155 sbLux.append(formatSimple(format, strLux)); 1156 sbNits.append(formatSimple(format, strNits)); 1157 sbLong.append(formatSimple(format, strLong)); 1158 sbShort.append(formatSimple(format, strShort)); 1159 sbBrightness.append(formatSimple(format, strBrightness)); 1160 sbPercent.append(formatSimple(format, strPercent)); 1161 sbPercentHbm.append(formatSimple(format, strPercentHbm)); 1162 1163 // At 80 chars, start another row 1164 if (sbLux.length() > 80 || (i == luxes.length - 1)) { 1165 pw.println(sbLux); 1166 if (!shortTermModelOnly) { 1167 pw.println(sbNits); 1168 pw.println(sbLong); 1169 } 1170 pw.println(sbShort); 1171 pw.println(sbBrightness); 1172 pw.println(sbPercent); 1173 if (hbmTransition < PowerManager.BRIGHTNESS_MAX) { 1174 pw.println(sbPercentHbm); 1175 } 1176 pw.println(""); 1177 needsHeaders = true; 1178 separator = ""; 1179 } 1180 } 1181 } 1182 toStrFloatForDump(float value)1183 private String toStrFloatForDump(float value) { 1184 if (value == 0.0f) { 1185 return "0"; 1186 } else if (value < 0.1f) { 1187 return String.format(Locale.US, "%.3f", value); 1188 } else if (value < 1) { 1189 return String.format(Locale.US, "%.2f", value); 1190 } else if (value < 10) { 1191 return String.format(Locale.US, "%.1f", value); 1192 } else { 1193 return formatSimple("%d", Math.round(value)); 1194 } 1195 } 1196 computeNitsBrightnessSplines(float[] nits)1197 private void computeNitsBrightnessSplines(float[] nits) { 1198 mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness); 1199 mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits); 1200 } 1201 computeSpline()1202 private void computeSpline() { 1203 Pair<float[], float[]> defaultCurve = mConfig.getCurve(); 1204 float[] defaultLux = defaultCurve.first; 1205 float[] defaultNits = defaultCurve.second; 1206 float[] defaultBrightness = new float[defaultNits.length]; 1207 for (int i = 0; i < defaultBrightness.length; i++) { 1208 defaultBrightness[i] = mAdjustedNitsToBrightnessSpline.interpolate(defaultNits[i]); 1209 } 1210 Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux, 1211 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 1212 float[] lux = curve.first; 1213 float[] brightness = curve.second; 1214 float[] nits = new float[brightness.length]; 1215 for (int i = 0; i < nits.length; i++) { 1216 nits[i] = mBrightnessToAdjustedNitsSpline.interpolate(brightness[i]); 1217 } 1218 mBrightnessSpline = Spline.createSpline(lux, nits); 1219 } 1220 getUnadjustedBrightness(float lux)1221 private float getUnadjustedBrightness(float lux) { 1222 Pair<float[], float[]> curve = mConfig.getCurve(); 1223 Spline spline = Spline.createSpline(curve.first, curve.second); 1224 return mAdjustedNitsToBrightnessSpline.interpolate(spline.interpolate(lux)); 1225 } 1226 correctBrightness(float brightness, String packageName, int category)1227 private float correctBrightness(float brightness, String packageName, int category) { 1228 if (packageName != null) { 1229 BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName); 1230 if (correction != null) { 1231 return correction.apply(brightness); 1232 } 1233 } 1234 if (category != ApplicationInfo.CATEGORY_UNDEFINED) { 1235 BrightnessCorrection correction = mConfig.getCorrectionByCategory(category); 1236 if (correction != null) { 1237 return correction.apply(brightness); 1238 } 1239 } 1240 return brightness; 1241 } 1242 } 1243 } 1244