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_BEDTIME_WEAR; 22 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; 23 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; 24 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; 25 26 import android.annotation.Nullable; 27 import android.content.Context; 28 import android.content.pm.ApplicationInfo; 29 import android.content.res.TypedArray; 30 import android.hardware.display.BrightnessConfiguration; 31 import android.hardware.display.BrightnessCorrection; 32 import android.os.PowerManager; 33 import android.os.UserHandle; 34 import android.provider.Settings; 35 import android.util.LongArray; 36 import android.util.MathUtils; 37 import android.util.Pair; 38 import android.util.Slog; 39 import android.util.Spline; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.display.BrightnessUtils; 43 import com.android.internal.util.Preconditions; 44 import com.android.server.display.utils.Plog; 45 import com.android.server.display.whitebalance.DisplayWhiteBalanceController; 46 47 import java.io.PrintWriter; 48 import java.text.SimpleDateFormat; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Date; 52 import java.util.List; 53 import java.util.Locale; 54 import java.util.Objects; 55 56 /** 57 * A utility to map from an ambient brightness to a display's "backlight" brightness based on the 58 * available display information and brightness configuration. 59 * 60 * Note that without a mapping from the nits to a display backlight level, any 61 * {@link BrightnessConfiguration}s that are set are just ignored. 62 */ 63 public abstract class BrightnessMappingStrategy { 64 private static final String TAG = "BrightnessMappingStrategy"; 65 66 public static final float INVALID_LUX = -1; 67 public static final float INVALID_NITS = -1; 68 69 private static final float LUX_GRAD_SMOOTHING = 0.25f; 70 private static final float MAX_GRAD = 1.0f; 71 private static final float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f; 72 73 // Constant that ensures that each step of the curve can increase by up to at least 74 // MIN_PERMISSABLE_INCREASE. Otherwise when the brightness is set to 0, the curve will never 75 // increase and will always be 0. 76 private static final float MIN_PERMISSABLE_INCREASE = 0.004f; 77 78 protected boolean mLoggingEnabled; 79 80 private static final Plog PLOG = Plog.createSystemPlog(TAG); 81 82 /** 83 * Creates a BrightnessMapping strategy. We do not create a simple mapping strategy for idle 84 * mode. 85 * 86 * @param context 87 * @param displayDeviceConfig 88 * @param mode The auto-brightness mode. Different modes use different brightness curves 89 * @param displayWhiteBalanceController 90 * @return the BrightnessMappingStrategy 91 */ 92 @Nullable create(Context context, DisplayDeviceConfig displayDeviceConfig, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, @Nullable DisplayWhiteBalanceController displayWhiteBalanceController)93 static BrightnessMappingStrategy create(Context context, 94 DisplayDeviceConfig displayDeviceConfig, 95 @AutomaticBrightnessController.AutomaticBrightnessMode int mode, 96 @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) { 97 98 // Display independent, mode dependent values 99 float[] brightnessLevelsNits = null; 100 float[] brightnessLevels = null; 101 float[] luxLevels = null; 102 int preset = Settings.System.getIntForUser(context.getContentResolver(), 103 Settings.System.SCREEN_BRIGHTNESS_FOR_ALS, 104 Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL, UserHandle.USER_CURRENT); 105 switch (mode) { 106 case AUTO_BRIGHTNESS_MODE_DEFAULT -> { 107 brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(); 108 luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); 109 brightnessLevels = 110 displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); 111 } 112 case AUTO_BRIGHTNESS_MODE_IDLE -> { 113 brightnessLevelsNits = getFloatArray(context.getResources().obtainTypedArray( 114 com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle)); 115 luxLevels = getLuxLevels(context.getResources().getIntArray( 116 com.android.internal.R.array.config_autoBrightnessLevelsIdle)); 117 } 118 case AUTO_BRIGHTNESS_MODE_DOZE, AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR -> { 119 luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); 120 brightnessLevels = 121 displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); 122 } 123 } 124 125 // Display independent, mode independent values 126 float autoBrightnessAdjustmentMaxGamma = context.getResources().getFraction( 127 com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 128 1, 1); 129 long shortTermModelTimeout = context.getResources().getInteger( 130 com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); 131 132 // Display dependent values - used for physical mapping strategy nits -> brightness 133 final float[] nitsRange = displayDeviceConfig.getNits(); 134 final float[] brightnessRange = displayDeviceConfig.getBrightness(); 135 136 if (isValidMapping(nitsRange, brightnessRange) 137 && isValidMapping(luxLevels, brightnessLevelsNits)) { 138 BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( 139 luxLevels, brightnessLevelsNits); 140 builder.setShortTermModelTimeoutMillis(shortTermModelTimeout); 141 builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); 142 builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); 143 return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange, 144 autoBrightnessAdjustmentMaxGamma, mode, preset, displayWhiteBalanceController); 145 } else if (isValidMapping(luxLevels, brightnessLevels)) { 146 return new SimpleMappingStrategy(luxLevels, brightnessLevels, 147 autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout, mode, preset); 148 } else { 149 return null; 150 } 151 } 152 getLuxLevels(int[] lux)153 private static float[] getLuxLevels(int[] lux) { 154 // The first control point is implicit and always at 0 lux. 155 float[] levels = new float[lux.length + 1]; 156 for (int i = 0; i < lux.length; i++) { 157 levels[i + 1] = (float) lux[i]; 158 } 159 return levels; 160 } 161 162 /** 163 * Extracts a float array from the specified {@link TypedArray}. 164 * 165 * @param array The array to convert. 166 * @return the given array as a float array. 167 */ getFloatArray(TypedArray array)168 public static float[] getFloatArray(TypedArray array) { 169 final int N = array.length(); 170 float[] vals = new float[N]; 171 for (int i = 0; i < N; i++) { 172 vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT); 173 } 174 array.recycle(); 175 return vals; 176 } 177 isValidMapping(float[] x, float[] y)178 private static boolean isValidMapping(float[] x, float[] y) { 179 if (x == null || y == null || x.length == 0 || y.length == 0) { 180 return false; 181 } 182 if (x.length != y.length) { 183 return false; 184 } 185 final int N = x.length; 186 float prevX = x[0]; 187 float prevY = y[0]; 188 if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) { 189 return false; 190 } 191 for (int i = 1; i < N; i++) { 192 if (prevX >= x[i] || prevY > y[i]) { 193 return false; 194 } 195 if (Float.isNaN(x[i]) || Float.isNaN(y[i])) { 196 return false; 197 } 198 prevX = x[i]; 199 prevY = y[i]; 200 } 201 return true; 202 } 203 isValidMapping(float[] x, int[] y)204 private static boolean isValidMapping(float[] x, int[] y) { 205 if (x == null || y == null || x.length == 0 || y.length == 0) { 206 return false; 207 } 208 if (x.length != y.length) { 209 return false; 210 } 211 final int N = x.length; 212 float prevX = x[0]; 213 int prevY = y[0]; 214 if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) { 215 return false; 216 } 217 for (int i = 1; i < N; i++) { 218 if (prevX >= x[i] || prevY > y[i]) { 219 return false; 220 } 221 if (Float.isNaN(x[i])) { 222 return false; 223 } 224 prevX = x[i]; 225 prevY = y[i]; 226 } 227 return true; 228 } 229 230 /** 231 * Enable/disable logging. 232 * 233 * @param loggingEnabled 234 * Whether logging should be on/off. 235 * 236 * @return Whether the method succeeded or not. 237 */ setLoggingEnabled(boolean loggingEnabled)238 public boolean setLoggingEnabled(boolean loggingEnabled) { 239 if (mLoggingEnabled == loggingEnabled) { 240 return false; 241 } 242 mLoggingEnabled = loggingEnabled; 243 return true; 244 } 245 246 /** 247 * Sets the {@link BrightnessConfiguration}. 248 * 249 * @param config The new configuration. If {@code null} is passed, the default configuration is 250 * used. 251 * @return Whether the brightness configuration has changed. 252 */ setBrightnessConfiguration(@ullable BrightnessConfiguration config)253 public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config); 254 255 /** 256 * Gets the current {@link BrightnessConfiguration}. 257 */ 258 @Nullable getBrightnessConfiguration()259 public abstract BrightnessConfiguration getBrightnessConfiguration(); 260 261 /** 262 * Returns the desired brightness of the display based on the current ambient lux, including 263 * any context-related corrections. 264 * 265 * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max 266 * brightness and 0 is the display at minimum brightness. 267 * 268 * @param lux The current ambient brightness in lux. 269 * @param packageName the foreground app package name. 270 * @param category the foreground app package category. 271 * @return The desired brightness of the display normalized to the range [0, 1.0]. 272 */ getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)273 public abstract float getBrightness(float lux, String packageName, 274 @ApplicationInfo.Category int category); 275 276 /** 277 * Returns the desired brightness of the display based on the current ambient lux. 278 * 279 * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max 280 * brightness and 0 is the display at minimum brightness. 281 * 282 * @param lux The current ambient brightness in lux. 283 * 284 * @return The desired brightness of the display normalized to the range [0, 1.0]. 285 */ getBrightness(float lux)286 public float getBrightness(float lux) { 287 return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED); 288 } 289 290 /** 291 * Returns the current auto-brightness adjustment. 292 * 293 * The returned adjustment is a value in the range [-1.0, 1.0] such that 294 * {@code config_autoBrightnessAdjustmentMaxGamma<sup>-adjustment</sup>} is used to gamma 295 * correct the brightness curve. 296 */ getAutoBrightnessAdjustment()297 public abstract float getAutoBrightnessAdjustment(); 298 299 /** 300 * Sets the auto-brightness adjustment. 301 * 302 * @param adjustment The desired auto-brightness adjustment. 303 * @return Whether the auto-brightness adjustment has changed. 304 * 305 * @Deprecated The auto-brightness adjustment should not be set directly, but rather inferred 306 * from user data points. 307 */ setAutoBrightnessAdjustment(float adjustment)308 public abstract boolean setAutoBrightnessAdjustment(float adjustment); 309 310 /** 311 * Converts the provided brightness value to nits if possible. 312 * 313 * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits. 314 */ convertToNits(float brightness)315 public abstract float convertToNits(float brightness); 316 317 /** 318 * Converts the provided brightness value to nits if possible. Adjustments, such as RBC are 319 * applied. 320 * 321 * Returns {@link INVALID_NITS} if there's no available mapping for the brightness to nits. 322 */ convertToAdjustedNits(float brightness)323 public abstract float convertToAdjustedNits(float brightness); 324 325 /** 326 * Converts the provided nit value to a float scale value if possible. 327 * 328 * Returns {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if there's no available mapping for 329 * the nits to float scale. 330 */ getBrightnessFromNits(float nits)331 public abstract float getBrightnessFromNits(float nits); 332 333 /** 334 * Adds a user interaction data point to the brightness mapping. 335 * 336 * This data point <b>must</b> exist on the brightness curve as a result of this call. This is 337 * so that the next time we come to query what the screen brightness should be, we get what the 338 * user requested rather than immediately changing to some other value. 339 * 340 * Currently, we only keep track of one of these at a time to constrain what can happen to the 341 * curve. 342 */ addUserDataPoint(float lux, float brightness)343 public abstract void addUserDataPoint(float lux, float brightness); 344 345 /** 346 * Removes any short term adjustments made to the curve from user interactions. 347 * 348 * Note that this does *not* reset the mapping to its initial state, any brightness 349 * configurations that have been applied will continue to be in effect. This solely removes the 350 * effects of user interactions on the model. 351 */ clearUserDataPoints()352 public abstract void clearUserDataPoints(); 353 354 /** @return True if there are any short term adjustments applied to the curve. */ hasUserDataPoints()355 public abstract boolean hasUserDataPoints(); 356 357 /** @return True if the current brightness configuration is the default one. */ isDefaultConfig()358 public abstract boolean isDefaultConfig(); 359 360 /** @return The default brightness configuration. */ getDefaultConfig()361 public abstract BrightnessConfiguration getDefaultConfig(); 362 363 /** Recalculates the backlight-to-nits and nits-to-backlight splines. */ recalculateSplines(boolean applyAdjustment, float[] adjustment)364 public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment); 365 366 /** 367 * Returns the timeout, in milliseconds for the short term model 368 * 369 * Timeout after which we remove the effects any user interactions might've had on the 370 * brightness mapping. This timeout doesn't start until we transition to a non-interactive 371 * display policy so that we don't reset while users are using their devices, but also so that 372 * we don't erroneously keep the short-term model if the device is dozing but the 373 * display is fully on. 374 * 375 * This timeout is also used when the device switches from interactive screen brightness mode 376 * to idle screen brightness mode, to preserve the user's preference when they resume usage of 377 * the device, within the specified timeframe. 378 */ getShortTermModelTimeout()379 public abstract long getShortTermModelTimeout(); 380 381 /** 382 * Prints dump output for display dumpsys. 383 */ dump(PrintWriter pw, float hbmTransition)384 public abstract void dump(PrintWriter pw, float hbmTransition); 385 getUserLux()386 abstract float getUserLux(); 387 getUserBrightness()388 abstract float getUserBrightness(); 389 390 /** 391 * @return The auto-brightness mode of this mapping strategy. Different modes use different 392 * brightness curves. 393 */ 394 @AutomaticBrightnessController.AutomaticBrightnessMode getMode()395 abstract int getMode(); 396 397 /** 398 * @return The preset for this mapping strategy. Presets are used on devices that allow users 399 * to choose from a set of predefined options in display auto-brightness settings. 400 */ getPreset()401 abstract int getPreset(); 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 insertControlPoint( float[] luxLevels, float[] brightnessLevels, float lux, float brightness)436 private Pair<float[], float[]> insertControlPoint( 437 float[] luxLevels, float[] brightnessLevels, float lux, float brightness) { 438 final int idx = findInsertionPoint(luxLevels, lux); 439 final float[] newLuxLevels; 440 final float[] newBrightnessLevels; 441 if (idx == luxLevels.length) { 442 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); 443 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); 444 newLuxLevels[idx] = lux; 445 newBrightnessLevels[idx] = brightness; 446 } else if (luxLevels[idx] == lux) { 447 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length); 448 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length); 449 newBrightnessLevels[idx] = brightness; 450 } else { 451 newLuxLevels = Arrays.copyOf(luxLevels, luxLevels.length + 1); 452 System.arraycopy(newLuxLevels, idx, newLuxLevels, idx+1, luxLevels.length - idx); 453 newLuxLevels[idx] = lux; 454 newBrightnessLevels = Arrays.copyOf(brightnessLevels, brightnessLevels.length + 1); 455 System.arraycopy(newBrightnessLevels, idx, newBrightnessLevels, idx+1, 456 brightnessLevels.length - idx); 457 newBrightnessLevels[idx] = brightness; 458 } 459 smoothCurve(newLuxLevels, newBrightnessLevels, idx); 460 return Pair.create(newLuxLevels, newBrightnessLevels); 461 } 462 463 /** 464 * Returns the index of the first value that's less than or equal to {@code val}. 465 * 466 * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater 467 * than val, then it will return the length of arr as the insertion point. 468 */ findInsertionPoint(float[] arr, float val)469 private int findInsertionPoint(float[] arr, float val) { 470 for (int i = 0; i < arr.length; i++) { 471 if (val <= arr[i]) { 472 return i; 473 } 474 } 475 return arr.length; 476 } 477 smoothCurve(float[] lux, float[] brightness, int idx)478 private void smoothCurve(float[] lux, float[] brightness, int idx) { 479 if (mLoggingEnabled) { 480 PLOG.logCurve("unsmoothed curve", lux, brightness); 481 } 482 float prevLux = lux[idx]; 483 float prevBrightness = brightness[idx]; 484 // Smooth curve for data points above the newly introduced point 485 for (int i = idx+1; i < lux.length; i++) { 486 float currLux = lux[i]; 487 float currBrightness = brightness[i]; 488 float maxBrightness = MathUtils.max( 489 prevBrightness * permissibleRatio(currLux, prevLux), 490 prevBrightness + MIN_PERMISSABLE_INCREASE); 491 float newBrightness = MathUtils.constrain( 492 currBrightness, prevBrightness, maxBrightness); 493 if (newBrightness == currBrightness) { 494 break; 495 } 496 prevLux = currLux; 497 prevBrightness = newBrightness; 498 brightness[i] = newBrightness; 499 } 500 // Smooth curve for data points below the newly introduced point 501 prevLux = lux[idx]; 502 prevBrightness = brightness[idx]; 503 for (int i = idx-1; i >= 0; i--) { 504 float currLux = lux[i]; 505 float currBrightness = brightness[i]; 506 float minBrightness = prevBrightness * permissibleRatio(currLux, prevLux); 507 float newBrightness = MathUtils.constrain( 508 currBrightness, minBrightness, prevBrightness); 509 if (newBrightness == currBrightness) { 510 break; 511 } 512 prevLux = currLux; 513 prevBrightness = newBrightness; 514 brightness[i] = newBrightness; 515 } 516 if (mLoggingEnabled) { 517 PLOG.logCurve("smoothed curve", lux, brightness); 518 } 519 } 520 permissibleRatio(float currLux, float prevLux)521 private float permissibleRatio(float currLux, float prevLux) { 522 return MathUtils.pow((currLux + LUX_GRAD_SMOOTHING) 523 / (prevLux + LUX_GRAD_SMOOTHING), MAX_GRAD); 524 } 525 inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness, float currentBrightness)526 protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness, 527 float currentBrightness) { 528 float adjustment = 0; 529 float gamma = Float.NaN; 530 // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges 531 // affects the curve rather drastically. 532 if (currentBrightness <= 0.1f || currentBrightness >= 0.9f) { 533 adjustment = (desiredBrightness - currentBrightness); 534 // Edge case: darkest adjustment possible. 535 } else if (desiredBrightness == 0) { 536 adjustment = -1; 537 // Edge case: brightest adjustment possible. 538 } else if (desiredBrightness == 1) { 539 adjustment = +1; 540 } else { 541 // current^gamma = desired => gamma = log[current](desired) 542 gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness); 543 // max^-adjustment = gamma => adjustment = -log[max](gamma) 544 adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma); 545 } 546 adjustment = MathUtils.constrain(adjustment, -1, +1); 547 if (mLoggingEnabled) { 548 Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" + 549 MathUtils.pow(maxGamma, -adjustment) + " == " + gamma); 550 Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" + 551 MathUtils.pow(currentBrightness, gamma) + " == " + desiredBrightness); 552 } 553 return adjustment; 554 } 555 getAdjustedCurve(float[] lux, float[] brightness, float userLux, float userBrightness, float adjustment, float maxGamma)556 protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness, 557 float userLux, float userBrightness, float adjustment, float maxGamma) { 558 float[] newLux = lux; 559 float[] newBrightness = Arrays.copyOf(brightness, brightness.length); 560 if (mLoggingEnabled) { 561 PLOG.logCurve("unadjusted curve", newLux, newBrightness); 562 } 563 adjustment = MathUtils.constrain(adjustment, -1, 1); 564 float gamma = MathUtils.pow(maxGamma, -adjustment); 565 if (mLoggingEnabled) { 566 Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" + 567 MathUtils.pow(maxGamma, -adjustment) + " == " + gamma); 568 } 569 if (gamma != 1) { 570 for (int i = 0; i < newBrightness.length; i++) { 571 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma); 572 } 573 } 574 if (mLoggingEnabled) { 575 PLOG.logCurve("gamma adjusted curve", newLux, newBrightness); 576 } 577 if (userLux != INVALID_LUX) { 578 Pair<float[], float[]> curve = insertControlPoint(newLux, newBrightness, userLux, 579 userBrightness); 580 newLux = curve.first; 581 newBrightness = curve.second; 582 if (mLoggingEnabled) { 583 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness); 584 // This is done for comparison. 585 curve = insertControlPoint(lux, brightness, userLux, userBrightness); 586 PLOG.logCurve("user adjusted curve", curve.first ,curve.second); 587 } 588 } 589 return Pair.create(newLux, newBrightness); 590 } 591 592 /** 593 * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the 594 * backlight of the display. 595 * 596 * Since we don't have information about the display's physical brightness, any brightness 597 * configurations that are set are just ignored. 598 */ 599 private static class SimpleMappingStrategy extends BrightnessMappingStrategy { 600 // Lux control points 601 private final float[] mLux; 602 // Brightness control points normalized to [0, 1] 603 private final float[] mBrightness; 604 605 @AutomaticBrightnessController.AutomaticBrightnessMode 606 private final int mMode; 607 608 private final int mPreset; 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, float[] brightness, float maxGamma, long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset)617 private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma, 618 long timeout, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, 619 int preset) { 620 Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, 621 "Lux and brightness arrays must not be empty!"); 622 Preconditions.checkArgument(lux.length == brightness.length, 623 "Lux and brightness arrays must be the same length!"); 624 Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); 625 Preconditions.checkArrayElementsInRange(brightness, 626 0, Integer.MAX_VALUE, "brightness"); 627 628 final int N = brightness.length; 629 mLux = new float[N]; 630 mBrightness = new float[N]; 631 for (int i = 0; i < N; i++) { 632 mLux[i] = lux[i]; 633 mBrightness[i] = brightness[i]; 634 } 635 636 mMaxGamma = maxGamma; 637 mAutoBrightnessAdjustment = 0; 638 mUserLux = INVALID_LUX; 639 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 640 if (mLoggingEnabled) { 641 PLOG.start("simple mapping strategy"); 642 } 643 computeSpline(); 644 mShortTermModelTimeout = timeout; 645 mMode = mode; 646 mPreset = preset; 647 } 648 649 @Override getShortTermModelTimeout()650 public long getShortTermModelTimeout() { 651 return mShortTermModelTimeout; 652 } 653 654 @Override setBrightnessConfiguration(@ullable BrightnessConfiguration config)655 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 656 return false; 657 } 658 659 @Override getBrightnessConfiguration()660 public BrightnessConfiguration getBrightnessConfiguration() { 661 return null; 662 } 663 664 @Override getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)665 public float getBrightness(float lux, String packageName, 666 @ApplicationInfo.Category int category) { 667 return mSpline.interpolate(lux); 668 } 669 670 @Override getAutoBrightnessAdjustment()671 public float getAutoBrightnessAdjustment() { 672 return mAutoBrightnessAdjustment; 673 } 674 675 @Override setAutoBrightnessAdjustment(float adjustment)676 public boolean setAutoBrightnessAdjustment(float adjustment) { 677 adjustment = MathUtils.constrain(adjustment, -1, 1); 678 if (adjustment == mAutoBrightnessAdjustment) { 679 return false; 680 } 681 if (mLoggingEnabled) { 682 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 683 adjustment); 684 PLOG.start("auto-brightness adjustment"); 685 } 686 mAutoBrightnessAdjustment = adjustment; 687 computeSpline(); 688 return true; 689 } 690 691 @Override convertToNits(float brightness)692 public float convertToNits(float brightness) { 693 return INVALID_NITS; 694 } 695 696 @Override convertToAdjustedNits(float brightness)697 public float convertToAdjustedNits(float brightness) { 698 return INVALID_NITS; 699 } 700 701 @Override getBrightnessFromNits(float nits)702 public float getBrightnessFromNits(float nits) { 703 return PowerManager.BRIGHTNESS_INVALID_FLOAT; 704 } 705 706 @Override addUserDataPoint(float lux, float brightness)707 public void addUserDataPoint(float lux, float brightness) { 708 float unadjustedBrightness = getUnadjustedBrightness(lux); 709 if (mLoggingEnabled) { 710 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 711 PLOG.start("add user data point") 712 .logPoint("user data point", lux, brightness) 713 .logPoint("current brightness", lux, unadjustedBrightness); 714 } 715 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 716 brightness /* desiredBrightness */, 717 unadjustedBrightness /* currentBrightness */); 718 if (mLoggingEnabled) { 719 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 720 adjustment); 721 } 722 mAutoBrightnessAdjustment = adjustment; 723 mUserLux = lux; 724 mUserBrightness = brightness; 725 computeSpline(); 726 } 727 728 @Override clearUserDataPoints()729 public void clearUserDataPoints() { 730 if (mUserLux != INVALID_LUX) { 731 if (mLoggingEnabled) { 732 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 733 PLOG.start("clear user data points") 734 .logPoint("user data point", mUserLux, mUserBrightness); 735 } 736 mAutoBrightnessAdjustment = 0; 737 mUserLux = INVALID_LUX; 738 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 739 computeSpline(); 740 } 741 } 742 743 @Override hasUserDataPoints()744 public boolean hasUserDataPoints() { 745 return mUserLux != INVALID_LUX; 746 } 747 748 @Override isDefaultConfig()749 public boolean isDefaultConfig() { 750 return true; 751 } 752 753 @Override getDefaultConfig()754 public BrightnessConfiguration getDefaultConfig() { 755 return null; 756 } 757 758 @Override recalculateSplines(boolean applyAdjustment, float[] adjustment)759 public void recalculateSplines(boolean applyAdjustment, float[] adjustment) { 760 // Do nothing. 761 } 762 763 @Override dump(PrintWriter pw, float hbmTransition)764 public void dump(PrintWriter pw, float hbmTransition) { 765 pw.println("SimpleMappingStrategy"); 766 pw.println(" mSpline=" + mSpline); 767 pw.println(" mMaxGamma=" + mMaxGamma); 768 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 769 pw.println(" mUserLux=" + mUserLux); 770 pw.println(" mUserBrightness=" + mUserBrightness); 771 pw.println(" mShortTermModelTimeout=" + mShortTermModelTimeout); 772 } 773 774 @Override getMode()775 int getMode() { 776 return mMode; 777 } 778 779 @Override getPreset()780 int getPreset() { 781 return mPreset; 782 } 783 784 @Override getUserLux()785 float getUserLux() { 786 return mUserLux; 787 } 788 789 @Override getUserBrightness()790 float getUserBrightness() { 791 return mUserBrightness; 792 } 793 computeSpline()794 private void computeSpline() { 795 Pair<float[], float[]> curve = getAdjustedCurve(mLux, mBrightness, mUserLux, 796 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 797 mSpline = Spline.createSpline(curve.first, curve.second); 798 } 799 getUnadjustedBrightness(float lux)800 private float getUnadjustedBrightness(float lux) { 801 Spline spline = Spline.createSpline(mLux, mBrightness); 802 return spline.interpolate(lux); 803 } 804 } 805 806 /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical 807 * range of the display, rather than to the range of the backlight control (typically 0-255). 808 * 809 * By mapping through the physical brightness, the curve becomes portable across devices and 810 * gives us more resolution in the resulting mapping. 811 */ 812 @VisibleForTesting 813 static class PhysicalMappingStrategy extends BrightnessMappingStrategy { 814 // The current brightness configuration. 815 private BrightnessConfiguration mConfig; 816 817 // A spline mapping from the current ambient light in lux to the desired display brightness 818 // in nits. 819 private Spline mBrightnessSpline; 820 821 // A spline mapping from nits to the corresponding brightness value, normalized to the range 822 // [0, 1.0]. 823 private Spline mNitsToBrightnessSpline; 824 825 // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to 826 // a brightness in nits. 827 private Spline mBrightnessToNitsSpline; 828 829 // A spline mapping from nits with adjustments applied to the corresponding brightness 830 // value, normalized to the range [0, 1.0]. 831 private Spline mAdjustedNitsToBrightnessSpline; 832 833 // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to 834 // a brightness in nits with adjustments applied. 835 private Spline mBrightnessToAdjustedNitsSpline; 836 837 // The default brightness configuration. 838 private final BrightnessConfiguration mDefaultConfig; 839 840 private final float[] mNits; 841 private final float[] mBrightness; 842 843 private boolean mBrightnessRangeAdjustmentApplied; 844 845 private final float mMaxGamma; 846 private float mAutoBrightnessAdjustment; 847 private float mUserLux; 848 private float mUserBrightness; 849 850 @Nullable 851 private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; 852 853 @AutomaticBrightnessController.AutomaticBrightnessMode 854 private final int mMode; 855 856 private final int mPreset; 857 858 // Previous short-term models and the times that they were computed stored for debugging 859 // purposes 860 private List<Spline> mPreviousBrightnessSplines = new ArrayList<>(); 861 private LongArray mBrightnessSplineChangeTimes = new LongArray(); 862 private static final int NO_OF_PREVIOUS_CONFIGS_TO_LOG = 5; 863 private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 864 PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, float[] brightness, float maxGamma, @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset, @Nullable DisplayWhiteBalanceController displayWhiteBalanceController)865 public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, 866 float[] brightness, float maxGamma, 867 @AutomaticBrightnessController.AutomaticBrightnessMode int mode, int preset, 868 @Nullable DisplayWhiteBalanceController displayWhiteBalanceController) { 869 870 Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, 871 "Nits and brightness arrays must not be empty!"); 872 873 Preconditions.checkArgument(nits.length == brightness.length, 874 "Nits and brightness arrays must be the same length!"); 875 Objects.requireNonNull(config); 876 Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); 877 Preconditions.checkArrayElementsInRange(brightness, 878 PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness"); 879 880 mMode = mode; 881 mPreset = preset; 882 mMaxGamma = maxGamma; 883 mAutoBrightnessAdjustment = 0; 884 mUserLux = INVALID_LUX; 885 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 886 mDisplayWhiteBalanceController = displayWhiteBalanceController; 887 888 mNits = nits; 889 mBrightness = brightness; 890 computeNitsBrightnessSplines(mNits); 891 mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline; 892 mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline; 893 894 mDefaultConfig = config; 895 if (mLoggingEnabled) { 896 PLOG.start("physical mapping strategy"); 897 } 898 mConfig = config; 899 computeSpline(); 900 } 901 902 @Override getShortTermModelTimeout()903 public long getShortTermModelTimeout() { 904 if (mConfig.getShortTermModelTimeoutMillis() >= 0) { 905 return mConfig.getShortTermModelTimeoutMillis(); 906 } else { 907 return mDefaultConfig.getShortTermModelTimeoutMillis(); 908 } 909 } 910 911 @Override setBrightnessConfiguration(@ullable BrightnessConfiguration config)912 public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { 913 if (config == null) { 914 config = mDefaultConfig; 915 } 916 if (config.equals(mConfig)) { 917 return false; 918 } 919 if (mLoggingEnabled) { 920 PLOG.start("brightness configuration"); 921 } 922 mConfig = config; 923 computeSpline(); 924 return true; 925 } 926 927 @Override getBrightnessConfiguration()928 public BrightnessConfiguration getBrightnessConfiguration() { 929 return mConfig; 930 } 931 932 @Override getBrightness(float lux, String packageName, @ApplicationInfo.Category int category)933 public float getBrightness(float lux, String packageName, 934 @ApplicationInfo.Category int category) { 935 float nits = mBrightnessSpline.interpolate(lux); 936 937 // Adjust nits to compensate for display white balance colour strength. 938 if (mDisplayWhiteBalanceController != null) { 939 nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits); 940 } 941 942 float brightness = mAdjustedNitsToBrightnessSpline.interpolate(nits); 943 // Correct the brightness according to the current application and its category, but 944 // only if no user data point is set (as this will override the user setting). 945 if (mUserLux == -1) { 946 brightness = correctBrightness(brightness, packageName, category); 947 } else if (mLoggingEnabled) { 948 Slog.d(TAG, "user point set, correction not applied"); 949 } 950 return brightness; 951 } 952 953 @Override getAutoBrightnessAdjustment()954 public float getAutoBrightnessAdjustment() { 955 return mAutoBrightnessAdjustment; 956 } 957 958 @Override setAutoBrightnessAdjustment(float adjustment)959 public boolean setAutoBrightnessAdjustment(float adjustment) { 960 adjustment = MathUtils.constrain(adjustment, -1, 1); 961 if (adjustment == mAutoBrightnessAdjustment) { 962 return false; 963 } 964 if (mLoggingEnabled) { 965 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " + 966 adjustment); 967 PLOG.start("auto-brightness adjustment"); 968 } 969 mAutoBrightnessAdjustment = adjustment; 970 computeSpline(); 971 return true; 972 } 973 974 @Override convertToNits(float brightness)975 public float convertToNits(float brightness) { 976 return mBrightnessToNitsSpline.interpolate(brightness); 977 } 978 979 @Override convertToAdjustedNits(float brightness)980 public float convertToAdjustedNits(float brightness) { 981 return mBrightnessToAdjustedNitsSpline.interpolate(brightness); 982 } 983 984 @Override getBrightnessFromNits(float nits)985 public float getBrightnessFromNits(float nits) { 986 return mNitsToBrightnessSpline.interpolate(nits); 987 } 988 989 @Override addUserDataPoint(float lux, float brightness)990 public void addUserDataPoint(float lux, float brightness) { 991 float unadjustedBrightness = getUnadjustedBrightness(lux); 992 if (mLoggingEnabled) { 993 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")"); 994 PLOG.start("add user data point") 995 .logPoint("user data point", lux, brightness) 996 .logPoint("current brightness", lux, unadjustedBrightness); 997 } 998 float adjustment = inferAutoBrightnessAdjustment(mMaxGamma, 999 brightness /* desiredBrightness */, 1000 unadjustedBrightness /* currentBrightness */); 1001 if (mLoggingEnabled) { 1002 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " + 1003 adjustment); 1004 } 1005 mAutoBrightnessAdjustment = adjustment; 1006 mUserLux = lux; 1007 mUserBrightness = brightness; 1008 computeSpline(); 1009 1010 if (mPreviousBrightnessSplines.size() == NO_OF_PREVIOUS_CONFIGS_TO_LOG) { 1011 mPreviousBrightnessSplines.remove(0); 1012 mBrightnessSplineChangeTimes.remove(0); 1013 } 1014 mPreviousBrightnessSplines.add(mBrightnessSpline); 1015 mBrightnessSplineChangeTimes.add(System.currentTimeMillis()); 1016 } 1017 1018 @Override clearUserDataPoints()1019 public void clearUserDataPoints() { 1020 if (mUserLux != -1) { 1021 if (mLoggingEnabled) { 1022 Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0"); 1023 PLOG.start("clear user data points") 1024 .logPoint("user data point", mUserLux, mUserBrightness); 1025 } 1026 mAutoBrightnessAdjustment = 0; 1027 mUserLux = INVALID_LUX; 1028 mUserBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; 1029 computeSpline(); 1030 } 1031 } 1032 1033 @Override hasUserDataPoints()1034 public boolean hasUserDataPoints() { 1035 return mUserLux != INVALID_LUX; 1036 } 1037 1038 @Override isDefaultConfig()1039 public boolean isDefaultConfig() { 1040 return mDefaultConfig.equals(mConfig); 1041 } 1042 1043 @Override getDefaultConfig()1044 public BrightnessConfiguration getDefaultConfig() { 1045 return mDefaultConfig; 1046 } 1047 1048 @Override recalculateSplines(boolean applyAdjustment, float[] adjustedNits)1049 public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) { 1050 mBrightnessRangeAdjustmentApplied = applyAdjustment; 1051 if (applyAdjustment) { 1052 mAdjustedNitsToBrightnessSpline = Spline.createSpline(adjustedNits, mBrightness); 1053 mBrightnessToAdjustedNitsSpline = Spline.createSpline(mBrightness, adjustedNits); 1054 } else { 1055 mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline; 1056 mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline; 1057 } 1058 } 1059 1060 @Override dump(PrintWriter pw, float hbmTransition)1061 public void dump(PrintWriter pw, float hbmTransition) { 1062 pw.println("PhysicalMappingStrategy"); 1063 pw.println(" mConfig=" + mConfig); 1064 pw.println(" mBrightnessSpline=" + mBrightnessSpline); 1065 pw.println(" mNitsToBrightnessSpline=" + mNitsToBrightnessSpline); 1066 pw.println(" mBrightnessToNitsSpline=" + mBrightnessToNitsSpline); 1067 pw.println(" mAdjustedNitsToBrightnessSpline=" + mAdjustedNitsToBrightnessSpline); 1068 pw.println(" mAdjustedBrightnessToNitsSpline=" + mBrightnessToAdjustedNitsSpline); 1069 pw.println(" mMaxGamma=" + mMaxGamma); 1070 pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); 1071 pw.println(" mUserLux=" + mUserLux); 1072 pw.println(" mUserBrightness=" + mUserBrightness); 1073 pw.println(" mDefaultConfig=" + mDefaultConfig); 1074 pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied); 1075 pw.println(" shortTermModelTimeout=" + getShortTermModelTimeout()); 1076 1077 if (!mPreviousBrightnessSplines.isEmpty()) { 1078 pw.println(" Previous short-term models (oldest to newest): "); 1079 } 1080 for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) { 1081 pw.println(" Computed at " 1082 + FORMAT.format(new Date(mBrightnessSplineChangeTimes.get(i))) + ": "); 1083 dumpConfigDiff(pw, hbmTransition, mPreviousBrightnessSplines.get(i), 1084 /* shortTermModelOnly= */ true); 1085 } 1086 1087 pw.println(" Difference between current config and default: "); 1088 dumpConfigDiff(pw, hbmTransition, mBrightnessSpline, /* shortTermModelOnly= */ false); 1089 } 1090 1091 @Override getMode()1092 int getMode() { 1093 return mMode; 1094 } 1095 1096 @Override getPreset()1097 int getPreset() { 1098 return mPreset; 1099 } 1100 1101 @Override getUserLux()1102 float getUserLux() { 1103 return mUserLux; 1104 } 1105 1106 @Override getUserBrightness()1107 float getUserBrightness() { 1108 return mUserBrightness; 1109 } 1110 1111 /** 1112 * Prints out the default curve and how it differs from the long-term curve 1113 * and the current curve (in case the current curve includes short-term adjustments). 1114 * 1115 * @param pw The print-writer to write to. 1116 */ dumpConfigDiff(PrintWriter pw, float hbmTransition, Spline brightnessSpline, boolean shortTermModelOnly)1117 private void dumpConfigDiff(PrintWriter pw, float hbmTransition, Spline brightnessSpline, 1118 boolean shortTermModelOnly) { 1119 Pair<float[], float[]> currentCurve = mConfig.getCurve(); 1120 Spline currSpline = Spline.createSpline(currentCurve.first, currentCurve.second); 1121 1122 Pair<float[], float[]> defaultCurve = mDefaultConfig.getCurve(); 1123 Spline defaultSpline = Spline.createSpline(defaultCurve.first, defaultCurve.second); 1124 1125 // Add the short-term curve lux point if present 1126 float[] luxes = currentCurve.first; 1127 if (mUserLux >= 0) { 1128 luxes = Arrays.copyOf(currentCurve.first, currentCurve.first.length + 1); 1129 luxes[luxes.length - 1] = mUserLux; 1130 Arrays.sort(luxes); 1131 } 1132 1133 StringBuilder sbLux = null; 1134 StringBuilder sbNits = null; 1135 StringBuilder sbLong = null; 1136 StringBuilder sbShort = null; 1137 StringBuilder sbBrightness = null; 1138 StringBuilder sbPercent = null; 1139 StringBuilder sbPercentHbm = null; 1140 boolean needsHeaders = true; 1141 String separator = ""; 1142 for (int i = 0; i < luxes.length; i++) { 1143 float lux = luxes[i]; 1144 if (needsHeaders) { 1145 sbLux = new StringBuilder(" lux: "); 1146 sbNits = new StringBuilder(" default: "); 1147 sbLong = new StringBuilder(" long-term: "); 1148 sbShort = new StringBuilder(" current: "); 1149 sbBrightness = new StringBuilder(" current(bl): "); 1150 sbPercent = new StringBuilder(" current(%): "); 1151 sbPercentHbm = new StringBuilder(" current(hbm%): "); 1152 needsHeaders = false; 1153 } 1154 1155 float defaultNits = defaultSpline.interpolate(lux); 1156 float longTermNits = currSpline.interpolate(lux); 1157 float shortTermNits = brightnessSpline.interpolate(lux); 1158 float brightness = mAdjustedNitsToBrightnessSpline.interpolate(shortTermNits); 1159 1160 String luxPrefix = (lux == mUserLux ? "^" : ""); 1161 String strLux = luxPrefix + toStrFloatForDump(lux); 1162 String strNits = toStrFloatForDump(defaultNits); 1163 String strLong = toStrFloatForDump(longTermNits); 1164 String strShort = toStrFloatForDump(shortTermNits); 1165 String strBrightness = toStrFloatForDump(brightness); 1166 String strPercent = String.valueOf( 1167 Math.round(100.0f * BrightnessUtils.convertLinearToGamma( 1168 (brightness / hbmTransition)))); 1169 String strPercentHbm = String.valueOf( 1170 Math.round(100.0f * BrightnessUtils.convertLinearToGamma(brightness))); 1171 1172 int maxLen = Math.max(strLux.length(), 1173 Math.max(strNits.length(), 1174 Math.max(strBrightness.length(), 1175 Math.max(strPercent.length(), 1176 Math.max(strPercentHbm.length(), 1177 Math.max(strLong.length(), strShort.length())))))); 1178 String format = separator + "%" + maxLen + "s"; 1179 separator = ", "; 1180 1181 sbLux.append(formatSimple(format, strLux)); 1182 sbNits.append(formatSimple(format, strNits)); 1183 sbLong.append(formatSimple(format, strLong)); 1184 sbShort.append(formatSimple(format, strShort)); 1185 sbBrightness.append(formatSimple(format, strBrightness)); 1186 sbPercent.append(formatSimple(format, strPercent)); 1187 sbPercentHbm.append(formatSimple(format, strPercentHbm)); 1188 1189 // At 80 chars, start another row 1190 if (sbLux.length() > 80 || (i == luxes.length - 1)) { 1191 pw.println(sbLux); 1192 if (!shortTermModelOnly) { 1193 pw.println(sbNits); 1194 pw.println(sbLong); 1195 } 1196 pw.println(sbShort); 1197 pw.println(sbBrightness); 1198 pw.println(sbPercent); 1199 if (hbmTransition < PowerManager.BRIGHTNESS_MAX) { 1200 pw.println(sbPercentHbm); 1201 } 1202 pw.println(""); 1203 needsHeaders = true; 1204 separator = ""; 1205 } 1206 } 1207 } 1208 toStrFloatForDump(float value)1209 private String toStrFloatForDump(float value) { 1210 if (value == 0.0f) { 1211 return "0"; 1212 } else if (value < 0.1f) { 1213 return String.format(Locale.US, "%.3f", value); 1214 } else if (value < 1) { 1215 return String.format(Locale.US, "%.2f", value); 1216 } else if (value < 10) { 1217 return String.format(Locale.US, "%.1f", value); 1218 } else { 1219 return formatSimple("%d", Math.round(value)); 1220 } 1221 } 1222 computeNitsBrightnessSplines(float[] nits)1223 private void computeNitsBrightnessSplines(float[] nits) { 1224 mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness); 1225 mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits); 1226 } 1227 computeSpline()1228 private void computeSpline() { 1229 Pair<float[], float[]> defaultCurve = mConfig.getCurve(); 1230 float[] defaultLux = defaultCurve.first; 1231 float[] defaultNits = defaultCurve.second; 1232 float[] defaultBrightness = new float[defaultNits.length]; 1233 for (int i = 0; i < defaultBrightness.length; i++) { 1234 defaultBrightness[i] = mAdjustedNitsToBrightnessSpline.interpolate(defaultNits[i]); 1235 } 1236 Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux, 1237 mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); 1238 float[] lux = curve.first; 1239 float[] brightness = curve.second; 1240 float[] nits = new float[brightness.length]; 1241 for (int i = 0; i < nits.length; i++) { 1242 nits[i] = mBrightnessToAdjustedNitsSpline.interpolate(brightness[i]); 1243 } 1244 mBrightnessSpline = Spline.createSpline(lux, nits); 1245 } 1246 getUnadjustedBrightness(float lux)1247 private float getUnadjustedBrightness(float lux) { 1248 Pair<float[], float[]> curve = mConfig.getCurve(); 1249 Spline spline = Spline.createSpline(curve.first, curve.second); 1250 return mAdjustedNitsToBrightnessSpline.interpolate(spline.interpolate(lux)); 1251 } 1252 correctBrightness(float brightness, String packageName, int category)1253 private float correctBrightness(float brightness, String packageName, int category) { 1254 if (packageName != null) { 1255 BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName); 1256 if (correction != null) { 1257 return correction.apply(brightness); 1258 } 1259 } 1260 if (category != ApplicationInfo.CATEGORY_UNDEFINED) { 1261 BrightnessCorrection correction = mConfig.getCorrectionByCategory(category); 1262 if (correction != null) { 1263 return correction.apply(brightness); 1264 } 1265 } 1266 return brightness; 1267 } 1268 } 1269 } 1270