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