1 /* 2 * Copyright (C) 2020 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 android.annotation.NonNull; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.hardware.display.DisplayManagerInternal; 23 import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation; 24 import android.os.Environment; 25 import android.os.PowerManager; 26 import android.text.TextUtils; 27 import android.util.MathUtils; 28 import android.util.Slog; 29 import android.util.Spline; 30 import android.view.DisplayAddress; 31 32 import com.android.internal.R; 33 import com.android.internal.display.BrightnessSynchronizer; 34 import com.android.server.display.config.DisplayConfiguration; 35 import com.android.server.display.config.DisplayQuirks; 36 import com.android.server.display.config.HbmTiming; 37 import com.android.server.display.config.HighBrightnessMode; 38 import com.android.server.display.config.NitsMap; 39 import com.android.server.display.config.Point; 40 import com.android.server.display.config.RefreshRateRange; 41 import com.android.server.display.config.SensorDetails; 42 import com.android.server.display.config.ThermalStatus; 43 import com.android.server.display.config.XmlParser; 44 45 import org.xmlpull.v1.XmlPullParserException; 46 47 import java.io.BufferedInputStream; 48 import java.io.File; 49 import java.io.FileInputStream; 50 import java.io.IOException; 51 import java.io.InputStream; 52 import java.math.BigDecimal; 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.List; 56 57 import javax.xml.datatype.DatatypeConfigurationException; 58 59 /** 60 * Reads and stores display-specific configurations. 61 */ 62 public class DisplayDeviceConfig { 63 private static final String TAG = "DisplayDeviceConfig"; 64 65 public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN; 66 67 public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc"; 68 69 private static final float BRIGHTNESS_DEFAULT = 0.5f; 70 private static final String ETC_DIR = "etc"; 71 private static final String DISPLAY_CONFIG_DIR = "displayconfig"; 72 private static final String CONFIG_FILE_FORMAT = "display_%s.xml"; 73 private static final String PORT_SUFFIX_FORMAT = "port_%d"; 74 private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d"; 75 private static final String NO_SUFFIX_FORMAT = "%d"; 76 private static final long STABLE_FLAG = 1L << 62; 77 78 // Float.NaN (used as invalid for brightness) cannot be stored in config.xml 79 // so -2 is used instead 80 private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f; 81 82 private static final float NITS_INVALID = -1; 83 84 private final Context mContext; 85 86 // The details of the ambient light sensor associated with this display. 87 private final SensorData mAmbientLightSensor = new SensorData(); 88 89 // The details of the proximity sensor associated with this display. 90 private final SensorData mProximitySensor = new SensorData(); 91 92 private final List<RefreshRateLimitation> mRefreshRateLimitations = 93 new ArrayList<>(2 /*initialCapacity*/); 94 95 // Nits and backlight values that are loaded from either the display device config file, or 96 // config.xml. These are the raw values and just used for the dumpsys 97 private float[] mRawNits; 98 private float[] mRawBacklight; 99 100 // These arrays are calculated from the raw arrays, but clamped to contain values equal to and 101 // between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same 102 // length 103 // Nits array that is used to store the entire range of nits values that the device supports 104 private float[] mNits; 105 // Backlight array holds the values that the HAL uses to display the corresponding nits values 106 private float[] mBacklight; 107 // Purely an array that covers the ranges of values 0.0 - 1.0, indicating the system brightness 108 // for the corresponding values above 109 private float[] mBrightness; 110 111 private float mBacklightMinimum = Float.NaN; 112 private float mBacklightMaximum = Float.NaN; 113 private float mBrightnessDefault = Float.NaN; 114 private float mBrightnessRampFastDecrease = Float.NaN; 115 private float mBrightnessRampFastIncrease = Float.NaN; 116 private float mBrightnessRampSlowDecrease = Float.NaN; 117 private float mBrightnessRampSlowIncrease = Float.NaN; 118 private Spline mBrightnessToBacklightSpline; 119 private Spline mBacklightToBrightnessSpline; 120 private Spline mBacklightToNitsSpline; 121 private List<String> mQuirks; 122 private boolean mIsHighBrightnessModeEnabled = false; 123 private HighBrightnessModeData mHbmData; 124 private String mLoadedFrom = null; 125 DisplayDeviceConfig(Context context)126 private DisplayDeviceConfig(Context context) { 127 mContext = context; 128 } 129 130 /** 131 * Creates an instance for the specified display. 132 * Tries to find a file with identifier in the following priority order: 133 * <ol> 134 * <li>physicalDisplayId</li> 135 * <li>physicalDisplayId without a stable flag (old system)</li> 136 * <li>portId</li> 137 * </ol> 138 * 139 * @param physicalDisplayId The display ID for which to load the configuration. 140 * @return A configuration instance for the specified display. 141 */ create(Context context, long physicalDisplayId, boolean isDefaultDisplay)142 public static DisplayDeviceConfig create(Context context, long physicalDisplayId, 143 boolean isDefaultDisplay) { 144 DisplayDeviceConfig config; 145 146 config = loadConfigFromDirectory(context, Environment.getProductDirectory(), 147 physicalDisplayId); 148 if (config != null) { 149 return config; 150 } 151 152 config = loadConfigFromDirectory(context, Environment.getVendorDirectory(), 153 physicalDisplayId); 154 if (config != null) { 155 return config; 156 } 157 158 // If no config can be loaded from any ddc xml at all, 159 // prepare a whole config using the global config.xml. 160 // Guaranteed not null 161 return create(context, isDefaultDisplay); 162 } 163 164 /** 165 * Creates an instance using global values since no display device config xml exists. 166 * Uses values from config or PowerManager. 167 * 168 * @param context 169 * @param useConfigXml 170 * @return A configuration instance. 171 */ create(Context context, boolean useConfigXml)172 public static DisplayDeviceConfig create(Context context, boolean useConfigXml) { 173 DisplayDeviceConfig config; 174 if (useConfigXml) { 175 config = getConfigFromGlobalXml(context); 176 } else { 177 config = getConfigFromPmValues(context); 178 } 179 return config; 180 } 181 loadConfigFromDirectory(Context context, File baseDirectory, long physicalDisplayId)182 private static DisplayDeviceConfig loadConfigFromDirectory(Context context, 183 File baseDirectory, long physicalDisplayId) { 184 DisplayDeviceConfig config; 185 // Create config using filename from physical ID (including "stable" bit). 186 config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT, 187 physicalDisplayId); 188 if (config != null) { 189 return config; 190 } 191 192 // Create config using filename from physical ID (excluding "stable" bit). 193 final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG; 194 config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag); 195 if (config != null) { 196 return config; 197 } 198 199 // Create config using filename from port ID. 200 final DisplayAddress.Physical physicalAddress = 201 DisplayAddress.fromPhysicalDisplayId(physicalDisplayId); 202 int port = physicalAddress.getPort(); 203 config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port); 204 return config; 205 } 206 207 /** 208 * Return the brightness mapping nits array. 209 * 210 * @return The brightness mapping nits array. 211 */ getNits()212 public float[] getNits() { 213 return mNits; 214 } 215 216 /** 217 * Return the brightness mapping backlight array. 218 * 219 * @return The backlight mapping value array. 220 */ getBacklight()221 public float[] getBacklight() { 222 return mBacklight; 223 } 224 225 /** 226 * Calculates the backlight value, as recognised by the HAL, from the brightness value 227 * given that the rest of the system deals with. 228 * 229 * @param brightness value on the framework scale of 0-1 230 * @return backlight value on the HAL scale of 0-1 231 */ getBacklightFromBrightness(float brightness)232 public float getBacklightFromBrightness(float brightness) { 233 return mBrightnessToBacklightSpline.interpolate(brightness); 234 } 235 236 /** 237 * Calculates the nits value for the specified backlight value if a mapping exists. 238 * 239 * @return The mapped nits or 0 if no mapping exits. 240 */ getNitsFromBacklight(float backlight)241 public float getNitsFromBacklight(float backlight) { 242 if (mBacklightToNitsSpline == null) { 243 Slog.wtf(TAG, "requesting nits when no mapping exists."); 244 return NITS_INVALID; 245 } 246 backlight = Math.max(backlight, mBacklightMinimum); 247 return mBacklightToNitsSpline.interpolate(backlight); 248 } 249 250 /** 251 * Return an array of equal length to backlight and nits, that covers the entire system 252 * brightness range of 0.0-1.0. 253 * 254 * @return brightness array 255 */ getBrightness()256 public float[] getBrightness() { 257 return mBrightness; 258 } 259 260 /** 261 * Return the default brightness on a scale of 0.0f - 1.0f 262 * 263 * @return default brightness 264 */ getBrightnessDefault()265 public float getBrightnessDefault() { 266 return mBrightnessDefault; 267 } 268 getBrightnessRampFastDecrease()269 public float getBrightnessRampFastDecrease() { 270 return mBrightnessRampFastDecrease; 271 } 272 getBrightnessRampFastIncrease()273 public float getBrightnessRampFastIncrease() { 274 return mBrightnessRampFastIncrease; 275 } 276 getBrightnessRampSlowDecrease()277 public float getBrightnessRampSlowDecrease() { 278 return mBrightnessRampSlowDecrease; 279 } 280 getBrightnessRampSlowIncrease()281 public float getBrightnessRampSlowIncrease() { 282 return mBrightnessRampSlowIncrease; 283 } 284 getAmbientLightSensor()285 SensorData getAmbientLightSensor() { 286 return mAmbientLightSensor; 287 } 288 getProximitySensor()289 SensorData getProximitySensor() { 290 return mProximitySensor; 291 } 292 293 /** 294 * @param quirkValue The quirk to test. 295 * @return {@code true} if the specified quirk is present in this configuration, 296 * {@code false} otherwise. 297 */ hasQuirk(String quirkValue)298 public boolean hasQuirk(String quirkValue) { 299 return mQuirks != null && mQuirks.contains(quirkValue); 300 } 301 302 /** 303 * @return high brightness mode configuration data for the display. 304 */ getHighBrightnessModeData()305 public HighBrightnessModeData getHighBrightnessModeData() { 306 if (!mIsHighBrightnessModeEnabled || mHbmData == null) { 307 return null; 308 } 309 310 HighBrightnessModeData hbmData = new HighBrightnessModeData(); 311 mHbmData.copyTo(hbmData); 312 return hbmData; 313 } 314 getRefreshRateLimitations()315 public List<RefreshRateLimitation> getRefreshRateLimitations() { 316 return mRefreshRateLimitations; 317 } 318 319 @Override toString()320 public String toString() { 321 String str = "DisplayDeviceConfig{" 322 + "mLoadedFrom=" + mLoadedFrom 323 + ", mBacklight=" + Arrays.toString(mBacklight) 324 + ", mNits=" + Arrays.toString(mNits) 325 + ", mRawBacklight=" + Arrays.toString(mRawBacklight) 326 + ", mRawNits=" + Arrays.toString(mRawNits) 327 + ", mBrightness=" + Arrays.toString(mBrightness) 328 + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline 329 + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline 330 + ", mBacklightMinimum=" + mBacklightMinimum 331 + ", mBacklightMaximum=" + mBacklightMaximum 332 + ", mBrightnessDefault=" + mBrightnessDefault 333 + ", mQuirks=" + mQuirks 334 + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled 335 + ", mHbmData=" + mHbmData 336 + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease 337 + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease 338 + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease 339 + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease 340 + ", mAmbientLightSensor=" + mAmbientLightSensor 341 + ", mProximitySensor=" + mProximitySensor 342 + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray()) 343 + "}"; 344 return str; 345 } 346 getConfigFromSuffix(Context context, File baseDirectory, String suffixFormat, long idNumber)347 private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory, 348 String suffixFormat, long idNumber) { 349 350 final String suffix = String.format(suffixFormat, idNumber); 351 final String filename = String.format(CONFIG_FILE_FORMAT, suffix); 352 final File filePath = Environment.buildPath( 353 baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename); 354 final DisplayDeviceConfig config = new DisplayDeviceConfig(context); 355 if (config.initFromFile(filePath)) { 356 return config; 357 } 358 return null; 359 } 360 getConfigFromGlobalXml(Context context)361 private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) { 362 DisplayDeviceConfig config = new DisplayDeviceConfig(context); 363 config.initFromGlobalXml(); 364 return config; 365 } 366 getConfigFromPmValues(Context context)367 private static DisplayDeviceConfig getConfigFromPmValues(Context context) { 368 DisplayDeviceConfig config = new DisplayDeviceConfig(context); 369 config.initFromDefaultValues(); 370 return config; 371 } 372 initFromFile(File configFile)373 private boolean initFromFile(File configFile) { 374 if (!configFile.exists()) { 375 // Display configuration files aren't required to exist. 376 return false; 377 } 378 379 if (!configFile.isFile()) { 380 Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping"); 381 return false; 382 } 383 384 try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { 385 final DisplayConfiguration config = XmlParser.read(in); 386 if (config != null) { 387 loadBrightnessDefaultFromDdcXml(config); 388 loadBrightnessConstraintsFromConfigXml(); 389 loadBrightnessMap(config); 390 loadHighBrightnessModeData(config); 391 loadQuirks(config); 392 loadBrightnessRamps(config); 393 loadAmbientLightSensorFromDdc(config); 394 loadProxSensorFromDdc(config); 395 } else { 396 Slog.w(TAG, "DisplayDeviceConfig file is null"); 397 } 398 } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { 399 Slog.e(TAG, "Encountered an error while reading/parsing display config file: " 400 + configFile, e); 401 } 402 mLoadedFrom = configFile.toString(); 403 return true; 404 } 405 initFromGlobalXml()406 private void initFromGlobalXml() { 407 // If no ddc exists, use config.xml 408 loadBrightnessDefaultFromConfigXml(); 409 loadBrightnessConstraintsFromConfigXml(); 410 loadBrightnessMapFromConfigXml(); 411 loadBrightnessRampsFromConfigXml(); 412 loadAmbientLightSensorFromConfigXml(); 413 setProxSensorUnspecified(); 414 mLoadedFrom = "<config.xml>"; 415 } 416 initFromDefaultValues()417 private void initFromDefaultValues() { 418 // Set all to basic values 419 mLoadedFrom = "Static values"; 420 mBacklightMinimum = PowerManager.BRIGHTNESS_MIN; 421 mBacklightMaximum = PowerManager.BRIGHTNESS_MAX; 422 mBrightnessDefault = BRIGHTNESS_DEFAULT; 423 mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX; 424 mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX; 425 mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX; 426 mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX; 427 setSimpleMappingStrategyValues(); 428 loadAmbientLightSensorFromConfigXml(); 429 setProxSensorUnspecified(); 430 } 431 loadBrightnessDefaultFromDdcXml(DisplayConfiguration config)432 private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) { 433 // Default brightness values are stored in the displayDeviceConfig file, 434 // Or we fallback standard values if not. 435 // Priority 1: Value in the displayDeviceConfig 436 // Priority 2: Value in the config.xml (float) 437 // Priority 3: Value in the config.xml (int) 438 if (config != null) { 439 BigDecimal configBrightnessDefault = config.getScreenBrightnessDefault(); 440 if (configBrightnessDefault != null) { 441 mBrightnessDefault = configBrightnessDefault.floatValue(); 442 } else { 443 loadBrightnessDefaultFromConfigXml(); 444 } 445 } 446 } 447 loadBrightnessDefaultFromConfigXml()448 private void loadBrightnessDefaultFromConfigXml() { 449 // Priority 1: Value in the config.xml (float) 450 // Priority 2: Value in the config.xml (int) 451 final float def = mContext.getResources().getFloat(com.android.internal.R.dimen 452 .config_screenBrightnessSettingDefaultFloat); 453 if (def == INVALID_BRIGHTNESS_IN_CONFIG) { 454 mBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat( 455 mContext.getResources().getInteger(com.android.internal.R.integer 456 .config_screenBrightnessSettingDefault)); 457 } else { 458 mBrightnessDefault = def; 459 } 460 } 461 loadBrightnessConstraintsFromConfigXml()462 private void loadBrightnessConstraintsFromConfigXml() { 463 // TODO(b/175373898) add constraints (min / max) to ddc. 464 final float min = mContext.getResources().getFloat(com.android.internal.R.dimen 465 .config_screenBrightnessSettingMinimumFloat); 466 final float max = mContext.getResources().getFloat(com.android.internal.R.dimen 467 .config_screenBrightnessSettingMaximumFloat); 468 if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) { 469 mBacklightMinimum = BrightnessSynchronizer.brightnessIntToFloat( 470 mContext.getResources().getInteger(com.android.internal.R.integer 471 .config_screenBrightnessSettingMinimum)); 472 mBacklightMaximum = BrightnessSynchronizer.brightnessIntToFloat( 473 mContext.getResources().getInteger(com.android.internal.R.integer 474 .config_screenBrightnessSettingMaximum)); 475 } else { 476 mBacklightMinimum = min; 477 mBacklightMaximum = max; 478 } 479 } 480 loadBrightnessMap(DisplayConfiguration config)481 private void loadBrightnessMap(DisplayConfiguration config) { 482 final NitsMap map = config.getScreenBrightnessMap(); 483 // Map may not exist in display device config 484 if (map == null) { 485 loadBrightnessMapFromConfigXml(); 486 return; 487 } 488 489 // Use the (preferred) display device config mapping 490 final List<Point> points = map.getPoint(); 491 final int size = points.size(); 492 493 float[] nits = new float[size]; 494 float[] backlight = new float[size]; 495 496 int i = 0; 497 for (Point point : points) { 498 nits[i] = point.getNits().floatValue(); 499 backlight[i] = point.getValue().floatValue(); 500 if (i > 0) { 501 if (nits[i] < nits[i - 1]) { 502 Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest " 503 + " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]); 504 return; 505 } 506 507 if (backlight[i] < backlight[i - 1]) { 508 Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest " 509 + " of configuration. Value: " + backlight[i] + " < " 510 + backlight[i - 1]); 511 return; 512 } 513 } 514 ++i; 515 } 516 mRawNits = nits; 517 mRawBacklight = backlight; 518 constrainNitsAndBacklightArrays(); 519 } 520 loadBrightnessMapFromConfigXml()521 private void loadBrightnessMapFromConfigXml() { 522 // Use the config.xml mapping 523 final Resources res = mContext.getResources(); 524 final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray( 525 com.android.internal.R.array.config_screenBrightnessNits)); 526 final int[] sysBrightness = res.getIntArray( 527 com.android.internal.R.array.config_screenBrightnessBacklight); 528 final float[] sysBrightnessFloat = new float[sysBrightness.length]; 529 530 for (int i = 0; i < sysBrightness.length; i++) { 531 sysBrightnessFloat[i] = BrightnessSynchronizer.brightnessIntToFloat( 532 sysBrightness[i]); 533 } 534 535 // These arrays are allowed to be empty, we set null values so that 536 // BrightnessMappingStrategy will create a SimpleMappingStrategy instead. 537 if (sysBrightnessFloat.length == 0 || sysNits.length == 0) { 538 setSimpleMappingStrategyValues(); 539 return; 540 } 541 542 mRawNits = sysNits; 543 mRawBacklight = sysBrightnessFloat; 544 constrainNitsAndBacklightArrays(); 545 } 546 setSimpleMappingStrategyValues()547 private void setSimpleMappingStrategyValues() { 548 // No translation from backlight to brightness should occur if we are using a 549 // SimpleMappingStrategy (ie they should be the same) so the splines are 550 // set to be linear, between 0.0 and 1.0 551 mNits = null; 552 mBacklight = null; 553 float[] simpleMappingStrategyArray = new float[]{0.0f, 1.0f}; 554 mBrightnessToBacklightSpline = Spline.createSpline(simpleMappingStrategyArray, 555 simpleMappingStrategyArray); 556 mBacklightToBrightnessSpline = Spline.createSpline(simpleMappingStrategyArray, 557 simpleMappingStrategyArray); 558 } 559 560 /** 561 * Change the nits and backlight arrays, so that they cover only the allowed backlight values 562 * Use the brightness minimum and maximum values to clamp these arrays. 563 */ constrainNitsAndBacklightArrays()564 private void constrainNitsAndBacklightArrays() { 565 if (mRawBacklight[0] > mBacklightMinimum 566 || mRawBacklight[mRawBacklight.length - 1] < mBacklightMaximum 567 || mBacklightMinimum > mBacklightMaximum) { 568 throw new IllegalStateException("Min or max values are invalid" 569 + "; raw min=" + mRawBacklight[0] 570 + "; raw max=" + mRawBacklight[mRawBacklight.length - 1] 571 + "; backlight min=" + mBacklightMinimum 572 + "; backlight max=" + mBacklightMaximum); 573 } 574 575 float[] newNits = new float[mRawBacklight.length]; 576 float[] newBacklight = new float[mRawBacklight.length]; 577 // Find the starting index of the clamped arrays. This may be less than the min so 578 // we'll need to clamp this value still when actually doing the remapping. 579 int newStart = 0; 580 for (int i = 0; i < mRawBacklight.length - 1; i++) { 581 if (mRawBacklight[i + 1] > mBacklightMinimum) { 582 newStart = i; 583 break; 584 } 585 } 586 587 boolean isLastValue = false; 588 int newIndex = 0; 589 for (int i = newStart; i < mRawBacklight.length && !isLastValue; i++) { 590 newIndex = i - newStart; 591 final float newBacklightVal; 592 final float newNitsVal; 593 isLastValue = mRawBacklight[i] > mBacklightMaximum 594 || i >= mRawBacklight.length - 1; 595 // Clamp beginning and end to valid backlight values. 596 if (newIndex == 0) { 597 newBacklightVal = MathUtils.max(mRawBacklight[i], mBacklightMinimum); 598 newNitsVal = rawBacklightToNits(i, newBacklightVal); 599 } else if (isLastValue) { 600 newBacklightVal = MathUtils.min(mRawBacklight[i], mBacklightMaximum); 601 newNitsVal = rawBacklightToNits(i - 1, newBacklightVal); 602 } else { 603 newBacklightVal = mRawBacklight[i]; 604 newNitsVal = mRawNits[i]; 605 } 606 newBacklight[newIndex] = newBacklightVal; 607 newNits[newIndex] = newNitsVal; 608 } 609 mBacklight = Arrays.copyOf(newBacklight, newIndex + 1); 610 mNits = Arrays.copyOf(newNits, newIndex + 1); 611 createBacklightConversionSplines(); 612 } 613 rawBacklightToNits(int i, float backlight)614 private float rawBacklightToNits(int i, float backlight) { 615 return MathUtils.map(mRawBacklight[i], mRawBacklight[i + 1], 616 mRawNits[i], mRawNits[i + 1], backlight); 617 } 618 619 // This method creates a brightness spline that is of equal length with proportional increments 620 // to the backlight spline. The values of this array range from 0.0f to 1.0f instead of the 621 // potential constrained range that the backlight array covers 622 // These splines are used to convert from the system brightness value to the HAL backlight 623 // value createBacklightConversionSplines()624 private void createBacklightConversionSplines() { 625 mBrightness = new float[mBacklight.length]; 626 for (int i = 0; i < mBrightness.length; i++) { 627 mBrightness[i] = MathUtils.map(mBacklight[0], 628 mBacklight[mBacklight.length - 1], 629 PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]); 630 } 631 mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight); 632 mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness); 633 mBacklightToNitsSpline = Spline.createSpline(mBacklight, mNits); 634 } 635 loadQuirks(DisplayConfiguration config)636 private void loadQuirks(DisplayConfiguration config) { 637 final DisplayQuirks quirks = config.getQuirks(); 638 if (quirks != null) { 639 mQuirks = new ArrayList<>(quirks.getQuirk()); 640 } 641 } 642 loadHighBrightnessModeData(DisplayConfiguration config)643 private void loadHighBrightnessModeData(DisplayConfiguration config) { 644 final HighBrightnessMode hbm = config.getHighBrightnessMode(); 645 if (hbm != null) { 646 mIsHighBrightnessModeEnabled = hbm.getEnabled(); 647 mHbmData = new HighBrightnessModeData(); 648 mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue(); 649 float transitionPointBacklightScale = hbm.getTransitionPoint_all().floatValue(); 650 if (transitionPointBacklightScale >= mBacklightMaximum) { 651 throw new IllegalArgumentException("HBM transition point invalid. " 652 + mHbmData.transitionPoint + " is not less than " 653 + mBacklightMaximum); 654 } 655 mHbmData.transitionPoint = 656 mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale); 657 final HbmTiming hbmTiming = hbm.getTiming_all(); 658 mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000; 659 mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000; 660 mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000; 661 mHbmData.thermalStatusLimit = convertThermalStatus(hbm.getThermalStatusLimit_all()); 662 mHbmData.allowInLowPowerMode = hbm.getAllowInLowPowerMode_all(); 663 final RefreshRateRange rr = hbm.getRefreshRate_all(); 664 if (rr != null) { 665 final float min = rr.getMinimum().floatValue(); 666 final float max = rr.getMaximum().floatValue(); 667 mRefreshRateLimitations.add(new RefreshRateLimitation( 668 DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max)); 669 } 670 } 671 } 672 loadBrightnessRamps(DisplayConfiguration config)673 private void loadBrightnessRamps(DisplayConfiguration config) { 674 // Priority 1: Value in the display device config (float) 675 // Priority 2: Value in the config.xml (int) 676 final BigDecimal fastDownDecimal = config.getScreenBrightnessRampFastDecrease(); 677 final BigDecimal fastUpDecimal = config.getScreenBrightnessRampFastIncrease(); 678 final BigDecimal slowDownDecimal = config.getScreenBrightnessRampSlowDecrease(); 679 final BigDecimal slowUpDecimal = config.getScreenBrightnessRampSlowIncrease(); 680 681 if (fastDownDecimal != null && fastUpDecimal != null && slowDownDecimal != null 682 && slowUpDecimal != null) { 683 mBrightnessRampFastDecrease = fastDownDecimal.floatValue(); 684 mBrightnessRampFastIncrease = fastUpDecimal.floatValue(); 685 mBrightnessRampSlowDecrease = slowDownDecimal.floatValue(); 686 mBrightnessRampSlowIncrease = slowUpDecimal.floatValue(); 687 } else { 688 if (fastDownDecimal != null || fastUpDecimal != null || slowDownDecimal != null 689 || slowUpDecimal != null) { 690 Slog.w(TAG, "Per display brightness ramp values ignored because not all " 691 + "values are present in display device config"); 692 } 693 loadBrightnessRampsFromConfigXml(); 694 } 695 } 696 loadBrightnessRampsFromConfigXml()697 private void loadBrightnessRampsFromConfigXml() { 698 mBrightnessRampFastIncrease = BrightnessSynchronizer.brightnessIntToFloat( 699 mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_fast)); 700 mBrightnessRampSlowIncrease = BrightnessSynchronizer.brightnessIntToFloat( 701 mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_slow)); 702 // config.xml uses the same values for both increasing and decreasing brightness 703 // transitions so we assign them to the same values here. 704 mBrightnessRampFastDecrease = mBrightnessRampFastIncrease; 705 mBrightnessRampSlowDecrease = mBrightnessRampSlowIncrease; 706 } 707 loadAmbientLightSensorFromConfigXml()708 private void loadAmbientLightSensorFromConfigXml() { 709 mAmbientLightSensor.name = ""; 710 mAmbientLightSensor.type = mContext.getResources().getString( 711 com.android.internal.R.string.config_displayLightSensorType); 712 } 713 loadAmbientLightSensorFromDdc(DisplayConfiguration config)714 private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) { 715 final SensorDetails sensorDetails = config.getLightSensor(); 716 if (sensorDetails != null) { 717 mAmbientLightSensor.type = sensorDetails.getType(); 718 mAmbientLightSensor.name = sensorDetails.getName(); 719 final RefreshRateRange rr = sensorDetails.getRefreshRate(); 720 if (rr != null) { 721 mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue(); 722 mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue(); 723 } 724 } else { 725 loadAmbientLightSensorFromConfigXml(); 726 } 727 } 728 setProxSensorUnspecified()729 private void setProxSensorUnspecified() { 730 mProximitySensor.name = ""; 731 mProximitySensor.type = ""; 732 } 733 loadProxSensorFromDdc(DisplayConfiguration config)734 private void loadProxSensorFromDdc(DisplayConfiguration config) { 735 SensorDetails sensorDetails = config.getProxSensor(); 736 if (sensorDetails != null) { 737 mProximitySensor.name = sensorDetails.getName(); 738 mProximitySensor.type = sensorDetails.getType(); 739 final RefreshRateRange rr = sensorDetails.getRefreshRate(); 740 if (rr != null) { 741 mProximitySensor.minRefreshRate = rr.getMinimum().floatValue(); 742 mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue(); 743 } 744 } else { 745 setProxSensorUnspecified(); 746 } 747 } 748 convertThermalStatus(ThermalStatus value)749 private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) { 750 if (value == null) { 751 return PowerManager.THERMAL_STATUS_NONE; 752 } 753 switch (value) { 754 case none: 755 return PowerManager.THERMAL_STATUS_NONE; 756 case light: 757 return PowerManager.THERMAL_STATUS_LIGHT; 758 case moderate: 759 return PowerManager.THERMAL_STATUS_MODERATE; 760 case severe: 761 return PowerManager.THERMAL_STATUS_SEVERE; 762 case critical: 763 return PowerManager.THERMAL_STATUS_CRITICAL; 764 case emergency: 765 return PowerManager.THERMAL_STATUS_EMERGENCY; 766 case shutdown: 767 return PowerManager.THERMAL_STATUS_SHUTDOWN; 768 default: 769 Slog.wtf(TAG, "Unexpected Thermal Status: " + value); 770 return PowerManager.THERMAL_STATUS_NONE; 771 } 772 } 773 774 static class SensorData { 775 public String type; 776 public String name; 777 public float minRefreshRate = 0.0f; 778 public float maxRefreshRate = Float.POSITIVE_INFINITY; 779 780 @Override toString()781 public String toString() { 782 return "Sensor{" 783 + "type: " + type 784 + ", name: " + name 785 + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]" 786 + "} "; 787 } 788 789 /** 790 * @return True if the sensor matches both the specified name and type, or one if only 791 * one is specified (not-empty). Always returns false if both parameters are null or empty. 792 */ matches(String sensorName, String sensorType)793 public boolean matches(String sensorName, String sensorType) { 794 final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); 795 final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); 796 return (isNameSpecified || isTypeSpecified) 797 && (!isNameSpecified || sensorName.equals(name)) 798 && (!isTypeSpecified || sensorType.equals(type)); 799 } 800 } 801 802 /** 803 * Container for high brightness mode configuration data. 804 */ 805 static class HighBrightnessModeData { 806 /** Minimum lux needed to enter high brightness mode */ 807 public float minimumLux; 808 809 /** Brightness level at which we transition from normal to high-brightness. */ 810 public float transitionPoint; 811 812 /** Enable HBM only if the thermal status is not higher than this. */ 813 public @PowerManager.ThermalStatus int thermalStatusLimit; 814 815 /** Whether HBM is allowed when {@code Settings.Global.LOW_POWER_MODE} is active. */ 816 public boolean allowInLowPowerMode; 817 818 /** Time window for HBM. */ 819 public long timeWindowMillis; 820 821 /** Maximum time HBM is allowed to be during in a {@code timeWindowMillis}. */ 822 public long timeMaxMillis; 823 824 /** Minimum time that HBM can be on before being enabled. */ 825 public long timeMinMillis; 826 HighBrightnessModeData()827 HighBrightnessModeData() {} 828 HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis, long timeMaxMillis, long timeMinMillis, @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode)829 HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis, 830 long timeMaxMillis, long timeMinMillis, 831 @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode) { 832 this.minimumLux = minimumLux; 833 this.transitionPoint = transitionPoint; 834 this.timeWindowMillis = timeWindowMillis; 835 this.timeMaxMillis = timeMaxMillis; 836 this.timeMinMillis = timeMinMillis; 837 this.thermalStatusLimit = thermalStatusLimit; 838 this.allowInLowPowerMode = allowInLowPowerMode; 839 } 840 841 /** 842 * Copies the HBM data to the specified parameter instance. 843 * @param other the instance to copy data to. 844 */ copyTo(@onNull HighBrightnessModeData other)845 public void copyTo(@NonNull HighBrightnessModeData other) { 846 other.minimumLux = minimumLux; 847 other.timeWindowMillis = timeWindowMillis; 848 other.timeMaxMillis = timeMaxMillis; 849 other.timeMinMillis = timeMinMillis; 850 other.transitionPoint = transitionPoint; 851 other.thermalStatusLimit = thermalStatusLimit; 852 other.allowInLowPowerMode = allowInLowPowerMode; 853 } 854 855 @Override toString()856 public String toString() { 857 return "HBM{" 858 + "minLux: " + minimumLux 859 + ", transition: " + transitionPoint 860 + ", timeWindow: " + timeWindowMillis + "ms" 861 + ", timeMax: " + timeMaxMillis + "ms" 862 + ", timeMin: " + timeMinMillis + "ms" 863 + ", thermalStatusLimit: " + thermalStatusLimit 864 + ", allowInLowPowerMode: " + allowInLowPowerMode 865 + "} "; 866 } 867 } 868 } 869