blob: 3d7c7e230e8ba83cf05c7e8bce958630feb87671 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.display;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.os.Environment;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.MathUtils;
import android.util.Slog;
import android.util.Spline;
import android.view.DisplayAddress;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.BrightnessThresholds;
import com.android.server.display.config.BrightnessThrottlingMap;
import com.android.server.display.config.BrightnessThrottlingPoint;
import com.android.server.display.config.Density;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.Point;
import com.android.server.display.config.RefreshRateRange;
import com.android.server.display.config.SdrHdrRatioMap;
import com.android.server.display.config.SdrHdrRatioPoint;
import com.android.server.display.config.SensorDetails;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.config.ThermalThrottling;
import com.android.server.display.config.Thresholds;
import com.android.server.display.config.XmlParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.xml.datatype.DatatypeConfigurationException;
/**
* Reads and stores display-specific configurations.
* File format:
* <pre>
* {@code
* <displayConfiguration>
* <densityMapping>
* <density>
* <height>480</height>
* <width>720</width>
* <density>120</density>
* </density>
* <density>
* <height>720</height>
* <width>1280</width>
* <density>213</density>
* </density>
* <density>
* <height>1080</height>
* <width>1920</width>
* <density>320</density>
* </density>
* <density>
* <height>2160</height>
* <width>3840</width>
* <density>640</density>
* </density>
* </densityMapping>
*
* <screenBrightnessMap>
* <point>
* <value>0.0</value>
* <nits>2.0</nits>
* </point>
* <point>
* <value>0.62</value>
* <nits>500.0</nits>
* </point>
* <point>
* <value>1.0</value>
* <nits>800.0</nits>
* </point>
* </screenBrightnessMap>
*
* <screenBrightnessDefault>0.65</screenBrightnessDefault>
*
* <thermalThrottling>
* <brightnessThrottlingMap>
* <brightnessThrottlingPoint>
* <thermalStatus>severe</thermalStatus>
* <brightness>0.1</brightness>
* </brightnessThrottlingPoint>
* <brightnessThrottlingPoint>
* <thermalStatus>critical</thermalStatus>
* <brightness>0.01</brightness>
* </brightnessThrottlingPoint>
* </brightnessThrottlingMap>
* </thermalThrottling>
*
* <highBrightnessMode enabled="true">
* <transitionPoint>0.62</transitionPoint>
* <minimumLux>10000</minimumLux>
* <timing>
* <timeWindowSecs>1800</timeWindowSecs> // Window in which we restrict HBM.
* <timeMaxSecs>300</timeMaxSecs> // Maximum time of HBM allowed in that window.
* <timeMinSecs>60</timeMinSecs> // Minimum time remaining required to switch
* </timing> // HBM on for.
* <refreshRate>
* <minimum>120</minimum>
* <maximum>120</maximum>
* </refreshRate>
* <thermalStatusLimit>light</thermalStatusLimit>
* <allowInLowPowerMode>false</allowInLowPowerMode>
* </highBrightnessMode>
*
* <quirks>
* <quirk>canSetBrightnessViaHwc</quirk>
* </quirks>
*
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
* <screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease>
* <screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>
* <screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>
*
* <screenBrightnessRampIncreaseMaxMillis>2000</screenBrightnessRampIncreaseMaxMillis>
* <screenBrightnessRampDecreaseMaxMillis>3000</screenBrightnessRampDecreaseMaxMillis>
*
* <lightSensor>
* <type>android.sensor.light</type>
* <name>1234 Ambient Light Sensor</name>
* </lightSensor>
* <proxSensor>
* <type>android.sensor.proximity</type>
* <name>1234 Proximity Sensor</name>
* </proxSensor>
*
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
* <ambientLightHorizonShort>2001</ambientLightHorizonShort>
*
* <displayBrightnessChangeThresholds>
* <brighteningThresholds>
* <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten.
* </brighteningThresholds>
* <darkeningThresholds>
* <minimum>0.002</minimum> // Minimum change needed in screen brightness to darken.
* </darkeningThresholds>
* </displayBrightnessChangeThresholds>
*
* <ambientBrightnessChangeThresholds>
* <brighteningThresholds>
* <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten.
* </brighteningThresholds>
* <darkeningThresholds>
* <minimum>0.004</minimum> // Minimum change needed in ambient brightness to darken.
* </darkeningThresholds>
* </ambientBrightnessChangeThresholds>
*
* </displayConfiguration>
* }
* </pre>
*/
public class DisplayDeviceConfig {
private static final String TAG = "DisplayDeviceConfig";
private static final boolean DEBUG = false;
public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN;
public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc";
private static final float BRIGHTNESS_DEFAULT = 0.5f;
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
private static final String DEFAULT_CONFIG_FILE = "default.xml";
private static final String DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT = "default_%s.xml";
private static final String PORT_SUFFIX_FORMAT = "port_%d";
private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
private static final String NO_SUFFIX_FORMAT = "%d";
private static final long STABLE_FLAG = 1L << 62;
private static final int INTERPOLATION_DEFAULT = 0;
private static final int INTERPOLATION_LINEAR = 1;
// Float.NaN (used as invalid for brightness) cannot be stored in config.xml
// so -2 is used instead
private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
private static final float NITS_INVALID = -1;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
private static final int AMBIENT_LIGHT_LONG_HORIZON_MILLIS = 10000;
// Length of the ambient light horizon used to calculate short-term estimate of ambient light.
private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
@VisibleForTesting
static final float HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT = 0.5f;
private final Context mContext;
// The details of the ambient light sensor associated with this display.
private final SensorData mAmbientLightSensor = new SensorData();
// The details of the proximity sensor associated with this display.
private final SensorData mProximitySensor = new SensorData();
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
// Nits and backlight values that are loaded from either the display device config file, or
// config.xml. These are the raw values and just used for the dumpsys
private float[] mRawNits;
private float[] mRawBacklight;
private int mInterpolationType;
// These arrays are calculated from the raw arrays, but clamped to contain values equal to and
// between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same
// length
// Nits array that is used to store the entire range of nits values that the device supports
private float[] mNits;
// Backlight array holds the values that the HAL uses to display the corresponding nits values
private float[] mBacklight;
// Purely an array that covers the ranges of values 0.0 - 1.0, indicating the system brightness
// for the corresponding values above
private float[] mBrightness;
private float mBacklightMinimum = Float.NaN;
private float mBacklightMaximum = Float.NaN;
private float mBrightnessDefault = Float.NaN;
private float mBrightnessRampFastDecrease = Float.NaN;
private float mBrightnessRampFastIncrease = Float.NaN;
private float mBrightnessRampSlowDecrease = Float.NaN;
private float mBrightnessRampSlowIncrease = Float.NaN;
private long mBrightnessRampDecreaseMaxMillis = 0;
private long mBrightnessRampIncreaseMaxMillis = 0;
private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
private float mScreenBrighteningMinThreshold = 0.0f; // Retain behaviour as though there is
private float mScreenDarkeningMinThreshold = 0.0f; // no minimum threshold for change in
private float mAmbientLuxBrighteningMinThreshold = 0.0f; // screen brightness or ambient
private float mAmbientLuxDarkeningMinThreshold = 0.0f; // brightness.
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
private Spline mNitsToBacklightSpline;
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
private DensityMapping mDensityMapping;
private String mLoadedFrom = null;
private Spline mSdrToHdrRatioSpline;
// Brightness Throttling data may be updated via the DeviceConfig. Here we store the original
// data, which comes from the ddc, and the current one, which may be the DeviceConfig
// overwritten value.
private BrightnessThrottlingData mBrightnessThrottlingData;
private BrightnessThrottlingData mOriginalBrightnessThrottlingData;
@VisibleForTesting
DisplayDeviceConfig(Context context) {
mContext = context;
}
/**
* Creates an instance for the specified display.
* Tries to find a file with identifier in the following priority order:
* <ol>
* <li>physicalDisplayId</li>
* <li>physicalDisplayId without a stable flag (old system)</li>
* <li>portId</li>
* </ol>
*
* @param physicalDisplayId The display ID for which to load the configuration.
* @return A configuration instance for the specified display.
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
boolean isFirstDisplay) {
final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
isFirstDisplay);
config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
return config;
}
/**
* Creates an instance using global values since no display device config xml exists.
* Uses values from config or PowerManager.
*
* @param context
* @param useConfigXml
* @return A configuration instance.
*/
public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
final DisplayDeviceConfig config;
if (useConfigXml) {
config = getConfigFromGlobalXml(context);
} else {
config = getConfigFromPmValues(context);
}
return config;
}
private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
long physicalDisplayId, boolean isFirstDisplay) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
physicalDisplayId);
if (config != null) {
return config;
}
config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
physicalDisplayId);
if (config != null) {
return config;
}
// If no config can be loaded from any ddc xml at all,
// prepare a whole config using the global config.xml.
// Guaranteed not null
return create(context, isFirstDisplay);
}
private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
List<File> defaultXmlLocations = new ArrayList<>();
defaultXmlLocations.add(Environment.buildPath(Environment.getProductDirectory(),
ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
defaultXmlLocations.add(Environment.buildPath(Environment.getVendorDirectory(),
ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
// Read config_defaultUiModeType directly because UiModeManager hasn't started yet.
final int uiModeType = context.getResources()
.getInteger(com.android.internal.R.integer.config_defaultUiModeType);
final String uiModeTypeStr = Configuration.getUiModeTypeString(uiModeType);
if (uiModeTypeStr != null) {
defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
ETC_DIR, DISPLAY_CONFIG_DIR,
String.format(DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT, uiModeTypeStr)));
}
defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
final File configFile = getFirstExistingFile(defaultXmlLocations);
if (configFile == null) {
// Display configuration files aren't required to exist.
return null;
}
DisplayConfiguration defaultConfig = null;
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
defaultConfig = XmlParser.read(in);
if (defaultConfig == null) {
Slog.i(TAG, "Default DisplayDeviceConfig file is null");
}
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ configFile, e);
}
return defaultConfig;
}
private static File getFirstExistingFile(Collection<File> files) {
for (File file : files) {
if (file.exists() && file.isFile()) {
return file;
}
}
return null;
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
File baseDirectory, long physicalDisplayId) {
DisplayDeviceConfig config;
// Create config using filename from physical ID (including "stable" bit).
config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
physicalDisplayId);
if (config != null) {
return config;
}
// Create config using filename from physical ID (excluding "stable" bit).
final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
if (config != null) {
return config;
}
// Create config using filename from port ID.
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
int port = physicalAddress.getPort();
config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
return config;
}
void setBrightnessThrottlingData(BrightnessThrottlingData brightnessThrottlingData) {
mBrightnessThrottlingData = brightnessThrottlingData;
}
/**
* Return the brightness mapping nits array.
*
* @return The brightness mapping nits array.
*/
public float[] getNits() {
return mNits;
}
/**
* Return the brightness mapping backlight array.
*
* @return The backlight mapping value array.
*/
public float[] getBacklight() {
return mBacklight;
}
/**
* Calculates the backlight value, as recognised by the HAL, from the brightness value
* given that the rest of the system deals with.
*
* @param brightness value on the framework scale of 0-1
* @return backlight value on the HAL scale of 0-1
*/
public float getBacklightFromBrightness(float brightness) {
return mBrightnessToBacklightSpline.interpolate(brightness);
}
/**
* Calculates the nits value for the specified backlight value if a mapping exists.
*
* @return The mapped nits or 0 if no mapping exits.
*/
public float getNitsFromBacklight(float backlight) {
if (mBacklightToNitsSpline == null) {
Slog.wtf(TAG, "requesting nits when no mapping exists.");
return NITS_INVALID;
}
backlight = Math.max(backlight, mBacklightMinimum);
return mBacklightToNitsSpline.interpolate(backlight);
}
/**
* Calculate the HDR brightness for the specified SDR brightenss.
*
* @return the HDR brightness or BRIGHTNESS_INVALID when no mapping exists.
*/
public float getHdrBrightnessFromSdr(float brightness) {
if (mSdrToHdrRatioSpline == null) {
return PowerManager.BRIGHTNESS_INVALID;
}
float backlight = getBacklightFromBrightness(brightness);
float nits = getNitsFromBacklight(backlight);
if (nits == NITS_INVALID) {
return PowerManager.BRIGHTNESS_INVALID;
}
float ratio = mSdrToHdrRatioSpline.interpolate(nits);
float hdrNits = nits * ratio;
if (mNitsToBacklightSpline == null) {
return PowerManager.BRIGHTNESS_INVALID;
}
float hdrBacklight = mNitsToBacklightSpline.interpolate(hdrNits);
hdrBacklight = Math.max(mBacklightMinimum, Math.min(mBacklightMaximum, hdrBacklight));
float hdrBrightness = mBacklightToBrightnessSpline.interpolate(hdrBacklight);
if (DEBUG) {
Slog.d(TAG, "getHdrBrightnessFromSdr: sdr brightness " + brightness
+ " backlight " + backlight
+ " nits " + nits
+ " ratio " + ratio
+ " hdrNits " + hdrNits
+ " hdrBacklight " + hdrBacklight
+ " hdrBrightness " + hdrBrightness
);
}
return hdrBrightness;
}
/**
* Return an array of equal length to backlight and nits, that covers the entire system
* brightness range of 0.0-1.0.
*
* @return brightness array
*/
public float[] getBrightness() {
return mBrightness;
}
/**
* Return the default brightness on a scale of 0.0f - 1.0f
*
* @return default brightness
*/
public float getBrightnessDefault() {
return mBrightnessDefault;
}
public float getBrightnessRampFastDecrease() {
return mBrightnessRampFastDecrease;
}
public float getBrightnessRampFastIncrease() {
return mBrightnessRampFastIncrease;
}
public float getBrightnessRampSlowDecrease() {
return mBrightnessRampSlowDecrease;
}
public float getBrightnessRampSlowIncrease() {
return mBrightnessRampSlowIncrease;
}
public long getBrightnessRampDecreaseMaxMillis() {
return mBrightnessRampDecreaseMaxMillis;
}
public long getBrightnessRampIncreaseMaxMillis() {
return mBrightnessRampIncreaseMaxMillis;
}
public int getAmbientHorizonLong() {
return mAmbientHorizonLong;
}
public int getAmbientHorizonShort() {
return mAmbientHorizonShort;
}
public float getScreenBrighteningMinThreshold() {
return mScreenBrighteningMinThreshold;
}
public float getScreenDarkeningMinThreshold() {
return mScreenDarkeningMinThreshold;
}
public float getAmbientLuxBrighteningMinThreshold() {
return mAmbientLuxBrighteningMinThreshold;
}
public float getAmbientLuxDarkeningMinThreshold() {
return mAmbientLuxDarkeningMinThreshold;
}
SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
SensorData getProximitySensor() {
return mProximitySensor;
}
/**
* @param quirkValue The quirk to test.
* @return {@code true} if the specified quirk is present in this configuration,
* {@code false} otherwise.
*/
public boolean hasQuirk(String quirkValue) {
return mQuirks != null && mQuirks.contains(quirkValue);
}
/**
* @return high brightness mode configuration data for the display.
*/
public HighBrightnessModeData getHighBrightnessModeData() {
if (!mIsHighBrightnessModeEnabled || mHbmData == null) {
return null;
}
HighBrightnessModeData hbmData = new HighBrightnessModeData();
mHbmData.copyTo(hbmData);
return hbmData;
}
public List<RefreshRateLimitation> getRefreshRateLimitations() {
return mRefreshRateLimitations;
}
public DensityMapping getDensityMapping() {
return mDensityMapping;
}
/**
* @return brightness throttling data configuration data for the display.
*/
public BrightnessThrottlingData getBrightnessThrottlingData() {
return BrightnessThrottlingData.create(mBrightnessThrottlingData);
}
@Override
public String toString() {
return "DisplayDeviceConfig{"
+ "mLoadedFrom=" + mLoadedFrom
+ ", mBacklight=" + Arrays.toString(mBacklight)
+ ", mNits=" + Arrays.toString(mNits)
+ ", mRawBacklight=" + Arrays.toString(mRawBacklight)
+ ", mRawNits=" + Arrays.toString(mRawNits)
+ ", mInterpolationType=" + mInterpolationType
+ ", mBrightness=" + Arrays.toString(mBrightness)
+ ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline
+ ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline
+ ", mNitsToBacklightSpline=" + mNitsToBacklightSpline
+ ", mBacklightMinimum=" + mBacklightMinimum
+ ", mBacklightMaximum=" + mBacklightMaximum
+ ", mBrightnessDefault=" + mBrightnessDefault
+ ", mQuirks=" + mQuirks
+ ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+ ", mHbmData=" + mHbmData
+ ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
+ ", mBrightnessThrottlingData=" + mBrightnessThrottlingData
+ ", mOriginalBrightnessThrottlingData=" + mOriginalBrightnessThrottlingData
+ ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
+ ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+ ", mBrightnessRampDecreaseMaxMillis=" + mBrightnessRampDecreaseMaxMillis
+ ", mBrightnessRampIncreaseMaxMillis=" + mBrightnessRampIncreaseMaxMillis
+ ", mAmbientHorizonLong=" + mAmbientHorizonLong
+ ", mAmbientHorizonShort=" + mAmbientHorizonShort
+ ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
+ ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold
+ ", mAmbientLuxDarkeningMinThreshold=" + mAmbientLuxDarkeningMinThreshold
+ ", mAmbientLuxBrighteningMinThreshold=" + mAmbientLuxBrighteningMinThreshold
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ ", mDensityMapping= " + mDensityMapping
+ "}";
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
String suffixFormat, long idNumber) {
final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
if (config.initFromFile(filePath)) {
return config;
}
return null;
}
private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
DisplayDeviceConfig config = new DisplayDeviceConfig(context);
config.initFromGlobalXml();
return config;
}
private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
DisplayDeviceConfig config = new DisplayDeviceConfig(context);
config.initFromDefaultValues();
return config;
}
@VisibleForTesting
boolean initFromFile(File configFile) {
if (!configFile.exists()) {
// Display configuration files aren't required to exist.
return false;
}
if (!configFile.isFile()) {
Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping");
return false;
}
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
final DisplayConfiguration config = XmlParser.read(in);
if (config != null) {
loadDensityMapping(config);
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
loadBrightnessThrottlingMap(config);
loadHighBrightnessModeData(config);
loadQuirks(config);
loadBrightnessRamps(config);
loadAmbientLightSensorFromDdc(config);
loadProxSensorFromDdc(config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ configFile, e);
}
mLoadedFrom = configFile.toString();
return true;
}
private void initFromGlobalXml() {
// If no ddc exists, use config.xml
loadBrightnessDefaultFromConfigXml();
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMapFromConfigXml();
loadBrightnessRampsFromConfigXml();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
mLoadedFrom = "<config.xml>";
}
private void initFromDefaultValues() {
// Set all to basic values
mLoadedFrom = "Static values";
mBacklightMinimum = PowerManager.BRIGHTNESS_MIN;
mBacklightMaximum = PowerManager.BRIGHTNESS_MAX;
mBrightnessDefault = BRIGHTNESS_DEFAULT;
mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX;
mBrightnessRampDecreaseMaxMillis = 0;
mBrightnessRampIncreaseMaxMillis = 0;
setSimpleMappingStrategyValues();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
}
private void copyUninitializedValuesFromSecondaryConfig(DisplayConfiguration defaultConfig) {
if (defaultConfig == null) {
return;
}
if (mDensityMapping == null) {
loadDensityMapping(defaultConfig);
}
}
private void loadDensityMapping(DisplayConfiguration config) {
if (config.getDensityMapping() == null) {
return;
}
final List<Density> entriesFromXml = config.getDensityMapping().getDensity();
final DensityMapping.Entry[] entries =
new DensityMapping.Entry[entriesFromXml.size()];
for (int i = 0; i < entriesFromXml.size(); i++) {
final Density density = entriesFromXml.get(i);
entries[i] = new DensityMapping.Entry(
density.getWidth().intValue(),
density.getHeight().intValue(),
density.getDensity().intValue());
}
mDensityMapping = DensityMapping.createByOwning(entries);
}
private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
// Default brightness values are stored in the displayDeviceConfig file,
// Or we fallback standard values if not.
// Priority 1: Value in the displayDeviceConfig
// Priority 2: Value in the config.xml (float)
// Priority 3: Value in the config.xml (int)
if (config != null) {
BigDecimal configBrightnessDefault = config.getScreenBrightnessDefault();
if (configBrightnessDefault != null) {
mBrightnessDefault = configBrightnessDefault.floatValue();
} else {
loadBrightnessDefaultFromConfigXml();
}
}
}
private void loadBrightnessDefaultFromConfigXml() {
// Priority 1: Value in the config.xml (float)
// Priority 2: Value in the config.xml (int)
final float def = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingDefaultFloat);
if (def == INVALID_BRIGHTNESS_IN_CONFIG) {
mBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(com.android.internal.R.integer
.config_screenBrightnessSettingDefault));
} else {
mBrightnessDefault = def;
}
}
private void loadBrightnessConstraintsFromConfigXml() {
// TODO(b/175373898) add constraints (min / max) to ddc.
final float min = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMinimumFloat);
final float max = mContext.getResources().getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMaximumFloat);
if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) {
mBacklightMinimum = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(com.android.internal.R.integer
.config_screenBrightnessSettingMinimum));
mBacklightMaximum = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(com.android.internal.R.integer
.config_screenBrightnessSettingMaximum));
} else {
mBacklightMinimum = min;
mBacklightMaximum = max;
}
}
private void loadBrightnessMap(DisplayConfiguration config) {
final NitsMap map = config.getScreenBrightnessMap();
// Map may not exist in display device config
if (map == null) {
loadBrightnessMapFromConfigXml();
return;
}
// Use the (preferred) display device config mapping
final List<Point> points = map.getPoint();
final int size = points.size();
float[] nits = new float[size];
float[] backlight = new float[size];
mInterpolationType = convertInterpolationType(map.getInterpolation());
int i = 0;
for (Point point : points) {
nits[i] = point.getNits().floatValue();
backlight[i] = point.getValue().floatValue();
if (i > 0) {
if (nits[i] < nits[i - 1]) {
Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
+ " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]);
return;
}
if (backlight[i] < backlight[i - 1]) {
Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
+ " of configuration. Value: " + backlight[i] + " < "
+ backlight[i - 1]);
return;
}
}
++i;
}
mRawNits = nits;
mRawBacklight = backlight;
constrainNitsAndBacklightArrays();
}
private Spline loadSdrHdrRatioMap(HighBrightnessMode hbmConfig) {
final SdrHdrRatioMap sdrHdrRatioMap = hbmConfig.getSdrHdrRatioMap_all();
if (sdrHdrRatioMap == null) {
return null;
}
final List<SdrHdrRatioPoint> points = sdrHdrRatioMap.getPoint();
final int size = points.size();
if (size <= 0) {
return null;
}
float[] nits = new float[size];
float[] ratios = new float[size];
int i = 0;
for (SdrHdrRatioPoint point : points) {
nits[i] = point.getSdrNits().floatValue();
if (i > 0) {
if (nits[i] < nits[i - 1]) {
Slog.e(TAG, "sdrHdrRatioMap must be non-decreasing, ignoring rest "
+ " of configuration. nits: " + nits[i] + " < "
+ nits[i - 1]);
return null;
}
}
ratios[i] = point.getHdrRatio().floatValue();
++i;
}
return Spline.createSpline(nits, ratios);
}
private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
final ThermalThrottling throttlingConfig = config.getThermalThrottling();
if (throttlingConfig == null) {
Slog.i(TAG, "no thermal throttling config found");
return;
}
final BrightnessThrottlingMap map = throttlingConfig.getBrightnessThrottlingMap();
if (map == null) {
Slog.i(TAG, "no brightness throttling map found");
return;
}
final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
// At least 1 point is guaranteed by the display device config schema
List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
new ArrayList<>(points.size());
boolean badConfig = false;
for (BrightnessThrottlingPoint point : points) {
ThermalStatus status = point.getThermalStatus();
if (!thermalStatusIsValid(status)) {
badConfig = true;
break;
}
throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
convertThermalStatus(status), point.getBrightness().floatValue()));
}
if (!badConfig) {
mBrightnessThrottlingData = BrightnessThrottlingData.create(throttlingLevels);
mOriginalBrightnessThrottlingData = mBrightnessThrottlingData;
}
}
private void loadBrightnessMapFromConfigXml() {
// Use the config.xml mapping
final Resources res = mContext.getResources();
final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits));
final int[] sysBrightness = res.getIntArray(
com.android.internal.R.array.config_screenBrightnessBacklight);
final float[] sysBrightnessFloat = new float[sysBrightness.length];
for (int i = 0; i < sysBrightness.length; i++) {
sysBrightnessFloat[i] = BrightnessSynchronizer.brightnessIntToFloat(
sysBrightness[i]);
}
// These arrays are allowed to be empty, we set null values so that
// BrightnessMappingStrategy will create a SimpleMappingStrategy instead.
if (sysBrightnessFloat.length == 0 || sysNits.length == 0) {
setSimpleMappingStrategyValues();
return;
}
mRawNits = sysNits;
mRawBacklight = sysBrightnessFloat;
constrainNitsAndBacklightArrays();
}
private void setSimpleMappingStrategyValues() {
// No translation from backlight to brightness should occur if we are using a
// SimpleMappingStrategy (ie they should be the same) so the splines are
// set to be linear, between 0.0 and 1.0
mNits = null;
mBacklight = null;
float[] simpleMappingStrategyArray = new float[]{0.0f, 1.0f};
mBrightnessToBacklightSpline = Spline.createSpline(simpleMappingStrategyArray,
simpleMappingStrategyArray);
mBacklightToBrightnessSpline = Spline.createSpline(simpleMappingStrategyArray,
simpleMappingStrategyArray);
}
/**
* Change the nits and backlight arrays, so that they cover only the allowed backlight values
* Use the brightness minimum and maximum values to clamp these arrays.
*/
private void constrainNitsAndBacklightArrays() {
if (mRawBacklight[0] > mBacklightMinimum
|| mRawBacklight[mRawBacklight.length - 1] < mBacklightMaximum
|| mBacklightMinimum > mBacklightMaximum) {
throw new IllegalStateException("Min or max values are invalid"
+ "; raw min=" + mRawBacklight[0]
+ "; raw max=" + mRawBacklight[mRawBacklight.length - 1]
+ "; backlight min=" + mBacklightMinimum
+ "; backlight max=" + mBacklightMaximum);
}
float[] newNits = new float[mRawBacklight.length];
float[] newBacklight = new float[mRawBacklight.length];
// Find the starting index of the clamped arrays. This may be less than the min so
// we'll need to clamp this value still when actually doing the remapping.
int newStart = 0;
for (int i = 0; i < mRawBacklight.length - 1; i++) {
if (mRawBacklight[i + 1] > mBacklightMinimum) {
newStart = i;
break;
}
}
boolean isLastValue = false;
int newIndex = 0;
for (int i = newStart; i < mRawBacklight.length && !isLastValue; i++) {
newIndex = i - newStart;
final float newBacklightVal;
final float newNitsVal;
isLastValue = mRawBacklight[i] >= mBacklightMaximum
|| i >= mRawBacklight.length - 1;
// Clamp beginning and end to valid backlight values.
if (newIndex == 0) {
newBacklightVal = MathUtils.max(mRawBacklight[i], mBacklightMinimum);
newNitsVal = rawBacklightToNits(i, newBacklightVal);
} else if (isLastValue) {
newBacklightVal = MathUtils.min(mRawBacklight[i], mBacklightMaximum);
newNitsVal = rawBacklightToNits(i - 1, newBacklightVal);
} else {
newBacklightVal = mRawBacklight[i];
newNitsVal = mRawNits[i];
}
newBacklight[newIndex] = newBacklightVal;
newNits[newIndex] = newNitsVal;
}
mBacklight = Arrays.copyOf(newBacklight, newIndex + 1);
mNits = Arrays.copyOf(newNits, newIndex + 1);
createBacklightConversionSplines();
}
private float rawBacklightToNits(int i, float backlight) {
return MathUtils.map(mRawBacklight[i], mRawBacklight[i + 1],
mRawNits[i], mRawNits[i + 1], backlight);
}
// This method creates a brightness spline that is of equal length with proportional increments
// to the backlight spline. The values of this array range from 0.0f to 1.0f instead of the
// potential constrained range that the backlight array covers
// These splines are used to convert from the system brightness value to the HAL backlight
// value
private void createBacklightConversionSplines() {
mBrightness = new float[mBacklight.length];
for (int i = 0; i < mBrightness.length; i++) {
mBrightness[i] = MathUtils.map(mBacklight[0],
mBacklight[mBacklight.length - 1],
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]);
}
mBrightnessToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mBrightness, mBacklight)
: Spline.createSpline(mBrightness, mBacklight);
mBacklightToBrightnessSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mBacklight, mBrightness)
: Spline.createSpline(mBacklight, mBrightness);
mBacklightToNitsSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mBacklight, mNits)
: Spline.createSpline(mBacklight, mNits);
mNitsToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
? Spline.createLinearSpline(mNits, mBacklight)
: Spline.createSpline(mNits, mBacklight);
}
private void loadQuirks(DisplayConfiguration config) {
final DisplayQuirks quirks = config.getQuirks();
if (quirks != null) {
mQuirks = new ArrayList<>(quirks.getQuirk());
}
}
private void loadHighBrightnessModeData(DisplayConfiguration config) {
final HighBrightnessMode hbm = config.getHighBrightnessMode();
if (hbm != null) {
mIsHighBrightnessModeEnabled = hbm.getEnabled();
mHbmData = new HighBrightnessModeData();
mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue();
float transitionPointBacklightScale = hbm.getTransitionPoint_all().floatValue();
if (transitionPointBacklightScale >= mBacklightMaximum) {
throw new IllegalArgumentException("HBM transition point invalid. "
+ mHbmData.transitionPoint + " is not less than "
+ mBacklightMaximum);
}
mHbmData.transitionPoint =
mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale);
final HbmTiming hbmTiming = hbm.getTiming_all();
mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
mHbmData.thermalStatusLimit = convertThermalStatus(hbm.getThermalStatusLimit_all());
mHbmData.allowInLowPowerMode = hbm.getAllowInLowPowerMode_all();
final RefreshRateRange rr = hbm.getRefreshRate_all();
if (rr != null) {
final float min = rr.getMinimum().floatValue();
final float max = rr.getMaximum().floatValue();
mRefreshRateLimitations.add(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
}
BigDecimal minHdrPctOfScreen = hbm.getMinimumHdrPercentOfScreen_all();
if (minHdrPctOfScreen != null) {
mHbmData.minimumHdrPercentOfScreen = minHdrPctOfScreen.floatValue();
if (mHbmData.minimumHdrPercentOfScreen > 1
|| mHbmData.minimumHdrPercentOfScreen < 0) {
Slog.w(TAG, "Invalid minimum HDR percent of screen: "
+ String.valueOf(mHbmData.minimumHdrPercentOfScreen));
mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
}
} else {
mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
}
mSdrToHdrRatioSpline = loadSdrHdrRatioMap(hbm);
}
}
private void loadBrightnessRamps(DisplayConfiguration config) {
// Priority 1: Value in the display device config (float)
// Priority 2: Value in the config.xml (int)
final BigDecimal fastDownDecimal = config.getScreenBrightnessRampFastDecrease();
final BigDecimal fastUpDecimal = config.getScreenBrightnessRampFastIncrease();
final BigDecimal slowDownDecimal = config.getScreenBrightnessRampSlowDecrease();
final BigDecimal slowUpDecimal = config.getScreenBrightnessRampSlowIncrease();
if (fastDownDecimal != null && fastUpDecimal != null && slowDownDecimal != null
&& slowUpDecimal != null) {
mBrightnessRampFastDecrease = fastDownDecimal.floatValue();
mBrightnessRampFastIncrease = fastUpDecimal.floatValue();
mBrightnessRampSlowDecrease = slowDownDecimal.floatValue();
mBrightnessRampSlowIncrease = slowUpDecimal.floatValue();
} else {
if (fastDownDecimal != null || fastUpDecimal != null || slowDownDecimal != null
|| slowUpDecimal != null) {
Slog.w(TAG, "Per display brightness ramp values ignored because not all "
+ "values are present in display device config");
}
loadBrightnessRampsFromConfigXml();
}
final BigInteger increaseMax = config.getScreenBrightnessRampIncreaseMaxMillis();
if (increaseMax != null) {
mBrightnessRampIncreaseMaxMillis = increaseMax.intValue();
}
final BigInteger decreaseMax = config.getScreenBrightnessRampDecreaseMaxMillis();
if (decreaseMax != null) {
mBrightnessRampDecreaseMaxMillis = decreaseMax.intValue();
}
}
private void loadBrightnessRampsFromConfigXml() {
mBrightnessRampFastIncrease = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_fast));
mBrightnessRampSlowIncrease = BrightnessSynchronizer.brightnessIntToFloat(
mContext.getResources().getInteger(R.integer.config_brightness_ramp_rate_slow));
// config.xml uses the same values for both increasing and decreasing brightness
// transitions so we assign them to the same values here.
mBrightnessRampFastDecrease = mBrightnessRampFastIncrease;
mBrightnessRampSlowDecrease = mBrightnessRampSlowIncrease;
}
private void loadAmbientLightSensorFromConfigXml() {
mAmbientLightSensor.name = "";
mAmbientLightSensor.type = mContext.getResources().getString(
com.android.internal.R.string.config_displayLightSensorType);
}
private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) {
final SensorDetails sensorDetails = config.getLightSensor();
if (sensorDetails != null) {
mAmbientLightSensor.type = sensorDetails.getType();
mAmbientLightSensor.name = sensorDetails.getName();
final RefreshRateRange rr = sensorDetails.getRefreshRate();
if (rr != null) {
mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue();
mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue();
}
} else {
loadAmbientLightSensorFromConfigXml();
}
}
private void setProxSensorUnspecified() {
mProximitySensor.name = "";
mProximitySensor.type = "";
}
private void loadProxSensorFromDdc(DisplayConfiguration config) {
SensorDetails sensorDetails = config.getProxSensor();
if (sensorDetails != null) {
mProximitySensor.name = sensorDetails.getName();
mProximitySensor.type = sensorDetails.getType();
final RefreshRateRange rr = sensorDetails.getRefreshRate();
if (rr != null) {
mProximitySensor.minRefreshRate = rr.getMinimum().floatValue();
mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue();
}
} else {
setProxSensorUnspecified();
}
}
private void loadBrightnessChangeThresholds(DisplayConfiguration config) {
Thresholds displayBrightnessThresholds = config.getDisplayBrightnessChangeThresholds();
Thresholds ambientBrightnessThresholds = config.getAmbientBrightnessChangeThresholds();
if (displayBrightnessThresholds != null) {
BrightnessThresholds brighteningScreen =
displayBrightnessThresholds.getBrighteningThresholds();
BrightnessThresholds darkeningScreen =
displayBrightnessThresholds.getDarkeningThresholds();
final BigDecimal screenBrighteningThreshold = brighteningScreen.getMinimum();
final BigDecimal screenDarkeningThreshold = darkeningScreen.getMinimum();
if (screenBrighteningThreshold != null) {
mScreenBrighteningMinThreshold = screenBrighteningThreshold.floatValue();
}
if (screenDarkeningThreshold != null) {
mScreenDarkeningMinThreshold = screenDarkeningThreshold.floatValue();
}
}
if (ambientBrightnessThresholds != null) {
BrightnessThresholds brighteningAmbientLux =
ambientBrightnessThresholds.getBrighteningThresholds();
BrightnessThresholds darkeningAmbientLux =
ambientBrightnessThresholds.getDarkeningThresholds();
final BigDecimal ambientBrighteningThreshold = brighteningAmbientLux.getMinimum();
final BigDecimal ambientDarkeningThreshold = darkeningAmbientLux.getMinimum();
if (ambientBrighteningThreshold != null) {
mAmbientLuxBrighteningMinThreshold = ambientBrighteningThreshold.floatValue();
}
if (ambientDarkeningThreshold != null) {
mAmbientLuxDarkeningMinThreshold = ambientDarkeningThreshold.floatValue();
}
}
}
private boolean thermalStatusIsValid(ThermalStatus value) {
if (value == null) {
return false;
}
switch (value) {
case none:
case light:
case moderate:
case severe:
case critical:
case emergency:
case shutdown:
return true;
default:
return false;
}
}
private @PowerManager.ThermalStatus int convertThermalStatus(ThermalStatus value) {
if (value == null) {
return PowerManager.THERMAL_STATUS_NONE;
}
switch (value) {
case none:
return PowerManager.THERMAL_STATUS_NONE;
case light:
return PowerManager.THERMAL_STATUS_LIGHT;
case moderate:
return PowerManager.THERMAL_STATUS_MODERATE;
case severe:
return PowerManager.THERMAL_STATUS_SEVERE;
case critical:
return PowerManager.THERMAL_STATUS_CRITICAL;
case emergency:
return PowerManager.THERMAL_STATUS_EMERGENCY;
case shutdown:
return PowerManager.THERMAL_STATUS_SHUTDOWN;
default:
Slog.wtf(TAG, "Unexpected Thermal Status: " + value);
return PowerManager.THERMAL_STATUS_NONE;
}
}
private int convertInterpolationType(String value) {
if (TextUtils.isEmpty(value)) {
return INTERPOLATION_DEFAULT;
}
if ("linear".equals(value)) {
return INTERPOLATION_LINEAR;
}
Slog.wtf(TAG, "Unexpected Interpolation Type: " + value);
return INTERPOLATION_DEFAULT;
}
private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
final BigInteger configLongHorizon = config.getAmbientLightHorizonLong();
if (configLongHorizon != null) {
mAmbientHorizonLong = configLongHorizon.intValue();
}
final BigInteger configShortHorizon = config.getAmbientLightHorizonShort();
if (configShortHorizon != null) {
mAmbientHorizonShort = configShortHorizon.intValue();
}
}
static class SensorData {
public String type;
public String name;
public float minRefreshRate = 0.0f;
public float maxRefreshRate = Float.POSITIVE_INFINITY;
@Override
public String toString() {
return "Sensor{"
+ "type: " + type
+ ", name: " + name
+ ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]"
+ "} ";
}
/**
* @return True if the sensor matches both the specified name and type, or one if only
* one is specified (not-empty). Always returns false if both parameters are null or empty.
*/
public boolean matches(String sensorName, String sensorType) {
final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
return (isNameSpecified || isTypeSpecified)
&& (!isNameSpecified || sensorName.equals(name))
&& (!isTypeSpecified || sensorType.equals(type));
}
}
/**
* Container for high brightness mode configuration data.
*/
static class HighBrightnessModeData {
/** Minimum lux needed to enter high brightness mode */
public float minimumLux;
/** Brightness level at which we transition from normal to high-brightness. */
public float transitionPoint;
/** Enable HBM only if the thermal status is not higher than this. */
public @PowerManager.ThermalStatus int thermalStatusLimit;
/** Whether HBM is allowed when {@code Settings.Global.LOW_POWER_MODE} is active. */
public boolean allowInLowPowerMode;
/** Time window for HBM. */
public long timeWindowMillis;
/** Maximum time HBM is allowed to be during in a {@code timeWindowMillis}. */
public long timeMaxMillis;
/** Minimum time that HBM can be on before being enabled. */
public long timeMinMillis;
/** Minimum HDR video size to enter high brightness mode */
public float minimumHdrPercentOfScreen;
HighBrightnessModeData() {}
HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis,
long timeMaxMillis, long timeMinMillis,
@PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode,
float minimumHdrPercentOfScreen) {
this.minimumLux = minimumLux;
this.transitionPoint = transitionPoint;
this.timeWindowMillis = timeWindowMillis;
this.timeMaxMillis = timeMaxMillis;
this.timeMinMillis = timeMinMillis;
this.thermalStatusLimit = thermalStatusLimit;
this.allowInLowPowerMode = allowInLowPowerMode;
this.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
/**
* Copies the HBM data to the specified parameter instance.
* @param other the instance to copy data to.
*/
public void copyTo(@NonNull HighBrightnessModeData other) {
other.minimumLux = minimumLux;
other.timeWindowMillis = timeWindowMillis;
other.timeMaxMillis = timeMaxMillis;
other.timeMinMillis = timeMinMillis;
other.transitionPoint = transitionPoint;
other.thermalStatusLimit = thermalStatusLimit;
other.allowInLowPowerMode = allowInLowPowerMode;
other.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
@Override
public String toString() {
return "HBM{"
+ "minLux: " + minimumLux
+ ", transition: " + transitionPoint
+ ", timeWindow: " + timeWindowMillis + "ms"
+ ", timeMax: " + timeMaxMillis + "ms"
+ ", timeMin: " + timeMinMillis + "ms"
+ ", thermalStatusLimit: " + thermalStatusLimit
+ ", allowInLowPowerMode: " + allowInLowPowerMode
+ ", minimumHdrPercentOfScreen: " + minimumHdrPercentOfScreen
+ "} ";
}
}
/**
* Container for brightness throttling data.
*/
public static class BrightnessThrottlingData {
public List<ThrottlingLevel> throttlingLevels;
static class ThrottlingLevel {
public @PowerManager.ThermalStatus int thermalStatus;
public float brightness;
ThrottlingLevel(@PowerManager.ThermalStatus int thermalStatus, float brightness) {
this.thermalStatus = thermalStatus;
this.brightness = brightness;
}
@Override
public String toString() {
return "[" + thermalStatus + "," + brightness + "]";
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ThrottlingLevel)) {
return false;
}
ThrottlingLevel otherThrottlingLevel = (ThrottlingLevel) obj;
return otherThrottlingLevel.thermalStatus == this.thermalStatus
&& otherThrottlingLevel.brightness == this.brightness;
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + thermalStatus;
result = 31 * result + Float.hashCode(brightness);
return result;
}
}
static public BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels)
{
if (throttlingLevels == null || throttlingLevels.size() == 0) {
Slog.e(TAG, "BrightnessThrottlingData received null or empty throttling levels");
return null;
}
ThrottlingLevel prevLevel = throttlingLevels.get(0);
final int numLevels = throttlingLevels.size();
for (int i = 1; i < numLevels; i++) {
ThrottlingLevel thisLevel = throttlingLevels.get(i);
if (thisLevel.thermalStatus <= prevLevel.thermalStatus) {
Slog.e(TAG, "brightnessThrottlingMap must be strictly increasing, ignoring "
+ "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= "
+ prevLevel.thermalStatus);
return null;
}
if (thisLevel.brightness >= prevLevel.brightness) {
Slog.e(TAG, "brightnessThrottlingMap must be strictly decreasing, ignoring "
+ "configuration. Brightness " + thisLevel.brightness + " >= "
+ thisLevel.brightness);
return null;
}
prevLevel = thisLevel;
}
for (ThrottlingLevel level : throttlingLevels) {
// Non-negative brightness values are enforced by device config schema
if (level.brightness > PowerManager.BRIGHTNESS_MAX) {
Slog.e(TAG, "brightnessThrottlingMap contains a brightness value exceeding "
+ "system max. Brightness " + level.brightness + " > "
+ PowerManager.BRIGHTNESS_MAX);
return null;
}
}
return new BrightnessThrottlingData(throttlingLevels);
}
static public BrightnessThrottlingData create(BrightnessThrottlingData other) {
if (other == null)
return null;
return BrightnessThrottlingData.create(other.throttlingLevels);
}
@Override
public String toString() {
return "BrightnessThrottlingData{"
+ "throttlingLevels:" + throttlingLevels
+ "} ";
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BrightnessThrottlingData)) {
return false;
}
BrightnessThrottlingData otherBrightnessThrottlingData = (BrightnessThrottlingData) obj;
return throttlingLevels.equals(otherBrightnessThrottlingData.throttlingLevels);
}
@Override
public int hashCode() {
return throttlingLevels.hashCode();
}
private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
throttlingLevels = new ArrayList<>(inLevels.size());
for (ThrottlingLevel level : inLevels) {
throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
}
}
}
}