Add reduce bright colors to brightness tracker

Bug: 168065315
Test: atest FrameworksServicesTests:ColorDisplayServiceTest and
atest FrameworksServicesTests:ReduceBrightColorsTintControllerTest and
atest FrameworksServicesTests:BrightnessMappingStrategyTest

Change-Id: I1913d45dfda8ee6ba80346541d04371a9fa90826
This commit is contained in:
Christine Franks
2020-12-14 01:04:32 -08:00
parent 421ce84db7
commit 057955ebbd
14 changed files with 649 additions and 59 deletions

View File

@@ -2875,6 +2875,9 @@ package android.hardware.display {
field public final boolean nightMode;
field public final String packageName;
field public final float powerBrightnessFactor;
field public final boolean reduceBrightColors;
field public final float reduceBrightColorsOffset;
field public final int reduceBrightColorsStrength;
field public final long timeStamp;
}

View File

@@ -65,6 +65,23 @@ public final class BrightnessChangeEvent implements Parcelable {
/** If night mode color filter is active this will be the temperature in kelvin */
public final int colorTemperature;
/** Whether the bright color reduction color transform is active */
public final boolean reduceBrightColors;
/** How strong the bright color reduction color transform is set (only applicable if active),
* specified as an integer from 0 - 100, inclusive. This value (scaled to 0-1, inclusive) is
* then used in Ynew = (a * scaledStrength^2 + b * scaledStrength + c) * Ycurrent, where a, b,
* and c are coefficients provided in the bright color reduction coefficient matrix, and
* Ycurrent is the current hardware brightness in nits.
*/
public final int reduceBrightColorsStrength;
/** Applied offset for the bright color reduction color transform (only applicable if active).
* The offset is computed by summing the coefficients a, b, and c, from the coefficient matrix
* and multiplying by the current brightness.
*/
public final float reduceBrightColorsOffset;
/** Brightness level before slider adjustment */
public final float lastBrightness;
@@ -105,8 +122,9 @@ public final class BrightnessChangeEvent implements Parcelable {
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
float powerBrightnessFactor, boolean nightMode, int colorTemperature,
float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness,
long[] colorValueBuckets, long colorSampleDuration) {
boolean reduceBrightColors, int reduceBrightColorsStrength,
float reduceBrightColorsOffset, float lastBrightness, boolean isDefaultBrightnessConfig,
boolean isUserSetBrightness, long[] colorValueBuckets, long colorSampleDuration) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -117,6 +135,9 @@ public final class BrightnessChangeEvent implements Parcelable {
this.powerBrightnessFactor = powerBrightnessFactor;
this.nightMode = nightMode;
this.colorTemperature = colorTemperature;
this.reduceBrightColors = reduceBrightColors;
this.reduceBrightColorsStrength = reduceBrightColorsStrength;
this.reduceBrightColorsOffset = reduceBrightColorsOffset;
this.lastBrightness = lastBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.isUserSetBrightness = isUserSetBrightness;
@@ -136,6 +157,9 @@ public final class BrightnessChangeEvent implements Parcelable {
this.powerBrightnessFactor = other.powerBrightnessFactor;
this.nightMode = other.nightMode;
this.colorTemperature = other.colorTemperature;
this.reduceBrightColors = other.reduceBrightColors;
this.reduceBrightColorsStrength = other.reduceBrightColorsStrength;
this.reduceBrightColorsOffset = other.reduceBrightColorsOffset;
this.lastBrightness = other.lastBrightness;
this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
this.isUserSetBrightness = other.isUserSetBrightness;
@@ -154,6 +178,9 @@ public final class BrightnessChangeEvent implements Parcelable {
powerBrightnessFactor = source.readFloat();
nightMode = source.readBoolean();
colorTemperature = source.readInt();
reduceBrightColors = source.readBoolean();
reduceBrightColorsStrength = source.readInt();
reduceBrightColorsOffset = source.readFloat();
lastBrightness = source.readFloat();
isDefaultBrightnessConfig = source.readBoolean();
isUserSetBrightness = source.readBoolean();
@@ -188,6 +215,9 @@ public final class BrightnessChangeEvent implements Parcelable {
dest.writeFloat(powerBrightnessFactor);
dest.writeBoolean(nightMode);
dest.writeInt(colorTemperature);
dest.writeBoolean(reduceBrightColors);
dest.writeInt(reduceBrightColorsStrength);
dest.writeFloat(reduceBrightColorsOffset);
dest.writeFloat(lastBrightness);
dest.writeBoolean(isDefaultBrightnessConfig);
dest.writeBoolean(isUserSetBrightness);
@@ -207,6 +237,9 @@ public final class BrightnessChangeEvent implements Parcelable {
private float mPowerBrightnessFactor;
private boolean mNightMode;
private int mColorTemperature;
private boolean mReduceBrightColors;
private int mReduceBrightColorsStrength;
private float mReduceBrightColorsOffset;
private float mLastBrightness;
private boolean mIsDefaultBrightnessConfig;
private boolean mIsUserSetBrightness;
@@ -273,6 +306,24 @@ public final class BrightnessChangeEvent implements Parcelable {
return this;
}
/** {@see BrightnessChangeEvent#reduceBrightColors} */
public Builder setReduceBrightColors(boolean reduceBrightColors) {
mReduceBrightColors = reduceBrightColors;
return this;
}
/** {@see BrightnessChangeEvent#reduceBrightColorsStrength} */
public Builder setReduceBrightColorsStrength(int strength) {
mReduceBrightColorsStrength = strength;
return this;
}
/** {@see BrightnessChangeEvent#reduceBrightColorsOffset} */
public Builder setReduceBrightColorsOffset(float offset) {
mReduceBrightColorsOffset = offset;
return this;
}
/** {@see BrightnessChangeEvent#lastBrightness} */
public Builder setLastBrightness(float lastBrightness) {
mLastBrightness = lastBrightness;
@@ -304,7 +355,8 @@ public final class BrightnessChangeEvent implements Parcelable {
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
mPowerBrightnessFactor, mNightMode, mColorTemperature, mReduceBrightColors,
mReduceBrightColorsStrength, mReduceBrightColorsOffset, mLastBrightness,
mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
mColorSampleDuration);
}

View File

@@ -437,6 +437,56 @@ public final class ColorDisplayManager {
return mManager.isDisplayWhiteBalanceEnabled();
}
/**
* Enables or disables reduce bright colors.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
public boolean setReduceBrightColorsActivated(boolean activated) {
return mManager.setReduceBrightColorsActivated(activated);
}
/**
* Returns whether reduce bright colors is currently enabled.
*
* @hide
*/
public boolean isReduceBrightColorsActivated() {
return mManager.isReduceBrightColorsActivated();
}
/**
* Set the strength level of bright color reduction to apply to the display.
*
* @param strength 0-100 (inclusive), where 100 is full strength
* @return whether the change was applied successfully
* @hide
*/
@RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
public boolean setReduceBrightColorsStrength(@IntRange(from = 0, to = 100) int strength) {
return mManager.setReduceBrightColorsStrength(strength);
}
/**
* Gets the strength of the bright color reduction transform.
*
* @hide
*/
public int getReduceBrightColorsStrength() {
return mManager.getReduceBrightColorsStrength();
}
/**
* Gets the brightness impact of the bright color reduction transform, as in the factor by which
* the current brightness (in nits) should be multiplied to obtain the brightness offset 'b'.
*
* @hide
*/
public float getReduceBrightColorsOffsetFactor() {
return mManager.getReduceBrightColorsOffsetFactor();
}
/**
* Returns {@code true} if Night Display is supported by the device.
*
@@ -477,6 +527,15 @@ public final class ColorDisplayManager {
return context.getResources().getBoolean(R.bool.config_displayWhiteBalanceAvailable);
}
/**
* Returns {@code true} if reduce bright colors is supported by the device.
*
* @hide
*/
public static boolean isReduceBrightColorsAvailable(Context context) {
return context.getResources().getBoolean(R.bool.config_reduceBrightColorsAvailable);
}
/**
* Check if the color transforms are color accelerated. Some transforms are experimental only
* on non-accelerated platforms due to the performance implications.
@@ -678,6 +737,46 @@ public final class ColorDisplayManager {
}
}
boolean isReduceBrightColorsActivated() {
try {
return mCdm.isReduceBrightColorsActivated();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
boolean setReduceBrightColorsActivated(boolean activated) {
try {
return mCdm.setReduceBrightColorsActivated(activated);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
int getReduceBrightColorsStrength() {
try {
return mCdm.getReduceBrightColorsStrength();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
boolean setReduceBrightColorsStrength(int strength) {
try {
return mCdm.setReduceBrightColorsStrength(strength);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
float getReduceBrightColorsOffsetFactor() {
try {
return mCdm.getReduceBrightColorsOffsetFactor();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
int getColorMode() {
try {
return mCdm.getColorMode();

View File

@@ -45,4 +45,10 @@ interface IColorDisplayManager {
boolean isDisplayWhiteBalanceEnabled();
boolean setDisplayWhiteBalanceEnabled(boolean enabled);
boolean isReduceBrightColorsActivated();
boolean setReduceBrightColorsActivated(boolean activated);
int getReduceBrightColorsStrength();
boolean setReduceBrightColorsStrength(int strength);
float getReduceBrightColorsOffsetFactor();
}

View File

@@ -822,28 +822,21 @@
<!-- B y-intercept --> <item>-0.198650895</item>
</string-array>
<string-array name="config_reduceBrightColorsCoefficientsNative">
<!-- R a-coefficient --> <item>-0.691218457</item>
<!-- R b-coefficient --> <item>0.050135153</item>
<!-- R y-intercept --> <item>0.917684143</item>
<!-- G a-coefficient --> <item>-0.691218457</item>
<!-- G b-coefficient --> <item>0.050135153</item>
<!-- G y-intercept --> <item>0.917684143</item>
<!-- B a-coefficient --> <item>-0.691218457</item>
<!-- B b-coefficient --> <item>0.050135153</item>
<!-- B y-intercept --> <item>0.917684143</item>
<!-- Control whether bright color reduction is available. This should only be enabled on devices
that have a HWC implementation that can apply the matrix passed to setColorTransform
without impacting power, performance, and app compatibility (e.g. protected content). -->
<bool name="config_reduceBrightColorsAvailable">@bool/config_setColorTransformAccelerated</bool>
<string-array name="config_reduceBrightColorsCoefficientsNonlinear">
<!-- a-coefficient --> <item>-0.4429953456</item>
<!-- b-coefficient --> <item>-0.2434077725</item>
<!-- y-intercept --> <item>0.9809063061</item>
</string-array>
<string-array name="config_reduceBrightColorsCoefficients">
<!-- R a-coefficient --> <item>0.00000000000000154</item>
<!-- R b-coefficient --> <item>-1.0</item>
<!-- R y-intercept --> <item>1.045977011</item>
<!-- G a-coefficient --> <item>0.00000000000000224</item>
<!-- G b-coefficient --> <item>-1.0</item>
<!-- G y-intercept --> <item>1.045977011</item>
<!-- B a-coefficient --> <item>0.0000000000000022</item>
<!-- B b-coefficient --> <item>-1.0</item>
<!-- B y-intercept --> <item>1.045977011</item>
<!-- a-coefficient --> <item>-0.000000000000001</item>
<!-- b-coefficient --> <item>-0.955555555555554</item>
<!-- y-intercept --> <item>1.000000000000000</item>
</string-array>
<!-- Boolean indicating whether display white balance is supported. -->

View File

@@ -3191,8 +3191,9 @@
<java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
<java-symbol type="bool" name="config_reduceBrightColorsAvailable" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficientsNative" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficientsNonlinear" />
<java-symbol type="array" name="config_availableColorModes" />
<java-symbol type="array" name="config_mappedColorModes" />
<java-symbol type="string" name="config_vendorColorModesRestoreHint" />

View File

@@ -300,6 +300,8 @@ public abstract class BrightnessMappingStrategy {
/** @return The default brightness configuration. */
public abstract BrightnessConfiguration getDefaultConfig();
/** Recalculates the backlight-to-nits and nits-to-backlight splines. */
public abstract void recalculateSplines(boolean applyAdjustment, float[] adjustment);
/**
* Returns the timeout for the short term model
@@ -657,6 +659,11 @@ public abstract class BrightnessMappingStrategy {
return null;
}
@Override
public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
// Do nothing.
}
@Override
public void dump(PrintWriter pw) {
pw.println("SimpleMappingStrategy");
@@ -696,7 +703,7 @@ public abstract class BrightnessMappingStrategy {
// A spline mapping from nits to the corresponding backlight value, normalized to the range
// [0, 1.0].
private final Spline mNitsToBacklightSpline;
private Spline mNitsToBacklightSpline;
// The default brightness configuration.
private final BrightnessConfiguration mDefaultConfig;
@@ -705,6 +712,11 @@ public abstract class BrightnessMappingStrategy {
// a brightness in nits.
private Spline mBacklightToNitsSpline;
private float[] mNits;
private int[] mBacklight;
private boolean mBrightnessRangeAdjustmentApplied;
private float mMaxGamma;
private float mAutoBrightnessAdjustment;
private float mUserLux;
@@ -726,15 +738,9 @@ public abstract class BrightnessMappingStrategy {
mUserLux = -1;
mUserBrightness = -1;
// Setup the backlight spline
final int N = nits.length;
float[] normalizedBacklight = new float[N];
for (int i = 0; i < N; i++) {
normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
}
mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
mNits = nits;
mBacklight = backlight;
computeNitsBrightnessSplines(mNits);
mDefaultConfig = config;
if (mLoggingEnabled) {
@@ -867,6 +873,12 @@ public abstract class BrightnessMappingStrategy {
return mDefaultConfig;
}
@Override
public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) {
mBrightnessRangeAdjustmentApplied = applyAdjustment;
computeNitsBrightnessSplines(mBrightnessRangeAdjustmentApplied ? adjustedNits : mNits);
}
@Override
public void dump(PrintWriter pw) {
pw.println("PhysicalMappingStrategy");
@@ -878,6 +890,18 @@ public abstract class BrightnessMappingStrategy {
pw.println(" mUserLux=" + mUserLux);
pw.println(" mUserBrightness=" + mUserBrightness);
pw.println(" mDefaultConfig=" + mDefaultConfig);
pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
}
private void computeNitsBrightnessSplines(float[] nits) {
final int len = nits.length;
float[] normalizedBacklight = new float[len];
for (int i = 0; i < len; i++) {
normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]);
}
mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight);
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
}
private void computeSpline() {

View File

@@ -63,7 +63,6 @@ import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.RingBuffer;
import com.android.server.LocalServices;
@@ -71,7 +70,6 @@ import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -80,7 +78,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -118,6 +115,9 @@ public class BrightnessTracker {
private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
private static final String ATTR_NIGHT_MODE = "nightMode";
private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
private static final String ATTR_REDUCE_BRIGHT_COLORS = "reduceBrightColors";
private static final String ATTR_REDUCE_BRIGHT_COLORS_STRENGTH = "reduceBrightColorsStrength";
private static final String ATTR_REDUCE_BRIGHT_COLORS_OFFSET = "reduceBrightColorsOffset";
private static final String ATTR_LAST_NITS = "lastNits";
private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
private static final String ATTR_POWER_SAVE = "powerSaveFactor";
@@ -398,6 +398,10 @@ public class BrightnessTracker {
builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
builder.setReduceBrightColors(mInjector.isReduceBrightColorsActivated(mContext));
builder.setReduceBrightColorsStrength(mInjector.getReduceBrightColorsStrength(mContext));
builder.setReduceBrightColorsOffset(mInjector.getReduceBrightColorsOffsetFactor(mContext)
* brightness);
if (mColorSamplingEnabled) {
DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
@@ -563,6 +567,12 @@ public class BrightnessTracker {
out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
toWrite[i].colorTemperature);
out.attributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS,
toWrite[i].reduceBrightColors);
out.attributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH,
toWrite[i].reduceBrightColorsStrength);
out.attributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET,
toWrite[i].reduceBrightColorsOffset);
out.attributeFloat(null, ATTR_LAST_NITS,
toWrite[i].lastBrightness);
out.attributeBoolean(null, ATTR_DEFAULT_CONFIG,
@@ -641,6 +651,12 @@ public class BrightnessTracker {
builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
builder.setColorTemperature(
parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE));
builder.setReduceBrightColors(
parser.getAttributeBoolean(null, ATTR_REDUCE_BRIGHT_COLORS));
builder.setReduceBrightColorsStrength(
parser.getAttributeInt(null, ATTR_REDUCE_BRIGHT_COLORS_STRENGTH));
builder.setReduceBrightColorsOffset(
parser.getAttributeFloat(null, ATTR_REDUCE_BRIGHT_COLORS_OFFSET));
builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS));
String luxValue = parser.getAttributeValue(null, ATTR_LUX);
@@ -1114,6 +1130,21 @@ public class BrightnessTracker {
return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
}
public int getReduceBrightColorsStrength(Context context) {
return context.getSystemService(ColorDisplayManager.class)
.getReduceBrightColorsStrength();
}
public float getReduceBrightColorsOffsetFactor(Context context) {
return context.getSystemService(ColorDisplayManager.class)
.getReduceBrightColorsOffsetFactor();
}
public boolean isReduceBrightColorsActivated(Context context) {
return context.getSystemService(ColorDisplayManager.class)
.isReduceBrightColorsActivated();
}
public DisplayedContentSample sampleColor(int noFramesToSample) {
final DisplayManagerInternal displayManagerInternal =
LocalServices.getService(DisplayManagerInternal.class);

View File

@@ -58,6 +58,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
@@ -346,6 +348,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Nullable
private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
private final ColorDisplayServiceInternal mCdsi;
private final float[] mNitsRange;
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -580,6 +585,37 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
mDisplayWhiteBalanceController = displayWhiteBalanceController;
if (displayDeviceConfig != null && displayDeviceConfig.getNits() != null) {
mNitsRange = displayDeviceConfig.getNits();
} else {
Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources()
.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
}
mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
@Override
public void onReduceBrightColorsActivationChanged(boolean activated) {
applyReduceBrightColorsSplineAdjustment();
}
@Override
public void onReduceBrightColorsStrengthChanged(int strength) {
applyReduceBrightColorsSplineAdjustment();
}
});
if (active) {
applyReduceBrightColorsSplineAdjustment();
}
}
private void applyReduceBrightColorsSplineAdjustment() {
float[] adjustedNits = new float[mNitsRange.length];
for (int i = 0; i < mNitsRange.length; i++) {
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
}
private Sensor findDisplayLightSensor(String sensorType) {

View File

@@ -173,6 +173,7 @@ public final class ColorDisplayService extends SystemService {
private ContentObserver mContentObserver;
private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
private ReduceBrightColorsListener mReduceBrightColorsListener;
private NightDisplayAutoMode mNightDisplayAutoMode;
@@ -617,18 +618,24 @@ public final class ColorDisplayService extends SystemService {
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
mReduceBrightColorsTintController.setActivated(
Secure.getIntForUser(getContext().getContentResolver(),
Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1);
final boolean activated = Secure.getIntForUser(getContext().getContentResolver(),
Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1;
mReduceBrightColorsTintController.setActivated(activated);
if (mReduceBrightColorsListener != null) {
mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated);
}
}
private void onReduceBrightColorsStrengthLevelChanged() {
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
mReduceBrightColorsTintController.setMatrix(
Secure.getIntForUser(getContext().getContentResolver(),
Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser));
final int strength = Secure.getIntForUser(getContext().getContentResolver(),
Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mCurrentUser);
mReduceBrightColorsTintController.setMatrix(strength);
if (mReduceBrightColorsListener != null) {
mReduceBrightColorsListener.onReduceBrightColorsStrengthChanged(strength);
}
}
/**
@@ -762,6 +769,22 @@ public final class ColorDisplayService extends SystemService {
mCurrentUser) == 1;
}
private boolean setReduceBrightColorsActivatedInternal(boolean activated) {
if (mCurrentUser == UserHandle.USER_NULL) {
return false;
}
return Secure.putIntForUser(getContext().getContentResolver(),
Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, activated ? 1 : 0, mCurrentUser);
}
private boolean setReduceBrightColorsStrengthInternal(int strength) {
if (mCurrentUser == UserHandle.USER_NULL) {
return false;
}
return Secure.putIntForUser(getContext().getContentResolver(),
Secure.REDUCE_BRIGHT_COLORS_LEVEL, strength, mCurrentUser);
}
private boolean isDeviceColorManagedInternal() {
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
return dtm.isDeviceColorManaged();
@@ -1468,6 +1491,31 @@ public final class ColorDisplayService extends SystemService {
return isDisplayWhiteBalanceSettingEnabled();
}
/**
* Sets the listener and returns whether reduce bright colors is currently enabled.
*/
public boolean setReduceBrightColorsListener(ReduceBrightColorsListener listener) {
mReduceBrightColorsListener = listener;
return mReduceBrightColorsTintController.isActivated();
}
/**
* Returns whether reduce bright colors is currently active.
*/
public boolean isReduceBrightColorsActivated() {
return mReduceBrightColorsTintController.isActivated();
}
/**
* Gets the computed brightness, in nits, when the reduce bright colors feature is applied
* at the current strength.
*
* @hide
*/
public float getReduceBrightColorsAdjustedBrightnessNits(float nits) {
return mReduceBrightColorsTintController.getAdjustedBrightness(nits);
}
/**
* Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
* invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
@@ -1491,6 +1539,22 @@ public final class ColorDisplayService extends SystemService {
void onDisplayWhiteBalanceStatusChanged(boolean activated);
}
/**
* Listener for changes in reduce bright colors status.
*/
public interface ReduceBrightColorsListener {
/**
* Notify that the reduce bright colors activation status has changed.
*/
void onReduceBrightColorsActivationChanged(boolean activated);
/**
* Notify that the reduce bright colors strength has changed.
*/
void onReduceBrightColorsStrengthChanged(int strength);
}
private final class TintHandler extends Handler {
private TintHandler(Looper looper) {
@@ -1786,6 +1850,62 @@ public final class ColorDisplayService extends SystemService {
}
}
@Override
public boolean isReduceBrightColorsActivated() {
final long token = Binder.clearCallingIdentity();
try {
return mReduceBrightColorsTintController.isActivated();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean setReduceBrightColorsActivated(boolean activated) {
getContext().enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
"Permission required to set reduce bright colors activation state");
final long token = Binder.clearCallingIdentity();
try {
return setReduceBrightColorsActivatedInternal(activated);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public int getReduceBrightColorsStrength() {
final long token = Binder.clearCallingIdentity();
try {
return mReduceBrightColorsTintController.getStrength();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public float getReduceBrightColorsOffsetFactor() {
final long token = Binder.clearCallingIdentity();
try {
return mReduceBrightColorsTintController.getOffsetFactor();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean setReduceBrightColorsStrength(int strength) {
getContext().enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
"Permission required to set reduce bright colors strength");
final long token = Binder.clearCallingIdentity();
try {
return setReduceBrightColorsStrengthInternal(strength);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {

View File

@@ -34,7 +34,7 @@ import java.util.Arrays;
public class ReduceBrightColorsTintController extends TintController {
private final float[] mMatrix = new float[16];
private final float[] mCoefficients = new float[9];
private final float[] mCoefficients = new float[3];
private int mStrength;
@@ -42,8 +42,8 @@ public class ReduceBrightColorsTintController extends TintController {
public void setUp(Context context, boolean needsLinear) {
final String[] coefficients = context.getResources().getStringArray(
needsLinear ? R.array.config_reduceBrightColorsCoefficients
: R.array.config_reduceBrightColorsCoefficientsNative);
for (int i = 0; i < 9 && i < coefficients.length; i++) {
: R.array.config_reduceBrightColorsCoefficientsNonlinear);
for (int i = 0; i < 3 && i < coefficients.length; i++) {
mCoefficients[i] = Float.parseFloat(coefficients[i]);
}
}
@@ -67,20 +67,11 @@ public class ReduceBrightColorsTintController extends TintController {
Matrix.setIdentityM(mMatrix, 0);
final float percentageStrength = strengthLevel / 100f;
final float squaredPercentageStrength = percentageStrength * percentageStrength;
final float red =
squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
+ mCoefficients[2];
final float green =
squaredPercentageStrength * mCoefficients[3] + percentageStrength * mCoefficients[4]
+ mCoefficients[5];
final float blue =
squaredPercentageStrength * mCoefficients[6] + percentageStrength * mCoefficients[7]
+ mCoefficients[8];
mMatrix[0] = clamp(red);
mMatrix[5] = clamp(green);
mMatrix[10] = clamp(blue);
// All three (r,g,b) components are equal and calculated with the same formula.
final float componentValue = computeComponentValue(strengthLevel);
mMatrix[0] = componentValue;
mMatrix[5] = componentValue;
mMatrix[10] = componentValue;
}
private float clamp(float value) {
@@ -110,4 +101,26 @@ public class ReduceBrightColorsTintController extends TintController {
public int getStrength() {
return mStrength;
}
/** Returns the offset factor at Ymax. */
public float getOffsetFactor() {
// Strength terms drop out as strength --> 1, leaving the coefficients.
return mCoefficients[0] + mCoefficients[1] + mCoefficients[2];
}
/**
* Returns the effective brightness (in nits), which has been adjusted to account for the effect
* of the bright color reduction.
*/
public float getAdjustedBrightness(float nits) {
return computeComponentValue(mStrength) * nits;
}
private float computeComponentValue(int strengthLevel) {
final float percentageStrength = strengthLevel / 100f;
final float squaredPercentageStrength = percentageStrength * percentageStrength;
return clamp(
squaredPercentageStrength * mCoefficients[0] + percentageStrength * mCoefficients[1]
+ mCoefficients[2]);
}
}

View File

@@ -226,6 +226,31 @@ public class BrightnessMappingStrategyTest {
strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01f /*tolerance*/);
}
@Test
public void testPhysicalStrategyRecalculateSplines() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
BACKLIGHT_RANGE);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
}
// Default is unadjusted
assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
// When adjustment is turned on, adjustment array is used
strategy.recalculateSplines(true, adjustedNits50p);
assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
// When adjustment is turned off, adjustment array is ignored
strategy.recalculateSplines(false, adjustedNits50p);
assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */);
assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */);
}
@Test
public void testDefaultStrategyIsPhysical() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,

View File

@@ -335,6 +335,9 @@ public class BrightnessTrackerTest {
assertEquals(0.5, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3333, event.colorTemperature);
assertTrue(event.reduceBrightColors);
assertEquals(40, event.reduceBrightColorsStrength);
assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals("a.package", event.packageName);
assertEquals(0, event.userId);
assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
@@ -561,6 +564,9 @@ public class BrightnessTrackerTest {
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
@@ -592,6 +598,9 @@ public class BrightnessTrackerTest {
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertEquals(3339, event.colorTemperature);
assertTrue(event.reduceBrightColors);
assertEquals(40, event.reduceBrightColorsStrength);
assertEquals(20f, event.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
assertTrue(event.isUserSetBrightness);
assertFalse(event.isDefaultBrightnessConfig);
@@ -606,6 +615,9 @@ public class BrightnessTrackerTest {
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
@@ -639,6 +651,7 @@ public class BrightnessTrackerTest {
assertEquals(brightness, event.brightness, FLOAT_DELTA);
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
assertTrue(event.reduceBrightColors);
assertEquals(3339, event.colorTemperature);
}
@@ -661,6 +674,9 @@ public class BrightnessTrackerTest {
builder.setBatteryLevel(0.7f);
builder.setNightMode(false);
builder.setColorTemperature(345);
builder.setReduceBrightColors(false);
builder.setReduceBrightColorsStrength(40);
builder.setReduceBrightColorsOffset(20f);
builder.setLastBrightness(50f);
builder.setColorValues(new long[] {23, 34, 45}, 1000L);
BrightnessChangeEvent event = builder.build();
@@ -684,6 +700,9 @@ public class BrightnessTrackerTest {
assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA);
assertEquals(event.nightMode, event2.nightMode);
assertEquals(event.colorTemperature, event2.colorTemperature);
assertEquals(event.reduceBrightColors, event2.reduceBrightColors);
assertEquals(event.reduceBrightColorsStrength, event2.reduceBrightColorsStrength);
assertEquals(event.reduceBrightColorsOffset, event2.reduceBrightColorsOffset, FLOAT_DELTA);
assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets);
assertEquals(event.colorSampleDuration, event2.colorSampleDuration);
@@ -1019,6 +1038,18 @@ public class BrightnessTrackerTest {
0) == 1;
}
@Override
public int getReduceBrightColorsStrength(Context context) {
return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL,
0);
}
@Override
public boolean isReduceBrightColorsActivated(Context context) {
return mSecureIntSettings.getOrDefault(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
0) == 1;
}
@Override
public DisplayedContentSample sampleColor(int noFramesToSample) {
return new DisplayedContentSample(600L,

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2021 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.color;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ReduceBrightColorsTintControllerTest {
private Context mContext;
@Before
public void setUp() {
final Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(
com.android.internal.R.array.config_reduceBrightColorsCoefficients))
.thenReturn(new String[]{"-0.000000000000001", "-0.955555555555554",
"1.000000000000000"});
when(mockResources.getStringArray(
com.android.internal.R.array.config_reduceBrightColorsCoefficientsNonlinear))
.thenReturn(new String[]{"-0.4429953456", "-0.2434077725", "0.9809063061"});
mContext = mock(Context.class);
when(mContext.getResources()).thenReturn(mockResources);
}
@Test
public void setAndGetMatrix() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(50);
tintController.setActivated(true);
assertThat(tintController.getStrength()).isEqualTo(50);
assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
.containsExactly(
0.5222222f, 0f, 0f, 0f,
0f, 0.5222222f, 0f, 0f,
0f, 0f, 0.5222222f, 0f,
0f, 0f, 0f, 1f)
.inOrder();
}
@Test
public void setAndGetMatrixClampToZero() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(-50);
tintController.setActivated(true);
assertThat(tintController.getStrength()).isEqualTo(0);
assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
.containsExactly(
1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f)
.inOrder();
}
@Test
public void setAndGetMatrixClampTo100() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(120);
tintController.setActivated(true);
assertThat(tintController.getStrength()).isEqualTo(100);
assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
.containsExactly(
0.04444444f, 0f, 0f, 0f,
0f, 0.04444444f, 0f, 0f,
0f, 0f, 0.04444444f, 0f,
0f, 0f, 0f, 1f)
.inOrder();
}
@Test
public void returnsIdentityMatrixWhenNotActivated() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(50);
tintController.setActivated(true);
tintController.setActivated(false);
assertThat(tintController.getStrength()).isEqualTo(50);
assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
.containsExactly(
1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f)
.inOrder();
}
@Test
public void getAdjustedBrightnessZeroRbcStrengthFullBrightness() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(0);
assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(450f);
}
@Test
public void getAdjustedBrightnessFullRbcStrengthFullBrightness() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(100);
assertThat(tintController.getAdjustedBrightness(450f)).isEqualTo(19.999998f);
}
@Test
public void getAdjustedBrightnessZeroRbcStrengthLowBrightness() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(0);
assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(2.2f);
}
@Test
public void getAdjustedBrightnessFullRbcStrengthLowBrightness() {
final ReduceBrightColorsTintController tintController =
new ReduceBrightColorsTintController();
tintController.setUp(mContext, /* needsLinear= */ true);
tintController.setMatrix(100);
assertThat(tintController.getAdjustedBrightness(2.2f)).isEqualTo(0.09777778f);
}
}