LowMinBrightness Mappings
- Create lowBrightness data object for ddc to use
- Read in low min brightness splines from ddc
- Call dimming method in LDA
Bug: 179428400
Test: atest DisplayServiceTests
Change-Id: If33d0164c2ca7179cb80c31f4c762e53f70b174c
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 3d95fee..57b066d 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -53,6 +53,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -249,6 +250,7 @@
// Controls Brightness range (including High Brightness Mode).
private final BrightnessRangeController mBrightnessRangeController;
+ private final BrightnessClamperController mBrightnessClamperController;
// Throttles (caps) maximum allowed brightness
private final BrightnessThrottler mBrightnessThrottler;
@@ -284,7 +286,8 @@
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessModeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits) {
+ int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessClamperController brightnessClamperController) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor,
brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
dozeScaleFactor, lightSensorRate, initialLightSensorRate,
@@ -294,7 +297,7 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits
+ userNits, brightnessClamperController
);
}
@@ -310,9 +313,10 @@
HysteresisLevels screenBrightnessThresholds,
HysteresisLevels ambientBrightnessThresholdsIdle,
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
- BrightnessRangeController brightnessModeController,
+ BrightnessRangeController brightnessRangeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits) {
+ int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessClamperController brightnessClamperController) {
mInjector = injector;
mClock = injector.createClock();
mContext = context;
@@ -355,7 +359,8 @@
mPendingForegroundAppPackageName = null;
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
- mBrightnessRangeController = brightnessModeController;
+ mBrightnessRangeController = brightnessRangeController;
+ mBrightnessClamperController = brightnessClamperController;
mBrightnessThrottler = brightnessThrottler;
mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
@@ -787,7 +792,7 @@
mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
}
mBrightnessRangeController.onAmbientLuxChange(mAmbientLux);
-
+ mBrightnessClamperController.onAmbientLuxChange(mAmbientLux);
// If the short term model was invalidated and the change is drastic enough, reset it.
mShortTermModel.maybeReset(mAmbientLux);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 4116669..04e7f77 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -61,6 +61,7 @@
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds;
import com.android.server.display.config.IntegerArray;
+import com.android.server.display.config.LowBrightnessData;
import com.android.server.display.config.LuxThrottling;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.NonNegativeFloatToFloatPoint;
@@ -555,6 +556,24 @@
* <majorVersion>2</majorVersion>
* <minorVersion>0</minorVersion>
* </usiVersion>
+ * <lowBrightness enabled="true">
+ * <transitionPoint>0.1</transitionPoint>
+ *
+ * <nits>0.2</nits>
+ * <nits>2.0</nits>
+ * <nits>500.0</nits>
+ * <nits>1000.0</nits>
+ *
+ * <backlight>0</backlight>
+ * <backlight>0.0001</backlight>
+ * <backlight>0.5</backlight>
+ * <backlight>1.0</backlight>
+ *
+ * <brightness>0</brightness>
+ * <brightness>0.1</brightness>
+ * <brightness>0.5</brightness>
+ * <brightness>1.0</brightness>
+ * </lowBrightness>
* <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
* <idleScreenRefreshRateTimeout>
* <luxThresholds>
@@ -568,6 +587,8 @@
* </point>
* </luxThresholds>
* </idleScreenRefreshRateTimeout>
+ *
+ *
* </displayConfiguration>
* }
* </pre>
@@ -732,6 +753,7 @@
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
private Spline mNitsToBacklightSpline;
+
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
@@ -872,6 +894,9 @@
@Nullable
private HdrBrightnessData mHdrBrightnessData;
+ @Nullable
+ public LowBrightnessData mLowBrightnessData;
+
/**
* Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
*/
@@ -1814,6 +1839,15 @@
}
/**
+ *
+ * @return true if low brightness mode is enabled
+ */
+ @VisibleForTesting
+ public boolean getLbmEnabled() {
+ return mLowBrightnessData != null;
+ }
+
+ /**
* @return Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
*/
public float getBrightnessCapForWearBedtimeMode() {
@@ -1952,6 +1986,8 @@
+ "mUsiVersion= " + mHostUsiVersion + "\n"
+ "mHdrBrightnessData= " + mHdrBrightnessData + "\n"
+ "mBrightnessCapForWearBedtimeMode= " + mBrightnessCapForWearBedtimeMode
+ + "\n"
+ + (mLowBrightnessData != null ? mLowBrightnessData.toString() : "")
+ "}";
}
@@ -2002,6 +2038,9 @@
loadDensityMapping(config);
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
+ if (mFlags.isEvenDimmerEnabled()) {
+ mLowBrightnessData = LowBrightnessData.loadConfig(config);
+ }
loadBrightnessMap(config);
loadThermalThrottlingConfig(config);
loadPowerThrottlingConfigData(config);
@@ -2793,6 +2832,18 @@
// These splines are used to convert from the system brightness value to the HAL backlight
// value
private void createBacklightConversionSplines() {
+ if (mLowBrightnessData != null) {
+ mBrightnessToBacklightSpline = mLowBrightnessData.mBrightnessToBacklight;
+ mBacklightToBrightnessSpline = mLowBrightnessData.mBacklightToBrightness;
+ mBacklightToNitsSpline = mLowBrightnessData.mBacklightToNits;
+ mNitsToBacklightSpline = mLowBrightnessData.mNitsToBacklight;
+
+ mNits = mLowBrightnessData.mNits;
+ mBrightness = mLowBrightnessData.mBrightness;
+ mBacklight = mLowBrightnessData.mBacklight;
+ return;
+ }
+
mBrightness = new float[mBacklight.length];
for (int i = 0; i < mBrightness.length; i++) {
mBrightness[i] = MathUtils.map(mBacklight[0],
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 77a43d0..67f2814 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1165,7 +1165,8 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
- mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits);
+ mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits,
+ mBrightnessClamperController);
mDisplayBrightnessController.setAutomaticBrightnessController(
mAutomaticBrightnessController);
@@ -2479,6 +2480,7 @@
public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
boolean slowChange) {
mBrightnessRangeController.onAmbientLuxChange(ambientLux);
+ mBrightnessClamperController.onAmbientLuxChange(ambientLux);
if (nits == BrightnessMappingStrategy.INVALID_NITS) {
mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
} else {
@@ -3176,7 +3178,9 @@
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessModeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits) {
+ int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessClamperController brightnessClamperController) {
+
return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor,
brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin,
brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
@@ -3186,7 +3190,7 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits);
+ userNits, brightnessClamperController);
}
BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b2fd9ed..3b3a03b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -37,6 +37,7 @@
import android.os.Trace;
import android.util.DisplayUtils;
import android.util.LongSparseArray;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -78,6 +79,13 @@
private static final String UNIQUE_ID_PREFIX = "local:";
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.boot.emulator.circular";
+ // Min and max strengths for even dimmer feature.
+ private static final float EVEN_DIMMER_MIN_STRENGTH = 0.0f;
+ private static final float EVEN_DIMMER_MAX_STRENGTH = 70.0f; // not too dim yet.
+ private static final float BRIGHTNESS_MIN = 0.0f;
+ // The brightness at which we start using color matrices rather than backlight,
+ // to dim the display
+ private static final float BACKLIGHT_COLOR_TRANSITION_POINT = 0.1f;
private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
@@ -91,6 +99,8 @@
private Context mOverlayContext;
+ private int mEvenDimmerStrength = -1;
+
// Called with SyncRoot lock held.
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
Handler handler, Listener listener, DisplayManagerFlags flags,
@@ -928,6 +938,10 @@
final float nits = backlightToNits(backlight);
final float sdrNits = backlightToNits(sdrBacklight);
+ if (getFeatureFlags().isEvenDimmerEnabled()) {
+ applyColorMatrixBasedDimming(brightnessState);
+ }
+
mBacklightAdapter.setBacklight(sdrBacklight, sdrNits, backlight, nits);
Trace.traceCounter(Trace.TRACE_TAG_POWER,
"ScreenBrightness",
@@ -974,6 +988,22 @@
}
}
}
+
+ private void applyColorMatrixBasedDimming(float brightnessState) {
+ int strength = (int) (MathUtils.constrainedMap(
+ EVEN_DIMMER_MAX_STRENGTH, EVEN_DIMMER_MIN_STRENGTH, // to this range
+ BRIGHTNESS_MIN, BACKLIGHT_COLOR_TRANSITION_POINT, // from this range
+ brightnessState) + 0.5); // map this (+ rounded up)
+
+ if (mEvenDimmerStrength < 0 // uninitialised
+ || MathUtils.abs(mEvenDimmerStrength - strength) > 1
+ || strength <= 1) {
+ mEvenDimmerStrength = strength;
+ }
+
+ // TODO: use `enabled` and `mRbcStrength` to set color matrices here
+ // TODO: boolean enabled = mEvenDimmerStrength > 0.0f;
+ }
};
}
return null;
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 18e8fab..d8a4500 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -189,6 +189,13 @@
mModifiers.forEach(BrightnessStateModifier::stop);
}
+ /**
+ * Notifies modifiers that ambient lux has changed.
+ * @param ambientLux current lux, debounced
+ */
+ public void onAmbientLuxChange(float ambientLux) {
+ mModifiers.forEach(modifier -> modifier.onAmbientLuxChange(ambientLux));
+ }
// Called in DisplayControllerHandler
private void recalculateBrightnessCap() {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
index 7f1f7a9..a91bb59 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
@@ -39,21 +39,21 @@
* Class used to prevent the screen brightness dipping below a certain value, based on current
* lux conditions and user preferred minimum.
*/
-public class BrightnessLowLuxModifier implements
- BrightnessStateModifier {
+public class BrightnessLowLuxModifier extends BrightnessModifier {
// To enable these logs, run:
// 'adb shell setprop persist.log.tag.BrightnessLowLuxModifier DEBUG && adb reboot'
private static final String TAG = "BrightnessLowLuxModifier";
private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+ private static final float MIN_NITS = 2.0f;
private final SettingsObserver mSettingsObserver;
private final ContentResolver mContentResolver;
private final Handler mHandler;
private final BrightnessClamperController.ClamperChangeListener mChangeListener;
- protected float mSettingNitsLowerBound = PowerManager.BRIGHTNESS_MIN;
private int mReason;
private float mBrightnessLowerBound;
private boolean mIsActive;
+ private float mAmbientLux;
@VisibleForTesting
BrightnessLowLuxModifier(Handler handler,
@@ -78,17 +78,17 @@
int userId = UserHandle.USER_CURRENT;
float settingNitsLowerBound = Settings.Secure.getFloatForUser(
mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
- /* def= */ PowerManager.BRIGHTNESS_MIN, userId);
+ /* def= */ MIN_NITS, userId);
- boolean isActive = Settings.Secure.getIntForUser(mContentResolver,
+ boolean isActive = Settings.Secure.getFloatForUser(mContentResolver,
Settings.Secure.EVEN_DIMMER_ACTIVATED,
- /* def= */ 0, userId) == 1;
+ /* def= */ 0, userId) == 1.0f;
- // TODO: luxBasedNitsLowerBound = mMinNitsToLuxSpline(currentLux);
- float luxBasedNitsLowerBound = 0.0f;
+ // TODO: luxBasedNitsLowerBound = mMinLuxToNitsSpline(currentLux);
+ float luxBasedNitsLowerBound = 2.0f;
- // TODO: final float nitsLowerBound = isActive ? Math.max(settingNitsLowerBound,
- // luxBasedNitsLowerBound) : PowerManager.BRIGHTNESS_MIN;
+ final float nitsLowerBound = isActive ? Math.max(settingNitsLowerBound,
+ luxBasedNitsLowerBound) : MIN_NITS;
final int reason = settingNitsLowerBound > luxBasedNitsLowerBound
? BrightnessReason.MODIFIER_MIN_USER_SET_LOWER_BOUND
@@ -104,8 +104,13 @@
mReason = reason;
if (DEBUG) {
Slog.i(TAG, "isActive: " + isActive
- + ", settingNitsLowerBound: " + settingNitsLowerBound
- + ", lowerBound: " + brightnessLowerBound);
+ + ", brightnessLowerBound: " + brightnessLowerBound
+ + ", mAmbientLux: " + mAmbientLux
+ + ", mReason: " + (
+ mReason == BrightnessReason.MODIFIER_MIN_USER_SET_LOWER_BOUND ? "minSetting"
+ : "lux")
+ + ", nitsLowerBound: " + nitsLowerBound
+ );
}
mBrightnessLowerBound = brightnessLowerBound;
mChangeListener.onChanged();
@@ -132,6 +137,22 @@
}
@Override
+ boolean shouldApply(DisplayManagerInternal.DisplayPowerRequest request) {
+ return mIsActive;
+ }
+
+ @Override
+ float getBrightnessAdjusted(float currentBrightness,
+ DisplayManagerInternal.DisplayPowerRequest request) {
+ return Math.max(mBrightnessLowerBound, currentBrightness);
+ }
+
+ @Override
+ int getModifier() {
+ return mReason;
+ }
+
+ @Override
public void apply(DisplayManagerInternal.DisplayPowerRequest request,
DisplayBrightnessState.Builder stateBuilder) {
stateBuilder.setMinBrightness(mBrightnessLowerBound);
@@ -150,10 +171,16 @@
}
@Override
+ public void onAmbientLuxChange(float ambientLux) {
+ mAmbientLux = ambientLux;
+ recalculateLowerBound();
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println("BrightnessLowLuxModifier:");
- pw.println(" mBrightnessLowerBound=" + mBrightnessLowerBound);
pw.println(" mIsActive=" + mIsActive);
+ pw.println(" mBrightnessLowerBound=" + mBrightnessLowerBound);
pw.println(" mReason=" + mReason);
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
index be8fa5a..2a3dd87 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
@@ -68,4 +68,9 @@
public void stop() {
// do nothing
}
+
+ @Override
+ public void onAmbientLuxChange(float ambientLux) {
+ // do nothing
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
index 441ba8f..2234258 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
@@ -42,4 +42,10 @@
* Called when stopped. Listeners can be unregistered here.
*/
void stop();
+
+ /**
+ * Allows modifiers to react to ambient lux changes.
+ * @param ambientLux current debounced lux.
+ */
+ void onAmbientLuxChange(float ambientLux);
}
diff --git a/services/core/java/com/android/server/display/config/LowBrightnessData.java b/services/core/java/com/android/server/display/config/LowBrightnessData.java
new file mode 100644
index 0000000..aa82533
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/LowBrightnessData.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 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.config;
+
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.Spline;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Brightness config for low brightness mode
+ */
+public class LowBrightnessData {
+ private static final String TAG = "LowBrightnessData";
+
+ /**
+ * Brightness value at which lower brightness methods are used.
+ */
+ public final float mTransitionPoint;
+
+ /**
+ * Nits array, maps to mBacklight
+ */
+ public final float[] mNits;
+
+ /**
+ * Backlight array, maps to mBrightness and mNits
+ */
+ public final float[] mBacklight;
+
+ /**
+ * Brightness array, maps to mBacklight
+ */
+ public final float[] mBrightness;
+ /**
+ * Spline, mapping between backlight and nits
+ */
+ public final Spline mBacklightToNits;
+ /**
+ * Spline, mapping between nits and backlight
+ */
+ public final Spline mNitsToBacklight;
+ /**
+ * Spline, mapping between brightness and backlight
+ */
+ public final Spline mBrightnessToBacklight;
+ /**
+ * Spline, mapping between backlight and brightness
+ */
+ public final Spline mBacklightToBrightness;
+
+ @VisibleForTesting
+ public LowBrightnessData(float transitionPoint, float[] nits,
+ float[] backlight, float[] brightness, Spline backlightToNits,
+ Spline nitsToBacklight, Spline brightnessToBacklight, Spline backlightToBrightness) {
+ mTransitionPoint = transitionPoint;
+ mNits = nits;
+ mBacklight = backlight;
+ mBrightness = brightness;
+ mBacklightToNits = backlightToNits;
+ mNitsToBacklight = nitsToBacklight;
+ mBrightnessToBacklight = brightnessToBacklight;
+ mBacklightToBrightness = backlightToBrightness;
+ }
+
+ @Override
+ public String toString() {
+ return "LowBrightnessData {"
+ + "mTransitionPoint: " + mTransitionPoint
+ + ", mNits: " + Arrays.toString(mNits)
+ + ", mBacklight: " + Arrays.toString(mBacklight)
+ + ", mBrightness: " + Arrays.toString(mBrightness)
+ + ", mBacklightToNits: " + mBacklightToNits
+ + ", mNitsToBacklight: " + mNitsToBacklight
+ + ", mBrightnessToBacklight: " + mBrightnessToBacklight
+ + ", mBacklightToBrightness: " + mBacklightToBrightness
+ + "} ";
+ }
+
+ /**
+ * Loads LowBrightnessData from DisplayConfiguration
+ */
+ @Nullable
+ public static LowBrightnessData loadConfig(DisplayConfiguration config) {
+ final LowBrightnessMode lbm = config.getLowBrightness();
+ if (lbm == null) {
+ return null;
+ }
+
+ boolean lbmIsEnabled = lbm.getEnabled();
+ if (!lbmIsEnabled) {
+ return null;
+ }
+
+ List<Float> nitsList = lbm.getNits();
+ List<Float> backlightList = lbm.getBacklight();
+ List<Float> brightnessList = lbm.getBrightness();
+ float transitionPoints = lbm.getTransitionPoint().floatValue();
+
+ if (nitsList.isEmpty()
+ || backlightList.size() != brightnessList.size()
+ || backlightList.size() != nitsList.size()) {
+ Slog.e(TAG, "Invalid low brightness array lengths");
+ return null;
+ }
+
+ float[] nits = new float[nitsList.size()];
+ float[] backlight = new float[nitsList.size()];
+ float[] brightness = new float[nitsList.size()];
+
+ for (int i = 0; i < nitsList.size(); i++) {
+ nits[i] = nitsList.get(i);
+ backlight[i] = backlightList.get(i);
+ brightness[i] = brightnessList.get(i);
+ }
+
+ return new LowBrightnessData(transitionPoints, nits, backlight, brightness,
+ Spline.createSpline(backlight, nits),
+ Spline.createSpline(nits, backlight),
+ Spline.createSpline(brightness, backlight),
+ Spline.createSpline(backlight, brightness)
+ );
+ }
+}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index d0df2b2..1f54518 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -162,6 +162,10 @@
<xs:element type="usiVersion" name="usiVersion">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="lowBrightnessMode" name="lowBrightness">
+ <xs:attribute name="enabled" type="xs:boolean" use="optional"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<!-- Maximum screen brightness setting when screen brightness capped in
Wear Bedtime mode. This must be a non-negative decimal within the range defined by
the first and the last brightness value in screenBrightnessMap. -->
@@ -172,6 +176,7 @@
<xs:element type="idleScreenRefreshRateTimeout" name="idleScreenRefreshRateTimeout" minOccurs="0">
<xs:annotation name="final"/>
</xs:element>
+
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -216,6 +221,21 @@
</xs:restriction>
</xs:simpleType>
+ <xs:complexType name="lowBrightnessMode">
+ <xs:sequence>
+ <xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1"
+ maxOccurs="1">
+ </xs:element>
+ <xs:element name="nits" type="xs:float" maxOccurs="unbounded">
+ </xs:element>
+ <xs:element name="backlight" type="xs:float" maxOccurs="unbounded">
+ </xs:element>
+ <xs:element name="brightness" type="xs:float" maxOccurs="unbounded">
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="enabled" type="xs:boolean" use="optional"/>
+ </xs:complexType>
+
<xs:complexType name="highBrightnessMode">
<xs:all>
<xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1"
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 00dc908..c39c3d7 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -113,6 +113,7 @@
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.IdleScreenRefreshRateTimeout getIdleScreenRefreshRateTimeout();
method public final com.android.server.display.config.SensorDetails getLightSensor();
+ method public final com.android.server.display.config.LowBrightnessMode getLowBrightness();
method public com.android.server.display.config.LuxThrottling getLuxThrottling();
method @Nullable public final String getName();
method public com.android.server.display.config.PowerThrottlingConfig getPowerThrottlingConfig();
@@ -149,6 +150,7 @@
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setIdleScreenRefreshRateTimeout(com.android.server.display.config.IdleScreenRefreshRateTimeout);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
+ method public final void setLowBrightness(com.android.server.display.config.LowBrightnessMode);
method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
method public final void setName(@Nullable String);
method public void setPowerThrottlingConfig(com.android.server.display.config.PowerThrottlingConfig);
@@ -248,6 +250,17 @@
method public java.util.List<java.math.BigInteger> getItem();
}
+ public class LowBrightnessMode {
+ ctor public LowBrightnessMode();
+ method public java.util.List<java.lang.Float> getBacklight();
+ method public java.util.List<java.lang.Float> getBrightness();
+ method public boolean getEnabled();
+ method public java.util.List<java.lang.Float> getNits();
+ method public java.math.BigDecimal getTransitionPoint();
+ method public void setEnabled(boolean);
+ method public void setTransitionPoint(java.math.BigDecimal);
+ }
+
public class LuxThrottling {
ctor public LuxThrottling();
method @NonNull public final java.util.List<com.android.server.display.config.BrightnessLimitMap> getBrightnessLimitMap();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 705dac5..347c53a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -51,6 +51,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.testutils.OffsettableClock;
import org.junit.After;
@@ -95,6 +96,8 @@
@Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
@Mock Handler mNoOpHandler;
@Mock BrightnessRangeController mBrightnessRangeController;
+ @Mock
+ BrightnessClamperController mBrightnessClamperController;
@Mock BrightnessThrottler mBrightnessThrottler;
@Before
@@ -160,7 +163,8 @@
mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
mContext, mBrightnessRangeController, mBrightnessThrottler,
useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
- useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits
+ useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
+ mBrightnessClamperController
);
when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 35b69f8..73a2f65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -44,6 +44,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.os.PowerManager;
import android.os.Temperature;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.provider.Settings;
import android.util.SparseArray;
import android.util.Spline;
@@ -57,6 +58,7 @@
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.feature.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -380,7 +382,7 @@
public void testInvalidLuxThrottling() throws Exception {
setupDisplayDeviceConfigFromDisplayConfigFile(
getContent(getInvalidLuxThrottling(), getValidProxSensor(),
- /* includeIdleMode= */ true));
+ /* includeIdleMode= */ true, /* enableEvenDimmer */ false));
Map<DisplayDeviceConfig.BrightnessLimitMapType, Map<Float, Float>> luxThrottlingData =
mDisplayDeviceConfig.getLuxThrottlingData();
@@ -588,7 +590,7 @@
public void testProximitySensorWithEmptyValuesFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile(
getContent(getValidLuxThrottling(), getProxSensorWithEmptyValues(),
- /* includeIdleMode= */ true));
+ /* includeIdleMode= */ true, /* enableEvenDimmer */ false));
assertNull(mDisplayDeviceConfig.getProximitySensor());
}
@@ -596,7 +598,7 @@
public void testProximitySensorWithRefreshRatesFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile(
getContent(getValidLuxThrottling(), getValidProxSensorWithRefreshRateAndVsyncRate(),
- /* includeIdleMode= */ true));
+ /* includeIdleMode= */ true, /* enableEvenDimmer */ false));
assertEquals("test_proximity_sensor",
mDisplayDeviceConfig.getProximitySensor().type);
assertEquals("Test Proximity Sensor",
@@ -784,7 +786,7 @@
@Test
public void testBrightnessRamps_IdleFallsBackToConfigInteractive() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
- getValidProxSensor(), /* includeIdleMode= */ false));
+ getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000);
@@ -801,14 +803,14 @@
@Test
public void testBrightnessCapForWearBedtimeMode() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
- getValidProxSensor(), /* includeIdleMode= */ false));
+ getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
assertEquals(0.1f, mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
}
@Test
public void testAutoBrightnessBrighteningLevels() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
- getValidProxSensor(), /* includeIdleMode= */ false));
+ getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
assertArrayEquals(new float[]{0.0f, 80},
mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
@@ -871,7 +873,7 @@
when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(false);
setupDisplayDeviceConfigFromConfigResourceFile();
setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
- getValidProxSensor(), /* includeIdleMode= */ false));
+ getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
assertArrayEquals(new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100),
brightnessIntToFloat(150)},
@@ -904,6 +906,18 @@
assertFalse(mDisplayDeviceConfig.isAutoBrightnessAvailable());
}
+ @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER)
+ @Test
+ public void testEvenDimmer() throws IOException {
+ when(mFlags.isEvenDimmerEnabled()).thenReturn(true);
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ true));
+
+ assertTrue(mDisplayDeviceConfig.getLbmEnabled());
+ assertEquals(0.0001f, mDisplayDeviceConfig.getBacklightFromBrightness(0.1f), ZERO_DELTA);
+ assertEquals(0.2f, mDisplayDeviceConfig.getNitsFromBacklight(0.0f), ZERO_DELTA);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -1229,11 +1243,11 @@
private String getContent() {
return getContent(getValidLuxThrottling(), getValidProxSensor(),
- /* includeIdleMode= */ true);
+ /* includeIdleMode= */ true, false);
}
private String getContent(String brightnessCapConfig, String proxSensor,
- boolean includeIdleMode) {
+ boolean includeIdleMode, boolean enableEvenDimmer) {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<displayConfiguration>\n"
+ "<name>Example Display</name>\n"
@@ -1603,6 +1617,7 @@
+ "<majorVersion>2</majorVersion>\n"
+ "<minorVersion>0</minorVersion>\n"
+ "</usiVersion>\n"
+ + evenDimmerConfig(enableEvenDimmer)
+ "<screenBrightnessCapForWearBedtimeMode>"
+ "0.1"
+ "</screenBrightnessCapForWearBedtimeMode>"
@@ -1621,6 +1636,24 @@
+ "</displayConfiguration>\n";
}
+ private String evenDimmerConfig(boolean enabled) {
+ return (enabled ? "<lowBrightness enabled=\"true\">" : "<lowBrightness enabled=\"false\">")
+ + " <transitionPoint>0.1</transitionPoint>\n"
+ + " <nits>0.2</nits>\n"
+ + " <nits>2.0</nits>\n"
+ + " <nits>500.0</nits>\n"
+ + " <nits>1000.0</nits>\n"
+ + " <backlight>0</backlight>\n"
+ + " <backlight>0.0001</backlight>\n"
+ + " <backlight>0.5</backlight>\n"
+ + " <backlight>1.0</backlight>\n"
+ + " <brightness>0</brightness>\n"
+ + " <brightness>0.1</brightness>\n"
+ + " <brightness>0.5</brightness>\n"
+ + " <brightness>1.0</brightness>\n"
+ + "</lowBrightness>";
+ }
+
private void mockDeviceConfigs() {
when(mResources.getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingDefaultFloat)).thenReturn(0.5f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index fb23213..19674c2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1184,7 +1184,8 @@
/* ambientLightHorizonShort= */ anyInt(),
/* ambientLightHorizonLong= */ anyInt(),
eq(lux),
- eq(nits)
+ eq(nits),
+ any(BrightnessClamperController.class)
);
}
@@ -2119,7 +2120,8 @@
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessRangeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits) {
+ int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessClamperController brightnessClamperController) {
return mAutomaticBrightnessController;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
index ac7d1f5..e4a7d98 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
@@ -65,7 +65,7 @@
Settings.Secure.putIntForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, userId)
Settings.Secure.putFloatForUser(context.contentResolver,
- Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.7f, userId)
+ Settings.Secure.EVEN_DIMMER_MIN_NITS, 30.0f, userId)
modifier.recalculateLowerBound()
testHandler.flush()
assertThat(modifier.isActive).isTrue()
@@ -81,11 +81,22 @@
Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, userId)
Settings.Secure.putFloatForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.0f, userId)
- modifier.recalculateLowerBound()
+ modifier.onAmbientLuxChange(3000.0f)
testHandler.flush()
assertThat(modifier.isActive).isTrue()
// Test restriction from lux setting
assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
}
+
+ @Test
+ fun testSettingOffDisablesModifier() {
+ Settings.Secure.putIntForUser(context.contentResolver,
+ Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, userId)
+ assertThat(modifier.brightnessLowerBound).isEqualTo(PowerManager.BRIGHTNESS_MIN)
+ modifier.onAmbientLuxChange(3000.0f)
+ testHandler.flush()
+ assertThat(modifier.isActive).isFalse()
+ assertThat(modifier.brightnessLowerBound).isEqualTo(PowerManager.BRIGHTNESS_MIN)
+ }
}