Extracting LightSensorController from ABC
No functional changes
Bug: b/322445088
Test: atest DisplayServiceTests
Change-Id: I6720977d81a816dd3ce982791e9d8fba08dd2685
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 4aab9d2..1546010 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -29,9 +29,6 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -40,20 +37,19 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
import android.util.EventLog;
import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.Clock;
import com.android.server.EventLogTags;
import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.LightSensorController;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import java.io.PrintWriter;
@@ -68,8 +64,6 @@
public class AutomaticBrightnessController {
private static final String TAG = "AutomaticBrightnessController";
- private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
-
public static final int AUTO_BRIGHTNESS_ENABLED = 1;
public static final int AUTO_BRIGHTNESS_DISABLED = 2;
public static final int AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE = 3;
@@ -87,32 +81,20 @@
public static final int AUTO_BRIGHTNESS_MODE_DOZE = 2;
public static final int AUTO_BRIGHTNESS_MODE_MAX = AUTO_BRIGHTNESS_MODE_DOZE;
- // How long the current sensor reading is assumed to be valid beyond the current time.
- // This provides a bit of prediction, as well as ensures that the weight for the last sample is
- // non-zero, which in turn ensures that the total weight is non-zero.
- private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
-
// Debounce for sampling user-initiated changes in display brightness to ensure
// the user is satisfied with the result before storing the sample.
private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
- private static final int MSG_UPDATE_AMBIENT_LUX = 1;
- private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
- private static final int MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL = 3;
- private static final int MSG_UPDATE_FOREGROUND_APP = 4;
- private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
- private static final int MSG_RUN_UPDATE = 6;
- private static final int MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL = 7;
+ private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 1;
+ private static final int MSG_INVALIDATE_CURRENT_SHORT_TERM_MODEL = 2;
+ private static final int MSG_UPDATE_FOREGROUND_APP = 3;
+ private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 4;
+ private static final int MSG_RUN_UPDATE = 5;
+ private static final int MSG_INVALIDATE_PAUSED_SHORT_TERM_MODEL = 6;
// Callbacks for requesting updates to the display's power state
private final Callbacks mCallbacks;
- // The sensor manager.
- private final SensorManager mSensorManager;
-
- // The light sensor, or null if not available or needed.
- private final Sensor mLightSensor;
-
// The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
@NonNull
private BrightnessMappingStrategy mCurrentBrightnessMapper;
@@ -127,94 +109,21 @@
// How much to scale doze brightness by (should be (0, 1.0]).
private final float mDozeScaleFactor;
- // Initial light sensor event rate in milliseconds.
- private final int mInitialLightSensorRate;
-
- // Steady-state light sensor event rate in milliseconds.
- private final int mNormalLightSensorRate;
-
- // The current light sensor event rate in milliseconds.
- private int mCurrentLightSensorRate;
-
- // Stability requirements in milliseconds for accepting a new brightness level. This is used
- // for debouncing the light sensor. Different constants are used to debounce the light sensor
- // when adapting to brighter or darker environments. This parameter controls how quickly
- // brightness changes occur in response to an observed change in light level that exceeds the
- // hysteresis threshold.
- private final long mBrighteningLightDebounceConfig;
- private final long mDarkeningLightDebounceConfig;
- private final long mBrighteningLightDebounceConfigIdle;
- private final long mDarkeningLightDebounceConfigIdle;
-
- // If true immediately after the screen is turned on the controller will try to adjust the
- // brightness based on the current sensor reads. If false, the controller will collect more data
- // and only then decide whether to change brightness.
- private final boolean mResetAmbientLuxAfterWarmUpConfig;
-
- // Period of time in which to consider light samples for a short/long-term estimate of ambient
- // light in milliseconds.
- private final int mAmbientLightHorizonLong;
- private final int mAmbientLightHorizonShort;
-
- // The intercept used for the weighting calculation. This is used in order to keep all possible
- // weighting values positive.
- private final int mWeightingIntercept;
-
// Configuration object for determining thresholds to change brightness dynamically
- private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
- private final HysteresisLevels mAmbientBrightnessThresholdsIdle;
private final HysteresisLevels mScreenBrightnessThresholdsIdle;
private boolean mLoggingEnabled;
-
- // Amount of time to delay auto-brightness after screen on while waiting for
- // the light sensor to warm-up in milliseconds.
- // May be 0 if no warm-up is required.
- private int mLightSensorWarmUpTimeConfig;
-
- // Set to true if the light sensor is enabled.
- private boolean mLightSensorEnabled;
-
- // The time when the light sensor was enabled.
- private long mLightSensorEnableTime;
-
// The currently accepted nominal ambient light level.
private float mAmbientLux;
-
- // The last calculated ambient light level (long time window).
- private float mSlowAmbientLux;
-
- // The last calculated ambient light level (short time window).
- private float mFastAmbientLux;
-
- // The last ambient lux value prior to passing the darkening or brightening threshold.
- private float mPreThresholdLux;
-
// True if mAmbientLux holds a valid value.
private boolean mAmbientLuxValid;
-
- // The ambient light level threshold at which to brighten or darken the screen.
- private float mAmbientBrighteningThreshold;
- private float mAmbientDarkeningThreshold;
-
// The last brightness value prior to passing the darkening or brightening threshold.
private float mPreThresholdBrightness;
// The screen brightness threshold at which to brighten or darken the screen.
private float mScreenBrighteningThreshold;
private float mScreenDarkeningThreshold;
- // The most recent light sample.
- private float mLastObservedLux = INVALID_LUX;
-
- // The time of the most light recent sample.
- private long mLastObservedLuxTime;
-
- // The number of light samples collected since the light sensor was enabled.
- private int mRecentLightSamples;
-
- // A ring buffer containing all of the recent ambient light sensor readings.
- private AmbientLightRingBuffer mAmbientLightRingBuffer;
// The handler
private AutomaticBrightnessHandler mHandler;
@@ -273,88 +182,55 @@
private Context mContext;
private int mState = AUTO_BRIGHTNESS_DISABLED;
- private Clock mClock;
+ private final Clock mClock;
private final Injector mInjector;
- AutomaticBrightnessController(Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor,
+ private final LightSensorController mLightSensorController;
+
+ AutomaticBrightnessController(Callbacks callbacks, Looper looper, SensorManager sensorManager,
SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
- int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
- float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
- long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
- long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
- boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
+ float brightnessMin, float brightnessMax, float dozeScaleFactor,
HysteresisLevels screenBrightnessThresholds,
- HysteresisLevels ambientBrightnessThresholdsIdle,
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessModeController,
- BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessThrottler brightnessThrottler, float userLux, float userNits,
+ int displayId, LightSensorController.LightSensorControllerConfig config,
BrightnessClamperController brightnessClamperController) {
- this(new Injector(), callbacks, looper, sensorManager, lightSensor,
- brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
- dozeScaleFactor, lightSensorRate, initialLightSensorRate,
- brighteningLightDebounceConfig, darkeningLightDebounceConfig,
- brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
- resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
- screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
+ this(new Injector(), callbacks, looper,
+ brightnessMappingStrategyMap, brightnessMin, brightnessMax, dozeScaleFactor,
+ screenBrightnessThresholds,
screenBrightnessThresholdsIdle, context, brightnessModeController,
- brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits, brightnessClamperController
+ brightnessThrottler, userLux, userNits,
+ new LightSensorController(sensorManager, looper, displayId, config),
+ brightnessClamperController
);
}
@VisibleForTesting
AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor,
SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
- int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
- float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
- long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
- long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
- boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
+ float brightnessMin, float brightnessMax, float dozeScaleFactor,
HysteresisLevels screenBrightnessThresholds,
- HysteresisLevels ambientBrightnessThresholdsIdle,
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessRangeController,
- BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessThrottler brightnessThrottler, float userLux, float userNits,
+ LightSensorController lightSensorController,
BrightnessClamperController brightnessClamperController) {
mInjector = injector;
mClock = injector.createClock();
mContext = context;
mCallbacks = callbacks;
- mSensorManager = sensorManager;
mCurrentBrightnessMapper = brightnessMappingStrategyMap.get(AUTO_BRIGHTNESS_MODE_DEFAULT);
mScreenBrightnessRangeMinimum = brightnessMin;
mScreenBrightnessRangeMaximum = brightnessMax;
- mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
mDozeScaleFactor = dozeScaleFactor;
- mNormalLightSensorRate = lightSensorRate;
- mInitialLightSensorRate = initialLightSensorRate;
- mCurrentLightSensorRate = -1;
- mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
- mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
- mBrighteningLightDebounceConfigIdle = brighteningLightDebounceConfigIdle;
- mDarkeningLightDebounceConfigIdle = darkeningLightDebounceConfigIdle;
- mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
- mAmbientLightHorizonLong = ambientLightHorizonLong;
- mAmbientLightHorizonShort = ambientLightHorizonShort;
- mWeightingIntercept = ambientLightHorizonLong;
- mAmbientBrightnessThresholds = ambientBrightnessThresholds;
- mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
mScreenBrightnessThresholds = screenBrightnessThresholds;
mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle;
mShortTermModel = new ShortTermModel();
mPausedShortTermModel = new ShortTermModel();
mHandler = new AutomaticBrightnessHandler(looper);
- mAmbientLightRingBuffer =
- new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizonLong, mClock);
-
- if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
- mLightSensor = lightSensor;
- }
-
+ mLightSensorController = lightSensorController;
+ mLightSensorController.setListener(this::setAmbientLux);
mActivityTaskManager = ActivityTaskManager.getService();
mPackageManager = mContext.getPackageManager();
mTaskStackListener = new TaskStackListenerImpl();
@@ -406,13 +282,13 @@
if (brightnessEvent != null) {
brightnessEvent.setLux(
mAmbientLuxValid ? mAmbientLux : PowerManager.BRIGHTNESS_INVALID_FLOAT);
- brightnessEvent.setPreThresholdLux(mPreThresholdLux);
brightnessEvent.setPreThresholdBrightness(mPreThresholdBrightness);
brightnessEvent.setRecommendedBrightness(mScreenAutoBrightness);
brightnessEvent.setFlags(brightnessEvent.getFlags()
| (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
| (shouldApplyDozeScaleFactor() ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
brightnessEvent.setAutoBrightnessMode(getMode());
+ mLightSensorController.updateBrightnessEvent(brightnessEvent);
}
if (!mAmbientLuxValid) {
@@ -435,21 +311,22 @@
*/
public float getAutomaticScreenBrightnessBasedOnLastObservedLux(
BrightnessEvent brightnessEvent) {
- if (mLastObservedLux == INVALID_LUX) {
+ float lastObservedLux = mLightSensorController.getLastObservedLux();
+ if (lastObservedLux == INVALID_LUX) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
- float brightness = mCurrentBrightnessMapper.getBrightness(mLastObservedLux,
+ float brightness = mCurrentBrightnessMapper.getBrightness(lastObservedLux,
mForegroundAppPackageName, mForegroundAppCategory);
if (shouldApplyDozeScaleFactor()) {
brightness *= mDozeScaleFactor;
}
if (brightnessEvent != null) {
- brightnessEvent.setLux(mLastObservedLux);
+ brightnessEvent.setLux(lastObservedLux);
brightnessEvent.setRecommendedBrightness(brightness);
brightnessEvent.setFlags(brightnessEvent.getFlags()
- | (mLastObservedLux == INVALID_LUX ? BrightnessEvent.FLAG_INVALID_LUX : 0)
+ | (lastObservedLux == INVALID_LUX ? BrightnessEvent.FLAG_INVALID_LUX : 0)
| (shouldApplyDozeScaleFactor() ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
brightnessEvent.setAutoBrightnessMode(getMode());
}
@@ -501,6 +378,7 @@
public void stop() {
setLightSensorEnabled(false);
+ mLightSensorController.stop();
}
public boolean hasUserDataPoints() {
@@ -529,14 +407,6 @@
return mAmbientLux;
}
- float getSlowAmbientLux() {
- return mSlowAmbientLux;
- }
-
- float getFastAmbientLux() {
- return mFastAmbientLux;
- }
-
private boolean setDisplayPolicy(int policy) {
if (mDisplayPolicy == policy) {
return false;
@@ -615,36 +485,13 @@
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
- pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
- pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
- pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
- pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
- pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
- pw.println(" mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
- pw.println(" mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
- pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
- pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
- pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
- pw.println(" mWeightingIntercept=" + mWeightingIntercept);
-
pw.println();
pw.println("Automatic Brightness Controller State:");
- pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
- pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
- pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
pw.println(" mAmbientLux=" + mAmbientLux);
pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
- pw.println(" mPreThresholdLux=" + mPreThresholdLux);
pw.println(" mPreThresholdBrightness=" + mPreThresholdBrightness);
- pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
- pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
pw.println(" mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
pw.println(" mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
- pw.println(" mLastObservedLux=" + mLastObservedLux);
- pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
- pw.println(" mRecentLightSamples=" + mRecentLightSamples);
- pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
pw.println(" mShortTermModel=");
@@ -673,22 +520,21 @@
}
pw.println();
- pw.println(" mAmbientBrightnessThresholds=");
- mAmbientBrightnessThresholds.dump(pw);
pw.println(" mScreenBrightnessThresholds=");
mScreenBrightnessThresholds.dump(pw);
pw.println(" mScreenBrightnessThresholdsIdle=");
mScreenBrightnessThresholdsIdle.dump(pw);
- pw.println(" mAmbientBrightnessThresholdsIdle=");
- mAmbientBrightnessThresholdsIdle.dump(pw);
+
+ pw.println();
+ mLightSensorController.dump(pw);
}
public float[] getLastSensorValues() {
- return mAmbientLightRingBuffer.getAllLuxValues();
+ return mLightSensorController.getLastSensorValues();
}
public long[] getLastSensorTimestamps() {
- return mAmbientLightRingBuffer.getAllTimestamps();
+ return mLightSensorController.getLastSensorTimestamps();
}
private String configStateToString(int state) {
@@ -705,273 +551,33 @@
}
private boolean setLightSensorEnabled(boolean enable) {
- if (enable) {
- if (!mLightSensorEnabled) {
- mLightSensorEnabled = true;
- mLightSensorEnableTime = mClock.uptimeMillis();
- mCurrentLightSensorRate = mInitialLightSensorRate;
- registerForegroundAppUpdater();
- mSensorManager.registerListener(mLightSensorListener, mLightSensor,
- mCurrentLightSensorRate * 1000, mHandler);
- return true;
- }
- } else if (mLightSensorEnabled) {
- mLightSensorEnabled = false;
- mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
- if (!mAmbientLuxValid) {
- mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- }
+ if (enable && mLightSensorController.enableLightSensorIfNeeded()) {
+ registerForegroundAppUpdater();
+ return true;
+ } else if (!enable && mLightSensorController.disableLightSensorIfNeeded()) {
mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mRawScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mRecentLightSamples = 0;
- mAmbientLightRingBuffer.clear();
- mCurrentLightSensorRate = -1;
- mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ mAmbientLuxValid = mLightSensorController.hasValidAmbientLux();
unregisterForegroundAppUpdater();
- mSensorManager.unregisterListener(mLightSensorListener);
}
return false;
}
- private void handleLightSensorEvent(long time, float lux) {
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
- mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
-
- if (mAmbientLightRingBuffer.size() == 0) {
- // switch to using the steady-state sample rate after grabbing the initial light sample
- adjustLightSensorRate(mNormalLightSensorRate);
- }
- applyLightSensorMeasurement(time, lux);
- updateAmbientLux(time);
- }
-
- private void applyLightSensorMeasurement(long time, float lux) {
- mRecentLightSamples++;
- mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
- mAmbientLightRingBuffer.push(time, lux);
-
- // Remember this sample value.
- mLastObservedLux = lux;
- mLastObservedLuxTime = time;
- }
-
- private void adjustLightSensorRate(int lightSensorRate) {
- // if the light sensor rate changed, update the sensor listener
- if (lightSensorRate != mCurrentLightSensorRate) {
- if (mLoggingEnabled) {
- Slog.d(TAG, "adjustLightSensorRate: " +
- "previousRate=" + mCurrentLightSensorRate + ", " +
- "currentRate=" + lightSensorRate);
- }
- mCurrentLightSensorRate = lightSensorRate;
- mSensorManager.unregisterListener(mLightSensorListener);
- mSensorManager.registerListener(mLightSensorListener, mLightSensor,
- lightSensorRate * 1000, mHandler);
- }
- }
-
private boolean setAutoBrightnessAdjustment(float adjustment) {
return mCurrentBrightnessMapper.setAutoBrightnessAdjustment(adjustment);
}
private void setAmbientLux(float lux) {
- if (mLoggingEnabled) {
- Slog.d(TAG, "setAmbientLux(" + lux + ")");
- }
- if (lux < 0) {
- Slog.w(TAG, "Ambient lux was negative, ignoring and setting to 0");
- lux = 0;
- }
+ // called by LightSensorController.setAmbientLux
+ mAmbientLuxValid = true;
mAmbientLux = lux;
- if (isInIdleMode()) {
- mAmbientBrighteningThreshold =
- mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux);
- mAmbientDarkeningThreshold =
- mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(lux);
- } else {
- mAmbientBrighteningThreshold =
- mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
- mAmbientDarkeningThreshold =
- 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);
- }
-
- private float calculateAmbientLux(long now, long horizon) {
- if (mLoggingEnabled) {
- Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
- }
- final int N = mAmbientLightRingBuffer.size();
- if (N == 0) {
- Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
- return -1;
- }
-
- // Find the first measurement that is just outside of the horizon.
- int endIndex = 0;
- final long horizonStartTime = now - horizon;
- for (int i = 0; i < N-1; i++) {
- if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
- endIndex++;
- } else {
- break;
- }
- }
- if (mLoggingEnabled) {
- Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
- mAmbientLightRingBuffer.getTime(endIndex) + ", " +
- mAmbientLightRingBuffer.getLux(endIndex) + ")");
- }
- float sum = 0;
- float totalWeight = 0;
- long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
- for (int i = N - 1; i >= endIndex; i--) {
- long eventTime = mAmbientLightRingBuffer.getTime(i);
- if (i == endIndex && eventTime < horizonStartTime) {
- // If we're at the final value, make sure we only consider the part of the sample
- // within our desired horizon.
- eventTime = horizonStartTime;
- }
- final long startTime = eventTime - now;
- float weight = calculateWeight(startTime, endTime);
- float lux = mAmbientLightRingBuffer.getLux(i);
- if (mLoggingEnabled) {
- Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
- "lux=" + lux + ", " +
- "weight=" + weight);
- }
- totalWeight += weight;
- sum += lux * weight;
- endTime = startTime;
- }
- if (mLoggingEnabled) {
- Slog.d(TAG, "calculateAmbientLux: " +
- "totalWeight=" + totalWeight + ", " +
- "newAmbientLux=" + (sum / totalWeight));
- }
- return sum / totalWeight;
- }
-
- private float calculateWeight(long startDelta, long endDelta) {
- return weightIntegral(endDelta) - weightIntegral(startDelta);
- }
-
- // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
- // horizon we're looking at and provides a non-linear weighting for light samples.
- private float weightIntegral(long x) {
- return x * (x * 0.5f + mWeightingIntercept);
- }
-
- private long nextAmbientLightBrighteningTransition(long time) {
- final int N = mAmbientLightRingBuffer.size();
- long earliestValidTime = time;
- for (int i = N - 1; i >= 0; i--) {
- if (mAmbientLightRingBuffer.getLux(i) <= mAmbientBrighteningThreshold) {
- break;
- }
- earliestValidTime = mAmbientLightRingBuffer.getTime(i);
- }
- return earliestValidTime + (isInIdleMode()
- ? mBrighteningLightDebounceConfigIdle : mBrighteningLightDebounceConfig);
- }
-
- private long nextAmbientLightDarkeningTransition(long time) {
- final int N = mAmbientLightRingBuffer.size();
- long earliestValidTime = time;
- for (int i = N - 1; i >= 0; i--) {
- if (mAmbientLightRingBuffer.getLux(i) >= mAmbientDarkeningThreshold) {
- break;
- }
- earliestValidTime = mAmbientLightRingBuffer.getTime(i);
- }
- return earliestValidTime + (isInIdleMode()
- ? mDarkeningLightDebounceConfigIdle : mDarkeningLightDebounceConfig);
- }
-
- private void updateAmbientLux() {
- long time = mClock.uptimeMillis();
- mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
- updateAmbientLux(time);
- }
-
- private void updateAmbientLux(long time) {
- // If the light sensor was just turned on then immediately update our initial
- // estimate of the current ambient light level.
- if (!mAmbientLuxValid) {
- final long timeWhenSensorWarmedUp =
- mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
- if (time < timeWhenSensorWarmedUp) {
- if (mLoggingEnabled) {
- Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
- + "time=" + time + ", "
- + "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
- }
- mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
- timeWhenSensorWarmedUp);
- return;
- }
- setAmbientLux(calculateAmbientLux(time, mAmbientLightHorizonShort));
- mAmbientLuxValid = true;
- if (mLoggingEnabled) {
- Slog.d(TAG, "updateAmbientLux: Initializing: " +
- "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
- "mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
- }
-
- long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
- long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
- // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
- // change in lighting conditions, and a fast ambient lux to determine what the new
- // brightness situation is since the slow lux can be quite slow to converge.
- //
- // Note that both values need to be checked for sufficient change before updating the
- // proposed ambient light value since the slow value might be sufficiently far enough away
- // from the fast value to cause a recalculation while its actually just converging on
- // the fast value still.
- mSlowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
- mFastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
-
- if ((mSlowAmbientLux >= mAmbientBrighteningThreshold
- && mFastAmbientLux >= mAmbientBrighteningThreshold
- && nextBrightenTransition <= time)
- || (mSlowAmbientLux <= mAmbientDarkeningThreshold
- && mFastAmbientLux <= mAmbientDarkeningThreshold
- && nextDarkenTransition <= time)) {
- mPreThresholdLux = mAmbientLux;
- setAmbientLux(mFastAmbientLux);
- if (mLoggingEnabled) {
- Slog.d(TAG, "updateAmbientLux: "
- + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
- + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
- + "mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold + ", "
- + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
- + "mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
- nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
- nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
- }
- long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
- // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
- // exceed the necessary threshold, then it's possible we'll get a transition time prior to
- // now. Rather than continually checking to see whether the weighted lux exceeds the
- // threshold, schedule an update for when we'd normally expect another light sample, which
- // should be enough time to decide whether we should actually transition to the new
- // weighted ambient lux or not.
- nextTransitionTime =
- nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
- if (mLoggingEnabled) {
- Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
- nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
- }
- mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
+ updateAutoBrightness(true /* sendUpdate */, false /* isManuallySet */);
}
private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
@@ -1072,8 +678,7 @@
if (mLoggingEnabled) {
Slog.d(TAG, "Auto-brightness adjustment changed by user: "
+ "lux=" + mAmbientLux + ", "
- + "brightness=" + mScreenAutoBrightness + ", "
- + "ring=" + mAmbientLightRingBuffer);
+ + "brightness=" + mScreenAutoBrightness);
}
EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ,
@@ -1203,6 +808,7 @@
if (mode == AUTO_BRIGHTNESS_MODE_IDLE
|| mCurrentBrightnessMapper.getMode() == AUTO_BRIGHTNESS_MODE_IDLE) {
switchModeAndShortTermModels(mode);
+ mLightSensorController.setIdleMode(isInIdleMode());
} else {
resetShortTermModel();
mCurrentBrightnessMapper = mBrightnessMappingStrategyMap.get(mode);
@@ -1359,10 +965,6 @@
updateAutoBrightness(true /*sendUpdate*/, false /*isManuallySet*/);
break;
- case MSG_UPDATE_AMBIENT_LUX:
- updateAmbientLux();
- break;
-
case MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE:
collectBrightnessAdjustmentSample();
break;
@@ -1386,22 +988,6 @@
}
}
- private final SensorEventListener mLightSensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mLightSensorEnabled) {
- final long time = mClock.uptimeMillis();
- final float lux = event.values[0];
- handleLightSensorEvent(time, lux);
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // Not used.
- }
- };
-
// Call back whenever the tasks stack changes, which includes tasks being created, removed, and
// moving to top.
class TaskStackListenerImpl extends TaskStackListener {
@@ -1416,192 +1002,13 @@
void updateBrightness();
}
- /** Functional interface for providing time. */
- @VisibleForTesting
- interface Clock {
- /**
- * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
- */
- long uptimeMillis();
- }
-
- /**
- * A ring buffer of ambient light measurements sorted by time.
- *
- * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
- * from oldest to newest.
- */
- private static final class AmbientLightRingBuffer {
- // Proportional extra capacity of the buffer beyond the expected number of light samples
- // in the horizon
- private static final float BUFFER_SLACK = 1.5f;
- private float[] mRingLux;
- private long[] mRingTime;
- private int mCapacity;
-
- // The first valid element and the next open slot.
- // Note that if mCount is zero then there are no valid elements.
- private int mStart;
- private int mEnd;
- private int mCount;
- Clock mClock;
-
- public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon, Clock clock) {
- if (lightSensorRate <= 0) {
- throw new IllegalArgumentException("lightSensorRate must be above 0");
- }
- mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
- mRingLux = new float[mCapacity];
- mRingTime = new long[mCapacity];
- mClock = clock;
- }
-
- public float getLux(int index) {
- return mRingLux[offsetOf(index)];
- }
-
- public float[] getAllLuxValues() {
- float[] values = new float[mCount];
- if (mCount == 0) {
- return values;
- }
-
- if (mStart < mEnd) {
- System.arraycopy(mRingLux, mStart, values, 0, mCount);
- } else {
- System.arraycopy(mRingLux, mStart, values, 0, mCapacity - mStart);
- System.arraycopy(mRingLux, 0, values, mCapacity - mStart, mEnd);
- }
-
- return values;
- }
-
- public long getTime(int index) {
- return mRingTime[offsetOf(index)];
- }
-
- public long[] getAllTimestamps() {
- long[] values = new long[mCount];
- if (mCount == 0) {
- return values;
- }
-
- if (mStart < mEnd) {
- System.arraycopy(mRingTime, mStart, values, 0, mCount);
- } else {
- System.arraycopy(mRingTime, mStart, values, 0, mCapacity - mStart);
- System.arraycopy(mRingTime, 0, values, mCapacity - mStart, mEnd);
- }
-
- return values;
- }
-
- public void push(long time, float lux) {
- int next = mEnd;
- if (mCount == mCapacity) {
- int newSize = mCapacity * 2;
-
- float[] newRingLux = new float[newSize];
- long[] newRingTime = new long[newSize];
- int length = mCapacity - mStart;
- System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
- System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
- if (mStart != 0) {
- System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
- System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
- }
- mRingLux = newRingLux;
- mRingTime = newRingTime;
-
- next = mCapacity;
- mCapacity = newSize;
- mStart = 0;
- }
- mRingTime[next] = time;
- mRingLux[next] = lux;
- mEnd = next + 1;
- if (mEnd == mCapacity) {
- mEnd = 0;
- }
- mCount++;
- }
-
- public void prune(long horizon) {
- if (mCount == 0) {
- return;
- }
-
- while (mCount > 1) {
- int next = mStart + 1;
- if (next >= mCapacity) {
- next -= mCapacity;
- }
- if (mRingTime[next] > horizon) {
- // Some light sensors only produce data upon a change in the ambient light
- // levels, so we need to consider the previous measurement as the ambient light
- // level for all points in time up until we receive a new measurement. Thus, we
- // always want to keep the youngest element that would be removed from the
- // buffer and just set its measurement time to the horizon time since at that
- // point it is the ambient light level, and to remove it would be to drop a
- // valid data point within our horizon.
- break;
- }
- mStart = next;
- mCount -= 1;
- }
-
- if (mRingTime[mStart] < horizon) {
- mRingTime[mStart] = horizon;
- }
- }
-
- public int size() {
- return mCount;
- }
-
- public void clear() {
- mStart = 0;
- mEnd = 0;
- mCount = 0;
- }
-
- @Override
- public String toString() {
- StringBuilder buf = new StringBuilder();
- buf.append('[');
- for (int i = 0; i < mCount; i++) {
- final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
- if (i != 0) {
- buf.append(", ");
- }
- buf.append(getLux(i));
- buf.append(" / ");
- buf.append(next - getTime(i));
- buf.append("ms");
- }
- buf.append(']');
- return buf.toString();
- }
-
- private int offsetOf(int index) {
- if (index >= mCount || index < 0) {
- throw new ArrayIndexOutOfBoundsException(index);
- }
- index += mStart;
- if (index >= mCapacity) {
- index -= mCapacity;
- }
- return index;
- }
- }
-
public static class Injector {
public Handler getBackgroundThreadHandler() {
return BackgroundThread.getHandler();
}
Clock createClock() {
- return SystemClock::uptimeMillis;
+ return Clock.SYSTEM_CLOCK;
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 90ad8c0..0807cc0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -81,6 +81,7 @@
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.brightness.DisplayBrightnessController;
+import com.android.server.display.brightness.LightSensorController;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
@@ -1048,102 +1049,13 @@
}
if (defaultModeBrightnessMapper != null) {
- // Ambient Lux - Active Mode Brightness Thresholds
- float[] ambientBrighteningThresholds =
- mDisplayDeviceConfig.getAmbientBrighteningPercentages();
- float[] ambientDarkeningThresholds =
- mDisplayDeviceConfig.getAmbientDarkeningPercentages();
- float[] ambientBrighteningLevels =
- mDisplayDeviceConfig.getAmbientBrighteningLevels();
- float[] ambientDarkeningLevels =
- mDisplayDeviceConfig.getAmbientDarkeningLevels();
- float ambientDarkeningMinThreshold =
- mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold();
- float ambientBrighteningMinThreshold =
- mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold();
- HysteresisLevels ambientBrightnessThresholds = mInjector.getHysteresisLevels(
- ambientBrighteningThresholds, ambientDarkeningThresholds,
- ambientBrighteningLevels, ambientDarkeningLevels, ambientDarkeningMinThreshold,
- ambientBrighteningMinThreshold);
-
// Display - Active Mode Brightness Thresholds
- float[] screenBrighteningThresholds =
- mDisplayDeviceConfig.getScreenBrighteningPercentages();
- float[] screenDarkeningThresholds =
- mDisplayDeviceConfig.getScreenDarkeningPercentages();
- float[] screenBrighteningLevels =
- mDisplayDeviceConfig.getScreenBrighteningLevels();
- float[] screenDarkeningLevels =
- mDisplayDeviceConfig.getScreenDarkeningLevels();
- float screenDarkeningMinThreshold =
- mDisplayDeviceConfig.getScreenDarkeningMinThreshold();
- float screenBrighteningMinThreshold =
- mDisplayDeviceConfig.getScreenBrighteningMinThreshold();
- HysteresisLevels screenBrightnessThresholds = mInjector.getHysteresisLevels(
- screenBrighteningThresholds, screenDarkeningThresholds,
- screenBrighteningLevels, screenDarkeningLevels, screenDarkeningMinThreshold,
- screenBrighteningMinThreshold, true);
-
- // Ambient Lux - Idle Screen Brightness Thresholds
- float ambientDarkeningMinThresholdIdle =
- mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
- float ambientBrighteningMinThresholdIdle =
- mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
- float[] ambientBrighteningThresholdsIdle =
- mDisplayDeviceConfig.getAmbientBrighteningPercentagesIdle();
- float[] ambientDarkeningThresholdsIdle =
- mDisplayDeviceConfig.getAmbientDarkeningPercentagesIdle();
- float[] ambientBrighteningLevelsIdle =
- mDisplayDeviceConfig.getAmbientBrighteningLevelsIdle();
- float[] ambientDarkeningLevelsIdle =
- mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle();
- HysteresisLevels ambientBrightnessThresholdsIdle = mInjector.getHysteresisLevels(
- ambientBrighteningThresholdsIdle, ambientDarkeningThresholdsIdle,
- ambientBrighteningLevelsIdle, ambientDarkeningLevelsIdle,
- ambientDarkeningMinThresholdIdle, ambientBrighteningMinThresholdIdle);
+ HysteresisLevels screenBrightnessThresholds =
+ mInjector.getBrightnessThresholdsHysteresisLevels(mDisplayDeviceConfig);
// Display - Idle Screen Brightness Thresholds
- float screenDarkeningMinThresholdIdle =
- mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
- float screenBrighteningMinThresholdIdle =
- mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
- float[] screenBrighteningThresholdsIdle =
- mDisplayDeviceConfig.getScreenBrighteningPercentagesIdle();
- float[] screenDarkeningThresholdsIdle =
- mDisplayDeviceConfig.getScreenDarkeningPercentagesIdle();
- float[] screenBrighteningLevelsIdle =
- mDisplayDeviceConfig.getScreenBrighteningLevelsIdle();
- float[] screenDarkeningLevelsIdle =
- mDisplayDeviceConfig.getScreenDarkeningLevelsIdle();
- HysteresisLevels screenBrightnessThresholdsIdle = mInjector.getHysteresisLevels(
- screenBrighteningThresholdsIdle, screenDarkeningThresholdsIdle,
- screenBrighteningLevelsIdle, screenDarkeningLevelsIdle,
- screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
-
- long brighteningLightDebounce = mDisplayDeviceConfig
- .getAutoBrightnessBrighteningLightDebounce();
- long darkeningLightDebounce = mDisplayDeviceConfig
- .getAutoBrightnessDarkeningLightDebounce();
- long brighteningLightDebounceIdle = mDisplayDeviceConfig
- .getAutoBrightnessBrighteningLightDebounceIdle();
- long darkeningLightDebounceIdle = mDisplayDeviceConfig
- .getAutoBrightnessDarkeningLightDebounceIdle();
- boolean autoBrightnessResetAmbientLuxAfterWarmUp = context.getResources().getBoolean(
- R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
-
- int lightSensorWarmUpTimeConfig = context.getResources().getInteger(
- R.integer.config_lightSensorWarmupTime);
- int lightSensorRate = context.getResources().getInteger(
- R.integer.config_autoBrightnessLightSensorRate);
- int initialLightSensorRate = context.getResources().getInteger(
- R.integer.config_autoBrightnessInitialLightSensorRate);
- if (initialLightSensorRate == -1) {
- initialLightSensorRate = lightSensorRate;
- } else if (initialLightSensorRate > lightSensorRate) {
- Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
- + initialLightSensorRate + ") to be less than or equal to "
- + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
- }
+ HysteresisLevels screenBrightnessThresholdsIdle =
+ mInjector.getBrightnessThresholdsIdleHysteresisLevels(mDisplayDeviceConfig);
loadAmbientLightSensor();
// BrightnessTracker should only use one light sensor, we want to use the light sensor
@@ -1155,17 +1067,15 @@
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.stop();
}
+
+ LightSensorController.LightSensorControllerConfig config =
+ mInjector.getLightSensorControllerConfig(context, mDisplayDeviceConfig);
mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController(
- this, handler.getLooper(), mSensorManager, mLightSensor,
- brightnessMappers, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, mDozeScaleFactor, lightSensorRate,
- initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
- brighteningLightDebounceIdle, darkeningLightDebounceIdle,
- autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
- screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
- mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
- mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits,
+ this, handler.getLooper(), mSensorManager, brightnessMappers,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mDozeScaleFactor,
+ screenBrightnessThresholds, screenBrightnessThresholdsIdle,
+ mContext, mBrightnessRangeController,
+ mBrightnessThrottler, userLux, userNits, mDisplayId, config,
mBrightnessClamperController);
mDisplayBrightnessController.setAutomaticBrightnessController(
mAutomaticBrightnessController);
@@ -3165,32 +3075,34 @@
AutomaticBrightnessController getAutomaticBrightnessController(
AutomaticBrightnessController.Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor,
+ SensorManager sensorManager,
SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
- int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
- float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
- long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
- long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
- boolean resetAmbientLuxAfterWarmUpConfig,
- HysteresisLevels ambientBrightnessThresholds,
+ float brightnessMin, float brightnessMax, float dozeScaleFactor,
HysteresisLevels screenBrightnessThresholds,
- HysteresisLevels ambientBrightnessThresholdsIdle,
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessModeController,
- BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessThrottler brightnessThrottler, float userLux, float userNits,
+ int displayId, LightSensorController.LightSensorControllerConfig config,
BrightnessClamperController brightnessClamperController) {
+ return new AutomaticBrightnessController(callbacks, looper, sensorManager,
+ brightnessMappingStrategyMap, brightnessMin, brightnessMax, dozeScaleFactor,
+ screenBrightnessThresholds, screenBrightnessThresholdsIdle, context,
+ brightnessModeController, brightnessThrottler, userLux, userNits, displayId,
+ config, brightnessClamperController);
+ }
- return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor,
- brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin,
- brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
- brighteningLightDebounceConfig, darkeningLightDebounceConfig,
- brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
- resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
- screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
- screenBrightnessThresholdsIdle, context, brightnessModeController,
- brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits, brightnessClamperController);
+ LightSensorController.LightSensorControllerConfig getLightSensorControllerConfig(
+ Context context, DisplayDeviceConfig displayDeviceConfig) {
+ return LightSensorController.LightSensorControllerConfig.create(
+ context.getResources(), displayDeviceConfig);
+ }
+
+ HysteresisLevels getBrightnessThresholdsIdleHysteresisLevels(DisplayDeviceConfig ddc) {
+ return HysteresisLevels.getBrightnessThresholdsIdle(ddc);
+ }
+
+ HysteresisLevels getBrightnessThresholdsHysteresisLevels(DisplayDeviceConfig ddc) {
+ return HysteresisLevels.getBrightnessThresholds(ddc);
}
BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
@@ -3200,25 +3112,6 @@
AUTO_BRIGHTNESS_MODE_DEFAULT, displayWhiteBalanceController);
}
- HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
- float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
- float[] darkeningThresholdLevels, float minDarkeningThreshold,
- float minBrighteningThreshold) {
- return new HysteresisLevels(brighteningThresholdsPercentages,
- darkeningThresholdsPercentages, brighteningThresholdLevels,
- darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold);
- }
-
- HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
- float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
- float[] darkeningThresholdLevels, float minDarkeningThreshold,
- float minBrighteningThreshold, boolean potentialOldBrightnessRange) {
- return new HysteresisLevels(brighteningThresholdsPercentages,
- darkeningThresholdsPercentages, brighteningThresholdLevels,
- darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold,
- potentialOldBrightnessRange);
- }
-
ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
SensorManager sensorManager,
Sensor lightSensor,
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 0521b8a..bb349e7 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,6 +18,7 @@
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.utils.DebugUtils;
import java.io.PrintWriter;
@@ -52,7 +53,8 @@
* @param potentialOldBrightnessRange whether or not the values used could be from the old
* screen brightness range ie, between 1-255.
*/
- HysteresisLevels(float[] brighteningThresholdsPercentages,
+ @VisibleForTesting
+ public HysteresisLevels(float[] brighteningThresholdsPercentages,
float[] darkeningThresholdsPercentages,
float[] brighteningThresholdLevels, float[] darkeningThresholdLevels,
float minDarkeningThreshold, float minBrighteningThreshold,
@@ -138,7 +140,10 @@
return levelArray;
}
- void dump(PrintWriter pw) {
+ /**
+ * Print the object's debug information into the given stream.
+ */
+ public void dump(PrintWriter pw) {
pw.println("HysteresisLevels");
pw.println(" mBrighteningThresholdLevels=" + Arrays.toString(mBrighteningThresholdLevels));
pw.println(" mBrighteningThresholdsPercentages="
@@ -149,4 +154,45 @@
+ Arrays.toString(mDarkeningThresholdsPercentages));
pw.println(" mMinDarkening=" + mMinDarkening);
}
+
+
+ /**
+ * Creates hysteresis levels for Active Ambient Lux
+ */
+ public static HysteresisLevels getAmbientBrightnessThresholds(DisplayDeviceConfig ddc) {
+ return new HysteresisLevels(ddc.getAmbientBrighteningPercentages(),
+ ddc.getAmbientDarkeningPercentages(), ddc.getAmbientBrighteningLevels(),
+ ddc.getAmbientDarkeningLevels(), ddc.getAmbientLuxDarkeningMinThreshold(),
+ ddc.getAmbientLuxBrighteningMinThreshold());
+ }
+
+ /**
+ * Creates hysteresis levels for Active Screen Brightness
+ */
+ public static HysteresisLevels getBrightnessThresholds(DisplayDeviceConfig ddc) {
+ return new HysteresisLevels(ddc.getScreenBrighteningPercentages(),
+ ddc.getScreenDarkeningPercentages(), ddc.getScreenBrighteningLevels(),
+ ddc.getScreenDarkeningLevels(), ddc.getScreenDarkeningMinThreshold(),
+ ddc.getScreenBrighteningMinThreshold(), true);
+ }
+
+ /**
+ * Creates hysteresis levels for Idle Ambient Lux
+ */
+ public static HysteresisLevels getAmbientBrightnessThresholdsIdle(DisplayDeviceConfig ddc) {
+ return new HysteresisLevels(ddc.getAmbientBrighteningPercentagesIdle(),
+ ddc.getAmbientDarkeningPercentagesIdle(), ddc.getAmbientBrighteningLevelsIdle(),
+ ddc.getAmbientDarkeningLevelsIdle(), ddc.getAmbientLuxDarkeningMinThresholdIdle(),
+ ddc.getAmbientLuxBrighteningMinThresholdIdle());
+ }
+
+ /**
+ * Creates hysteresis levels for Idle Screen Brightness
+ */
+ public static HysteresisLevels getBrightnessThresholdsIdle(DisplayDeviceConfig ddc) {
+ return new HysteresisLevels(ddc.getScreenBrighteningPercentagesIdle(),
+ ddc.getScreenDarkeningPercentagesIdle(), ddc.getScreenBrighteningLevelsIdle(),
+ ddc.getScreenDarkeningLevelsIdle(), ddc.getScreenDarkeningMinThresholdIdle(),
+ ddc.getScreenBrighteningMinThresholdIdle());
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/LightSensorController.java b/services/core/java/com/android/server/display/brightness/LightSensorController.java
new file mode 100644
index 0000000..d82d698
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/LightSensorController.java
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) 2024 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.brightness;
+
+import static com.android.server.display.BrightnessMappingStrategy.INVALID_LUX;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.Trace;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.HysteresisLevels;
+import com.android.server.display.config.SensorData;
+import com.android.server.display.utils.SensorUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages light sensor subscription and notifies its listeners about ambient lux changes based on
+ * configuration
+ */
+public class LightSensorController {
+ // How long the current sensor reading is assumed to be valid beyond the current time.
+ // This provides a bit of prediction, as well as ensures that the weight for the last sample is
+ // non-zero, which in turn ensures that the total weight is non-zero.
+ private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
+
+ // Proportional extra capacity of the buffer beyond the expected number of light samples
+ // in the horizon
+ private static final float BUFFER_SLACK = 1.5f;
+
+ private boolean mLoggingEnabled;
+ private boolean mLightSensorEnabled;
+ private long mLightSensorEnableTime;
+ // The current light sensor event rate in milliseconds.
+ private int mCurrentLightSensorRate = -1;
+ // The number of light samples collected since the light sensor was enabled.
+ private int mRecentLightSamples;
+ private float mAmbientLux;
+ // True if mAmbientLux holds a valid value.
+ private boolean mAmbientLuxValid;
+ // The last ambient lux value prior to passing the darkening or brightening threshold.
+ private float mPreThresholdLux;
+ // The most recent light sample.
+ private float mLastObservedLux = INVALID_LUX;
+ // The time of the most light recent sample.
+ private long mLastObservedLuxTime;
+ // The last calculated ambient light level (long time window).
+ private float mSlowAmbientLux;
+ // The last calculated ambient light level (short time window).
+ private float mFastAmbientLux;
+ private volatile boolean mIsIdleMode;
+ // The ambient light level threshold at which to brighten or darken the screen.
+ private float mAmbientBrighteningThreshold;
+ private float mAmbientDarkeningThreshold;
+
+ private final LightSensorControllerConfig mConfig;
+
+ // The light sensor, or null if not available or needed.
+ @Nullable
+ private final Sensor mLightSensor;
+
+ // A ring buffer containing all of the recent ambient light sensor readings.
+ private final AmbientLightRingBuffer mAmbientLightRingBuffer;
+
+ private final Injector mInjector;
+
+ private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mLightSensorEnabled) {
+ final long time = mClock.uptimeMillis();
+ final float lux = event.values[0];
+ handleLightSensorEvent(time, lux);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ // Runnable used to delay ambient lux update when:
+ // 1) update triggered before configured warm up time
+ // 2) next brightening or darkening transition need to happen
+ private final Runnable mAmbientLuxUpdater = this::updateAmbientLux;
+
+ private final Clock mClock;
+
+ private final Handler mHandler;
+
+ private final String mTag;
+
+ private LightSensorListener mListener;
+
+ public LightSensorController(
+ SensorManager sensorManager,
+ Looper looper,
+ int displayId,
+ LightSensorControllerConfig config) {
+ this(config, new RealInjector(sensorManager, displayId), new LightSensorHandler(looper));
+ }
+
+ @VisibleForTesting
+ LightSensorController(
+ LightSensorControllerConfig config,
+ Injector injector,
+ Handler handler) {
+ if (config.mNormalLightSensorRate <= 0) {
+ throw new IllegalArgumentException("lightSensorRate must be above 0");
+ }
+ mInjector = injector;
+ int bufferInitialCapacity = (int) Math.ceil(
+ config.mAmbientLightHorizonLong * BUFFER_SLACK / config.mNormalLightSensorRate);
+ mClock = injector.getClock();
+ mHandler = handler;
+ mAmbientLightRingBuffer = new AmbientLightRingBuffer(bufferInitialCapacity, mClock);
+ mConfig = config;
+ mLightSensor = mInjector.getLightSensor(mConfig);
+ mTag = mInjector.getTag();
+ }
+
+ public void setListener(LightSensorListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * @return true if sensor registered, false if sensor already registered
+ */
+ public boolean enableLightSensorIfNeeded() {
+ if (!mLightSensorEnabled) {
+ mLightSensorEnabled = true;
+ mLightSensorEnableTime = mClock.uptimeMillis();
+ mCurrentLightSensorRate = mConfig.mInitialLightSensorRate;
+ mInjector.registerLightSensorListener(
+ mLightSensorListener, mLightSensor, mCurrentLightSensorRate, mHandler);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if sensor unregistered, false if sensor already unregistered
+ */
+ public boolean disableLightSensorIfNeeded() {
+ if (mLightSensorEnabled) {
+ mLightSensorEnabled = false;
+ mAmbientLuxValid = !mConfig.mResetAmbientLuxAfterWarmUpConfig;
+ if (!mAmbientLuxValid) {
+ mPreThresholdLux = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+ mRecentLightSamples = 0;
+ mAmbientLightRingBuffer.clear();
+ mCurrentLightSensorRate = -1;
+ mInjector.unregisterLightSensorListener(mLightSensorListener);
+ return true;
+ }
+ return false;
+ }
+
+ public void setLoggingEnabled(boolean loggingEnabled) {
+ mLoggingEnabled = loggingEnabled;
+ }
+
+ /**
+ * Updates BrightnessEvent with LightSensorController details
+ */
+ public void updateBrightnessEvent(BrightnessEvent brightnessEvent) {
+ brightnessEvent.setPreThresholdLux(mPreThresholdLux);
+ }
+
+ /**
+ * Print the object's debug information into the given stream.
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("LightSensorController state:");
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+ pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
+ pw.println(" mRecentLightSamples=" + mRecentLightSamples);
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
+ pw.println(" mPreThresholdLux=" + mPreThresholdLux);
+ pw.println(" mLastObservedLux=" + mLastObservedLux);
+ pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
+ pw.println(" mSlowAmbientLux=" + mSlowAmbientLux);
+ pw.println(" mFastAmbientLux=" + mFastAmbientLux);
+ pw.println(" mIsIdleMode=" + mIsIdleMode);
+ pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
+ pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
+ pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
+ pw.println(" mLightSensor=" + mLightSensor);
+ mConfig.dump(pw);
+ }
+
+ /**
+ * This method should be called when this LightSensorController is no longer in use
+ * i.e. when corresponding display removed
+ */
+ public void stop() {
+ mHandler.removeCallbacksAndMessages(null);
+ disableLightSensorIfNeeded();
+ }
+
+ public void setIdleMode(boolean isIdleMode) {
+ mIsIdleMode = isIdleMode;
+ }
+
+ /**
+ * returns true if LightSensorController holds valid ambient lux value
+ */
+ public boolean hasValidAmbientLux() {
+ return mAmbientLuxValid;
+ }
+
+ /**
+ * returns all last observed sensor values
+ */
+ public float[] getLastSensorValues() {
+ return mAmbientLightRingBuffer.getAllLuxValues();
+ }
+
+ /**
+ * returns all last observed sensor event timestamps
+ */
+ public long[] getLastSensorTimestamps() {
+ return mAmbientLightRingBuffer.getAllTimestamps();
+ }
+
+ public float getLastObservedLux() {
+ return mLastObservedLux;
+ }
+
+ private void handleLightSensorEvent(long time, float lux) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
+ mHandler.removeCallbacks(mAmbientLuxUpdater);
+
+ if (mAmbientLightRingBuffer.size() == 0) {
+ // switch to using the steady-state sample rate after grabbing the initial light sample
+ adjustLightSensorRate(mConfig.mNormalLightSensorRate);
+ }
+ applyLightSensorMeasurement(time, lux);
+ updateAmbientLux(time);
+ }
+
+ private void applyLightSensorMeasurement(long time, float lux) {
+ mRecentLightSamples++;
+ mAmbientLightRingBuffer.prune(time - mConfig.mAmbientLightHorizonLong);
+ mAmbientLightRingBuffer.push(time, lux);
+ // Remember this sample value.
+ mLastObservedLux = lux;
+ mLastObservedLuxTime = time;
+ }
+
+ private void adjustLightSensorRate(int lightSensorRate) {
+ // if the light sensor rate changed, update the sensor listener
+ if (lightSensorRate != mCurrentLightSensorRate) {
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "adjustLightSensorRate: "
+ + "previousRate=" + mCurrentLightSensorRate + ", "
+ + "currentRate=" + lightSensorRate);
+ }
+ mCurrentLightSensorRate = lightSensorRate;
+ mInjector.unregisterLightSensorListener(mLightSensorListener);
+ mInjector.registerLightSensorListener(
+ mLightSensorListener, mLightSensor, lightSensorRate, mHandler);
+ }
+ }
+
+ private void setAmbientLux(float lux) {
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "setAmbientLux(" + lux + ")");
+ }
+ if (lux < 0) {
+ Slog.w(mTag, "Ambient lux was negative, ignoring and setting to 0");
+ lux = 0;
+ }
+ mAmbientLux = lux;
+
+ if (mIsIdleMode) {
+ mAmbientBrighteningThreshold =
+ mConfig.mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux);
+ mAmbientDarkeningThreshold =
+ mConfig.mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(lux);
+ } else {
+ mAmbientBrighteningThreshold =
+ mConfig.mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
+ mAmbientDarkeningThreshold =
+ mConfig.mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+ }
+
+ mListener.onAmbientLuxChange(mAmbientLux);
+ }
+
+ private float calculateAmbientLux(long now, long horizon) {
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "calculateAmbientLux(" + now + ", " + horizon + ")");
+ }
+ final int size = mAmbientLightRingBuffer.size();
+ if (size == 0) {
+ Slog.e(mTag, "calculateAmbientLux: No ambient light readings available");
+ return -1;
+ }
+
+ // Find the first measurement that is just outside of the horizon.
+ int endIndex = 0;
+ final long horizonStartTime = now - horizon;
+ for (int i = 0; i < size - 1; i++) {
+ if (mAmbientLightRingBuffer.getTime(i + 1) <= horizonStartTime) {
+ endIndex++;
+ } else {
+ break;
+ }
+ }
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=("
+ + mAmbientLightRingBuffer.getTime(endIndex) + ", "
+ + mAmbientLightRingBuffer.getLux(endIndex) + ")");
+ }
+ float sum = 0;
+ float totalWeight = 0;
+ long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
+ for (int i = size - 1; i >= endIndex; i--) {
+ long eventTime = mAmbientLightRingBuffer.getTime(i);
+ if (i == endIndex && eventTime < horizonStartTime) {
+ // If we're at the final value, make sure we only consider the part of the sample
+ // within our desired horizon.
+ eventTime = horizonStartTime;
+ }
+ final long startTime = eventTime - now;
+ float weight = calculateWeight(startTime, endTime);
+ float lux = mAmbientLightRingBuffer.getLux(i);
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: "
+ + "lux=" + lux + ", "
+ + "weight=" + weight);
+ }
+ totalWeight += weight;
+ sum += lux * weight;
+ endTime = startTime;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "calculateAmbientLux: "
+ + "totalWeight=" + totalWeight + ", "
+ + "newAmbientLux=" + (sum / totalWeight));
+ }
+ return sum / totalWeight;
+ }
+
+ private float calculateWeight(long startDelta, long endDelta) {
+ return weightIntegral(endDelta) - weightIntegral(startDelta);
+ }
+
+ // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
+ // horizon we're looking at and provides a non-linear weighting for light samples.
+ private float weightIntegral(long x) {
+ return x * (x * 0.5f + mConfig.mWeightingIntercept);
+ }
+
+ private long nextAmbientLightBrighteningTransition(long time) {
+ final int size = mAmbientLightRingBuffer.size();
+ long earliestValidTime = time;
+ for (int i = size - 1; i >= 0; i--) {
+ if (mAmbientLightRingBuffer.getLux(i) <= mAmbientBrighteningThreshold) {
+ break;
+ }
+ earliestValidTime = mAmbientLightRingBuffer.getTime(i);
+ }
+ return earliestValidTime + (mIsIdleMode ? mConfig.mBrighteningLightDebounceConfigIdle
+ : mConfig.mBrighteningLightDebounceConfig);
+ }
+
+ private long nextAmbientLightDarkeningTransition(long time) {
+ final int size = mAmbientLightRingBuffer.size();
+ long earliestValidTime = time;
+ for (int i = size - 1; i >= 0; i--) {
+ if (mAmbientLightRingBuffer.getLux(i) >= mAmbientDarkeningThreshold) {
+ break;
+ }
+ earliestValidTime = mAmbientLightRingBuffer.getTime(i);
+ }
+ return earliestValidTime + (mIsIdleMode ? mConfig.mDarkeningLightDebounceConfigIdle
+ : mConfig.mDarkeningLightDebounceConfig);
+ }
+
+ private void updateAmbientLux() {
+ long time = mClock.uptimeMillis();
+ mAmbientLightRingBuffer.prune(time - mConfig.mAmbientLightHorizonLong);
+ updateAmbientLux(time);
+ }
+
+ private void updateAmbientLux(long time) {
+ // If the light sensor was just turned on then immediately update our initial
+ // estimate of the current ambient light level.
+ if (!mAmbientLuxValid) {
+ final long timeWhenSensorWarmedUp =
+ mConfig.mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
+ if (time < timeWhenSensorWarmedUp) {
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "updateAmbientLux: Sensor not ready yet: "
+ + "time=" + time + ", "
+ + "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
+ }
+ mHandler.postAtTime(mAmbientLuxUpdater, timeWhenSensorWarmedUp);
+ return;
+ }
+ mAmbientLuxValid = true;
+ setAmbientLux(calculateAmbientLux(time, mConfig.mAmbientLightHorizonShort));
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "updateAmbientLux: Initializing: "
+ + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ + "mAmbientLux=" + mAmbientLux);
+ }
+ }
+
+ long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
+ long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ // Essentially, we calculate both a slow ambient lux, to ensure there's a true long-term
+ // change in lighting conditions, and a fast ambient lux to determine what the new
+ // brightness situation is since the slow lux can be quite slow to converge.
+ //
+ // Note that both values need to be checked for sufficient change before updating the
+ // proposed ambient light value since the slow value might be sufficiently far enough away
+ // from the fast value to cause a recalculation while its actually just converging on
+ // the fast value still.
+ mSlowAmbientLux = calculateAmbientLux(time, mConfig.mAmbientLightHorizonLong);
+ mFastAmbientLux = calculateAmbientLux(time, mConfig.mAmbientLightHorizonShort);
+
+ if ((mSlowAmbientLux >= mAmbientBrighteningThreshold
+ && mFastAmbientLux >= mAmbientBrighteningThreshold
+ && nextBrightenTransition <= time)
+ || (mSlowAmbientLux <= mAmbientDarkeningThreshold
+ && mFastAmbientLux <= mAmbientDarkeningThreshold
+ && nextDarkenTransition <= time)) {
+ mPreThresholdLux = mAmbientLux;
+ setAmbientLux(mFastAmbientLux);
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "updateAmbientLux: "
+ + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ + "mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold + ", "
+ + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ + "mAmbientLux=" + mAmbientLux);
+ }
+ nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
+ nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ }
+ long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
+ // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
+ // exceed the necessary threshold, then it's possible we'll get a transition time prior to
+ // now. Rather than continually checking to see whether the weighted lux exceeds the
+ // threshold, schedule an update for when we'd normally expect another light sample, which
+ // should be enough time to decide whether we should actually transition to the new
+ // weighted ambient lux or not.
+ nextTransitionTime = nextTransitionTime > time ? nextTransitionTime
+ : time + mConfig.mNormalLightSensorRate;
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "updateAmbientLux: Scheduling ambient lux update for "
+ + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
+ }
+ mHandler.postAtTime(mAmbientLuxUpdater, nextTransitionTime);
+ }
+
+ public interface LightSensorListener {
+ /**
+ * Called when new ambient lux value is ready
+ */
+ void onAmbientLuxChange(float ambientLux);
+ }
+
+ private static final class LightSensorHandler extends Handler {
+ private LightSensorHandler(Looper looper) {
+ super(looper, /* callback= */ null, /* async= */ true);
+ }
+ }
+
+ /**
+ * A ring buffer of ambient light measurements sorted by time.
+ * Each entry consists of a timestamp and a lux measurement, and the overall buffer is sorted
+ * from oldest to newest.
+ */
+ @VisibleForTesting
+ static final class AmbientLightRingBuffer {
+
+ private float[] mRingLux;
+ private long[] mRingTime;
+ private int mCapacity;
+
+ // The first valid element and the next open slot.
+ // Note that if mCount is zero then there are no valid elements.
+ private int mStart;
+ private int mEnd;
+ private int mCount;
+
+ private final Clock mClock;
+
+ @VisibleForTesting
+ AmbientLightRingBuffer(int initialCapacity, Clock clock) {
+ mCapacity = initialCapacity;
+ mRingLux = new float[mCapacity];
+ mRingTime = new long[mCapacity];
+ mClock = clock;
+
+ }
+
+ @VisibleForTesting
+ float getLux(int index) {
+ return mRingLux[offsetOf(index)];
+ }
+
+ @VisibleForTesting
+ float[] getAllLuxValues() {
+ float[] values = new float[mCount];
+ if (mCount == 0) {
+ return values;
+ }
+
+ if (mStart < mEnd) {
+ System.arraycopy(mRingLux, mStart, values, 0, mCount);
+ } else {
+ System.arraycopy(mRingLux, mStart, values, 0, mCapacity - mStart);
+ System.arraycopy(mRingLux, 0, values, mCapacity - mStart, mEnd);
+ }
+
+ return values;
+ }
+
+ @VisibleForTesting
+ long getTime(int index) {
+ return mRingTime[offsetOf(index)];
+ }
+
+ @VisibleForTesting
+ long[] getAllTimestamps() {
+ long[] values = new long[mCount];
+ if (mCount == 0) {
+ return values;
+ }
+
+ if (mStart < mEnd) {
+ System.arraycopy(mRingTime, mStart, values, 0, mCount);
+ } else {
+ System.arraycopy(mRingTime, mStart, values, 0, mCapacity - mStart);
+ System.arraycopy(mRingTime, 0, values, mCapacity - mStart, mEnd);
+ }
+
+ return values;
+ }
+
+ @VisibleForTesting
+ void push(long time, float lux) {
+ int next = mEnd;
+ if (mCount == mCapacity) {
+ int newSize = mCapacity * 2;
+
+ float[] newRingLux = new float[newSize];
+ long[] newRingTime = new long[newSize];
+ int length = mCapacity - mStart;
+ System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
+ System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
+ if (mStart != 0) {
+ System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
+ System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
+ }
+ mRingLux = newRingLux;
+ mRingTime = newRingTime;
+
+ next = mCapacity;
+ mCapacity = newSize;
+ mStart = 0;
+ }
+ mRingTime[next] = time;
+ mRingLux[next] = lux;
+ mEnd = next + 1;
+ if (mEnd == mCapacity) {
+ mEnd = 0;
+ }
+ mCount++;
+ }
+
+ @VisibleForTesting
+ void prune(long horizon) {
+ if (mCount == 0) {
+ return;
+ }
+
+ while (mCount > 1) {
+ int next = mStart + 1;
+ if (next >= mCapacity) {
+ next -= mCapacity;
+ }
+ if (mRingTime[next] > horizon) {
+ // Some light sensors only produce data upon a change in the ambient light
+ // levels, so we need to consider the previous measurement as the ambient light
+ // level for all points in time up until we receive a new measurement. Thus, we
+ // always want to keep the youngest element that would be removed from the
+ // buffer and just set its measurement time to the horizon time since at that
+ // point it is the ambient light level, and to remove it would be to drop a
+ // valid data point within our horizon.
+ break;
+ }
+ mStart = next;
+ mCount -= 1;
+ }
+
+ if (mRingTime[mStart] < horizon) {
+ mRingTime[mStart] = horizon;
+ }
+ }
+
+ @VisibleForTesting
+ int size() {
+ return mCount;
+ }
+
+ @VisibleForTesting
+ void clear() {
+ mStart = 0;
+ mEnd = 0;
+ mCount = 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append('[');
+ for (int i = 0; i < mCount; i++) {
+ final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
+ if (i != 0) {
+ buf.append(", ");
+ }
+ buf.append(getLux(i));
+ buf.append(" / ");
+ buf.append(next - getTime(i));
+ buf.append("ms");
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+ private int offsetOf(int index) {
+ if (index >= mCount || index < 0) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ index += mStart;
+ if (index >= mCapacity) {
+ index -= mCapacity;
+ }
+ return index;
+ }
+ }
+
+ @VisibleForTesting
+ interface Injector {
+ Clock getClock();
+
+ Sensor getLightSensor(LightSensorControllerConfig config);
+
+ boolean registerLightSensorListener(
+ SensorEventListener listener, Sensor sensor, int rate, Handler handler);
+
+ void unregisterLightSensorListener(SensorEventListener listener);
+
+ String getTag();
+
+ }
+
+ private static class RealInjector implements Injector {
+ private final SensorManager mSensorManager;
+ private final int mSensorFallbackType;
+
+ private final String mTag;
+
+ private RealInjector(SensorManager sensorManager, int displayId) {
+ mSensorManager = sensorManager;
+ mSensorFallbackType = displayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
+ mTag = "LightSensorController [" + displayId + "]";
+ }
+
+ @Override
+ public Clock getClock() {
+ return Clock.SYSTEM_CLOCK;
+ }
+
+ @Override
+ public Sensor getLightSensor(LightSensorControllerConfig config) {
+ return SensorUtils.findSensor(
+ mSensorManager, config.mAmbientLightSensor, mSensorFallbackType);
+ }
+
+ @Override
+ public boolean registerLightSensorListener(
+ SensorEventListener listener, Sensor sensor, int rate, Handler handler) {
+ return mSensorManager.registerListener(listener, sensor, rate * 1000, handler);
+ }
+
+ @Override
+ public void unregisterLightSensorListener(SensorEventListener listener) {
+ mSensorManager.unregisterListener(listener);
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+ }
+
+ public static class LightSensorControllerConfig {
+ // Steady-state light sensor event rate in milliseconds.
+ private final int mNormalLightSensorRate;
+ private final int mInitialLightSensorRate;
+
+ // If true immediately after the screen is turned on the controller will try to adjust the
+ // brightness based on the current sensor reads. If false, the controller will collect
+ // more data
+ // and only then decide whether to change brightness.
+ private final boolean mResetAmbientLuxAfterWarmUpConfig;
+
+ // Period of time in which to consider light samples for a short/long-term estimate of
+ // ambient
+ // light in milliseconds.
+ private final int mAmbientLightHorizonShort;
+ private final int mAmbientLightHorizonLong;
+
+
+ // Amount of time to delay auto-brightness after screen on while waiting for
+ // the light sensor to warm-up in milliseconds.
+ // May be 0 if no warm-up is required.
+ private final int mLightSensorWarmUpTimeConfig;
+
+
+ // The intercept used for the weighting calculation. This is used in order to keep all
+ // possible
+ // weighting values positive.
+ private final int mWeightingIntercept;
+
+ // Configuration object for determining thresholds to change brightness dynamically
+ private final HysteresisLevels mAmbientBrightnessThresholds;
+ private final HysteresisLevels mAmbientBrightnessThresholdsIdle;
+
+
+ // Stability requirements in milliseconds for accepting a new brightness level. This is
+ // used
+ // for debouncing the light sensor. Different constants are used to debounce the light
+ // sensor
+ // when adapting to brighter or darker environments. This parameter controls how quickly
+ // brightness changes occur in response to an observed change in light level that exceeds
+ // the
+ // hysteresis threshold.
+ private final long mBrighteningLightDebounceConfig;
+ private final long mDarkeningLightDebounceConfig;
+ private final long mBrighteningLightDebounceConfigIdle;
+ private final long mDarkeningLightDebounceConfigIdle;
+
+ private final SensorData mAmbientLightSensor;
+
+ @VisibleForTesting
+ LightSensorControllerConfig(int initialLightSensorRate, int normalLightSensorRate,
+ boolean resetAmbientLuxAfterWarmUpConfig, int ambientLightHorizonShort,
+ int ambientLightHorizonLong, int lightSensorWarmUpTimeConfig,
+ int weightingIntercept, HysteresisLevels ambientBrightnessThresholds,
+ HysteresisLevels ambientBrightnessThresholdsIdle,
+ long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+ long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
+ SensorData ambientLightSensor) {
+ mInitialLightSensorRate = initialLightSensorRate;
+ mNormalLightSensorRate = normalLightSensorRate;
+ mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
+ mAmbientLightHorizonShort = ambientLightHorizonShort;
+ mAmbientLightHorizonLong = ambientLightHorizonLong;
+ mLightSensorWarmUpTimeConfig = lightSensorWarmUpTimeConfig;
+ mWeightingIntercept = weightingIntercept;
+ mAmbientBrightnessThresholds = ambientBrightnessThresholds;
+ mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
+ mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
+ mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
+ mBrighteningLightDebounceConfigIdle = brighteningLightDebounceConfigIdle;
+ mDarkeningLightDebounceConfigIdle = darkeningLightDebounceConfigIdle;
+ mAmbientLightSensor = ambientLightSensor;
+ }
+
+ private void dump(PrintWriter pw) {
+ pw.println("LightSensorControllerConfig:");
+ pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
+ pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
+ pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
+ pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
+ pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
+ pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+ pw.println(" mWeightingIntercept=" + mWeightingIntercept);
+ pw.println(" mAmbientBrightnessThresholds=");
+ mAmbientBrightnessThresholds.dump(pw);
+ pw.println(" mAmbientBrightnessThresholdsIdle=");
+ mAmbientBrightnessThresholdsIdle.dump(pw);
+ pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
+ pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+ pw.println(
+ " mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+ pw.println(" mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
+ pw.println(" mAmbientLightSensor=" + mAmbientLightSensor);
+ }
+
+ /**
+ * Creates LightSensorControllerConfig object form Resources and DisplayDeviceConfig
+ */
+ public static LightSensorControllerConfig create(Resources res, DisplayDeviceConfig ddc) {
+ int lightSensorRate = res.getInteger(R.integer.config_autoBrightnessLightSensorRate);
+ int initialLightSensorRate = res.getInteger(
+ R.integer.config_autoBrightnessInitialLightSensorRate);
+ if (initialLightSensorRate == -1) {
+ initialLightSensorRate = lightSensorRate;
+ } else if (initialLightSensorRate > lightSensorRate) {
+ Slog.w("LightSensorControllerConfig",
+ "Expected config_autoBrightnessInitialLightSensorRate ("
+ + initialLightSensorRate + ") to be less than or equal to "
+ + "config_autoBrightnessLightSensorRate (" + lightSensorRate
+ + ").");
+ }
+
+ boolean resetAmbientLuxAfterWarmUp = res.getBoolean(
+ R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+ int lightSensorWarmUpTimeConfig = res.getInteger(
+ R.integer.config_lightSensorWarmupTime);
+
+ return new LightSensorControllerConfig(initialLightSensorRate, lightSensorRate,
+ resetAmbientLuxAfterWarmUp, ddc.getAmbientHorizonShort(),
+ ddc.getAmbientHorizonLong(), lightSensorWarmUpTimeConfig,
+ ddc.getAmbientHorizonLong(),
+ HysteresisLevels.getAmbientBrightnessThresholds(ddc),
+ HysteresisLevels.getAmbientBrightnessThresholdsIdle(ddc),
+ ddc.getAutoBrightnessBrighteningLightDebounce(),
+ ddc.getAutoBrightnessDarkeningLightDebounce(),
+ ddc.getAutoBrightnessBrighteningLightDebounceIdle(),
+ ddc.getAutoBrightnessDarkeningLightDebounceIdle(),
+ ddc.getAmbientLightSensor()
+ );
+ }
+ }
+}
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 54de64e..dd87572 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -22,9 +22,7 @@
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
@@ -38,9 +36,6 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.PowerManager;
@@ -52,6 +47,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.Clock;
+import com.android.server.display.brightness.LightSensorController;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.testutils.OffsettableClock;
@@ -61,7 +58,6 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@@ -69,31 +65,18 @@
public class AutomaticBrightnessControllerTest {
private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
- private static final int LIGHT_SENSOR_RATE = 20;
private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
- private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 2000;
- private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 4000;
- private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 1000;
- private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 2000;
private static final float DOZE_SCALE_FACTOR = 0.54f;
- private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
- private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
- private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000;
- private static final int AMBIENT_LIGHT_HORIZON_LONG = 2000;
private static final float EPSILON = 0.001f;
private OffsettableClock mClock = new OffsettableClock();
private TestLooper mTestLooper;
private Context mContext;
private AutomaticBrightnessController mController;
- private Sensor mLightSensor;
- @Mock SensorManager mSensorManager;
@Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
@Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
@Mock BrightnessMappingStrategy mDozeBrightnessMappingStrategy;
- @Mock HysteresisLevels mAmbientBrightnessThresholds;
@Mock HysteresisLevels mScreenBrightnessThresholds;
- @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
@Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
@Mock Handler mNoOpHandler;
@Mock BrightnessRangeController mBrightnessRangeController;
@@ -101,17 +84,18 @@
BrightnessClamperController mBrightnessClamperController;
@Mock BrightnessThrottler mBrightnessThrottler;
+ @Mock
+ LightSensorController mLightSensorController;
+
@Before
public void setUp() throws Exception {
// Share classloader to allow package private access.
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
- mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
mContext = InstrumentationRegistry.getContext();
setupController(BrightnessMappingStrategy.INVALID_LUX,
- BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ false,
- /* useHorizon= */ true);
+ BrightnessMappingStrategy.INVALID_NITS);
}
@After
@@ -123,8 +107,7 @@
}
}
- private void setupController(float userLux, float userNits, boolean applyDebounce,
- boolean useHorizon) {
+ private void setupController(float userLux, float userNits) {
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
@@ -147,25 +130,22 @@
}
@Override
- AutomaticBrightnessController.Clock createClock() {
- return mClock::now;
+ Clock createClock() {
+ return new Clock() {
+ @Override
+ public long uptimeMillis() {
+ return mClock.now();
+ }
+ };
}
}, // pass in test looper instead, pass in offsettable clock
- () -> { }, mTestLooper.getLooper(), mSensorManager, mLightSensor,
- brightnessMappingStrategyMap, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
- BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
- INITIAL_LIGHT_SENSOR_RATE, applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG : 0,
- applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG : 0,
- applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
- applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
- RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
- mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
- mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
+ () -> { }, mTestLooper.getLooper(),
+ brightnessMappingStrategyMap, BRIGHTNESS_MIN_FLOAT,
+ BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, mScreenBrightnessThresholds,
+ mScreenBrightnessThresholdsIdle,
mContext, mBrightnessRangeController, mBrightnessThrottler,
- useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
- useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
- mBrightnessClamperController
+ userLux, userNits, mLightSensorController, mBrightnessClamperController
);
when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
@@ -186,20 +166,15 @@
@Test
public void testNoHysteresisAtMinBrightness() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Set up system to return 0.02f as a brightness value
float lux1 = 100.0f;
// Brightness as float (from 0.0f to 1.0f)
float normalizedBrightness1 = 0.02f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
- .thenReturn(lux1);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
- .thenReturn(lux1);
when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
.thenReturn(normalizedBrightness1);
@@ -210,39 +185,31 @@
.thenReturn(1.0f);
// Send new sensor value and verify
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1));
+ listener.onAmbientLuxChange(lux1);
assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON);
// Set up system to return 0.0f (minimum possible brightness) as a brightness value
float lux2 = 10.0f;
float normalizedBrightness2 = 0.0f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
- .thenReturn(lux2);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
- .thenReturn(lux2);
when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
.thenReturn(normalizedBrightness2);
// Send new sensor value and verify
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2));
+ listener.onAmbientLuxChange(lux2);
assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON);
}
@Test
public void testNoHysteresisAtMaxBrightness() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Set up system to return 0.98f as a brightness value
float lux1 = 100.0f;
float normalizedBrightness1 = 0.98f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
- .thenReturn(lux1);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
- .thenReturn(lux1);
+
when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
.thenReturn(normalizedBrightness1);
@@ -253,35 +220,30 @@
.thenReturn(1.1f);
// Send new sensor value and verify
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1));
+ listener.onAmbientLuxChange(lux1);
assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON);
// Set up system to return 1.0f as a brightness value (brightness_max)
float lux2 = 110.0f;
float normalizedBrightness2 = 1.0f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
- .thenReturn(lux2);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
- .thenReturn(lux2);
when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
.thenReturn(normalizedBrightness2);
// Send new sensor value and verify
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2));
+ listener.onAmbientLuxChange(lux2);
assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON);
}
@Test
public void testUserAddUserDataPoint() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+ listener.onAmbientLuxChange(1000);
// User sets brightness to 100
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
@@ -298,12 +260,11 @@
public void testRecalculateSplines() throws Exception {
// Enabling the light sensor, and setting the ambient lux to 1000
int currentLux = 1000;
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, currentLux));
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
+ listener.onAmbientLuxChange(currentLux);
// User sets brightness to 0.5f
when(mBrightnessMappingStrategy.getBrightness(currentLux,
@@ -333,14 +294,13 @@
@Test
public void testShortTermModelTimesOut() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Sensor reads 123 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+ listener.onAmbientLuxChange(123);
// User sets brightness to 100
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
@@ -354,7 +314,7 @@
123f, 0.5f)).thenReturn(true);
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+ listener.onAmbientLuxChange(1000);
mTestLooper.moveTimeForward(
mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
mTestLooper.dispatchAll();
@@ -373,14 +333,13 @@
@Test
public void testShortTermModelDoesntTimeOut() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Sensor reads 123 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+ listener.onAmbientLuxChange(123);
// User sets brightness to 100
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
@@ -399,7 +358,7 @@
mTestLooper.dispatchAll();
// Sensor reads 100000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910));
+ listener.onAmbientLuxChange(678910);
mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
// Verify short term model is not reset.
@@ -413,14 +372,13 @@
@Test
public void testShortTermModelIsRestoredWhenSwitchingWithinTimeout() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Sensor reads 123 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+ listener.onAmbientLuxChange(123);
// User sets brightness to 100
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
@@ -440,7 +398,7 @@
123f, 0.5f)).thenReturn(true);
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+ listener.onAmbientLuxChange(1000);
mTestLooper.moveTimeForward(
mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
mTestLooper.dispatchAll();
@@ -459,14 +417,13 @@
@Test
public void testShortTermModelNotRestoredAfterTimeout() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Sensor reads 123 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+ listener.onAmbientLuxChange(123);
// User sets brightness to 100
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
/* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
@@ -488,7 +445,7 @@
123f, 0.5f)).thenReturn(true);
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+ listener.onAmbientLuxChange(1000);
// Do not fast-forward time.
mTestLooper.dispatchAll();
@@ -506,14 +463,13 @@
@Test
public void testSwitchBetweenModesNoUserInteractions() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Sensor reads 123 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
+ listener.onAmbientLuxChange(123);
when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
@@ -529,7 +485,7 @@
BrightnessMappingStrategy.INVALID_LUX);
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+ listener.onAmbientLuxChange(1000);
// Do not fast-forward time.
mTestLooper.dispatchAll();
@@ -545,14 +501,19 @@
@Test
public void testSwitchToIdleMappingStrategy() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
+ clearInvocations(mBrightnessMappingStrategy);
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
+ listener.onAmbientLuxChange(1000);
+
+
+ verify(mBrightnessMappingStrategy).getBrightness(anyFloat(), any(), anyInt());
+
+ clearInvocations(mBrightnessMappingStrategy);
// User sets brightness to 100
mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
@@ -561,22 +522,19 @@
/* shouldResetShortTermModel= */ true);
// There should be a user data point added to the mapper.
- verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
+ verify(mBrightnessMappingStrategy).addUserDataPoint(/* lux= */ 1000f,
/* brightness= */ 0.5f);
- verify(mBrightnessMappingStrategy, times(2)).setBrightnessConfiguration(any());
- verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt());
+ verify(mBrightnessMappingStrategy).setBrightnessConfiguration(any());
+ verify(mBrightnessMappingStrategy).getBrightness(anyFloat(), any(), anyInt());
+ clearInvocations(mBrightnessMappingStrategy);
// Now let's do the same for idle mode
mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
- // Called once when switching,
- // setAmbientLux() is called twice and once in updateAutoBrightness(),
- // nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
- // called twice each.
- verify(mBrightnessMappingStrategy, times(8)).getMode();
- // Called when switching.
- verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
- verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
- verify(mBrightnessMappingStrategy, times(1)).getUserLux();
+
+ verify(mBrightnessMappingStrategy).getMode();
+ verify(mBrightnessMappingStrategy).getShortTermModelTimeout();
+ verify(mBrightnessMappingStrategy).getUserBrightness();
+ verify(mBrightnessMappingStrategy).getUserLux();
// Ensure, after switching, original BMS is not used anymore
verifyNoMoreInteractions(mBrightnessMappingStrategy);
@@ -588,154 +546,25 @@
/* shouldResetShortTermModel= */ true);
// Ensure we use the correct mapping strategy
- verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
+ verify(mIdleBrightnessMappingStrategy).addUserDataPoint(/* lux= */ 1000f,
/* brightness= */ 0.5f);
}
@Test
- public void testAmbientLightHorizon() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- long increment = 500;
- // set autobrightness to low
- // t = 0
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
-
- // t = 500
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
-
- // t = 1000
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
- assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
-
- // t = 1500
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
- assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
-
- // t = 2000
- // ensure that our reading is at 0.
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
- assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
-
- // t = 2500
- // first 10000 lux sensor event reading
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
- assertTrue(mController.getAmbientLux() > 0.0f);
- assertTrue(mController.getAmbientLux() < 10000.0f);
-
- // t = 3000
- // lux reading should still not yet be 10000.
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
- assertTrue(mController.getAmbientLux() > 0.0f);
- assertTrue(mController.getAmbientLux() < 10000.0f);
-
- // t = 3500
- mClock.fastForward(increment);
- // lux has been high (10000) for 1000ms.
- // lux reading should be 10000
- // short horizon (ambient lux) is high, long horizon is still not high
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
- assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
-
- // t = 4000
- // stay high
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
- assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
-
- // t = 4500
- Mockito.clearInvocations(mBrightnessMappingStrategy);
- mClock.fastForward(increment);
- // short horizon is high, long horizon is high too
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
- verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1);
- assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
-
- // t = 5000
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
- assertTrue(mController.getAmbientLux() > 0.0f);
- assertTrue(mController.getAmbientLux() < 10000.0f);
-
- // t = 5500
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
- assertTrue(mController.getAmbientLux() > 0.0f);
- assertTrue(mController.getAmbientLux() < 10000.0f);
-
- // t = 6000
- mClock.fastForward(increment);
- // ambient lux goes to 0
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
- assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
-
- // only the values within the horizon should be kept
- assertArrayEquals(new float[] {10000, 10000, 0, 0, 0}, mController.getLastSensorValues(),
- EPSILON);
- assertArrayEquals(new long[] {4000, 4500, 5000, 5500, 6000},
- mController.getLastSensorTimestamps());
- }
-
- @Test
- public void testHysteresisLevels() {
- float[] ambientBrighteningThresholds = {50, 100};
- float[] ambientDarkeningThresholds = {10, 20};
- float[] ambientThresholdLevels = {0, 500};
- float ambientDarkeningMinChangeThreshold = 3.0f;
- float ambientBrighteningMinChangeThreshold = 1.5f;
- HysteresisLevels hysteresisLevels = new HysteresisLevels(ambientBrighteningThresholds,
- ambientDarkeningThresholds, ambientThresholdLevels, ambientThresholdLevels,
- ambientDarkeningMinChangeThreshold, ambientBrighteningMinChangeThreshold);
-
- // test low, activate minimum change thresholds.
- assertEquals(1.5f, hysteresisLevels.getBrighteningThreshold(0.0f), EPSILON);
- assertEquals(0f, hysteresisLevels.getDarkeningThreshold(0.0f), EPSILON);
- assertEquals(1f, hysteresisLevels.getDarkeningThreshold(4.0f), EPSILON);
-
- // test max
- // epsilon is x2 here, since the next floating point value about 20,000 is 0.0019531 greater
- assertEquals(20000f, hysteresisLevels.getBrighteningThreshold(10000.0f), EPSILON * 2);
- assertEquals(8000f, hysteresisLevels.getDarkeningThreshold(10000.0f), EPSILON);
-
- // test just below threshold
- assertEquals(748.5f, hysteresisLevels.getBrighteningThreshold(499f), EPSILON);
- assertEquals(449.1f, hysteresisLevels.getDarkeningThreshold(499f), EPSILON);
-
- // test at (considered above) threshold
- assertEquals(1000f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON);
- assertEquals(400f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON);
- }
-
- @Test
public void testBrightnessGetsThrottled() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Set up system to return max brightness at 100 lux
final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT;
final float lux = 100.0f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux))
- .thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux))
- .thenReturn(lux);
when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
.thenReturn(normalizedBrightness);
// Sensor reads 100 lux. We should get max brightness.
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+ listener.onAmbientLuxChange(lux);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
@@ -763,94 +592,6 @@
}
@Test
- public void testGetSensorReadings() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
- int increment = 11;
- int lux = 5000;
- for (int i = 0; i < 1000; i++) {
- lux += increment;
- mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
- }
-
- int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment + 1);
- float[] sensorValues = mController.getLastSensorValues();
- long[] sensorTimestamps = mController.getLastSensorTimestamps();
-
- // Only the values within the horizon should be kept
- assertEquals(valuesCount, sensorValues.length);
- assertEquals(valuesCount, sensorTimestamps.length);
-
- long sensorTimestamp = mClock.now();
- for (int i = valuesCount - 1; i >= 1; i--) {
- assertEquals(lux, sensorValues[i], EPSILON);
- assertEquals(sensorTimestamp, sensorTimestamps[i]);
- lux -= increment;
- sensorTimestamp -= increment;
- }
- assertEquals(lux, sensorValues[0], EPSILON);
- assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
- }
-
- @Test
- public void testGetSensorReadingsFullBuffer() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
- int initialCapacity = 150;
-
- // Choose values such that the ring buffer is pruned
- int increment1 = 200;
- int lux = 5000;
- for (int i = 0; i < 20; i++) {
- lux += increment1;
- mClock.fastForward(increment1);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
- }
-
- int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment1 + 1);
-
- // Choose values such that the buffer becomes full
- int increment2 = 1;
- for (int i = 0; i < initialCapacity - valuesCount; i++) {
- lux += increment2;
- mClock.fastForward(increment2);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
- }
-
- float[] sensorValues = mController.getLastSensorValues();
- long[] sensorTimestamps = mController.getLastSensorTimestamps();
-
- // The buffer should be full
- assertEquals(initialCapacity, sensorValues.length);
- assertEquals(initialCapacity, sensorTimestamps.length);
-
- long sensorTimestamp = mClock.now();
- for (int i = initialCapacity - 1; i >= 1; i--) {
- assertEquals(lux, sensorValues[i], EPSILON);
- assertEquals(sensorTimestamp, sensorTimestamps[i]);
-
- if (i >= valuesCount) {
- lux -= increment2;
- sensorTimestamp -= increment2;
- } else {
- lux -= increment1;
- sensorTimestamp -= increment1;
- }
- }
- assertEquals(lux, sensorValues[0], EPSILON);
- assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
- }
-
- @Test
public void testResetShortTermModelWhenConfigChanges() {
when(mBrightnessMappingStrategy.setBrightnessConfiguration(any())).thenReturn(true);
@@ -875,179 +616,22 @@
float userNits = 500;
float userBrightness = 0.3f;
when(mBrightnessMappingStrategy.getBrightnessFromNits(userNits)).thenReturn(userBrightness);
- setupController(userLux, userNits, /* applyDebounce= */ true,
- /* useHorizon= */ false);
+ setupController(userLux, userNits);
verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness);
}
@Test
- public void testBrighteningLightDebounce() throws Exception {
- clearInvocations(mSensorManager);
- setupController(BrightnessMappingStrategy.INVALID_LUX,
- BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
- /* useHorizon= */ false);
-
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // t = 0
- // Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
-
- // t = 1000
- // Lux isn't steady yet
- mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
-
- // t = 1500
- // Lux isn't steady yet
- mClock.fastForward(500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
-
- // t = 2500
- // Lux is steady now
- mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
- }
-
- @Test
- public void testDarkeningLightDebounce() throws Exception {
- clearInvocations(mSensorManager);
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(anyFloat()))
- .thenReturn(10000f);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(anyFloat()))
- .thenReturn(10000f);
- setupController(BrightnessMappingStrategy.INVALID_LUX,
- BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
- /* useHorizon= */ false);
-
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // t = 0
- // Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
-
- // t = 2000
- // Lux isn't steady yet
- mClock.fastForward(2000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
-
- // t = 2500
- // Lux isn't steady yet
- mClock.fastForward(500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
-
- // t = 4500
- // Lux is steady now
- mClock.fastForward(2000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
- }
-
- @Test
- public void testBrighteningLightDebounceIdle() throws Exception {
- clearInvocations(mSensorManager);
- setupController(BrightnessMappingStrategy.INVALID_LUX,
- BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
- /* useHorizon= */ false);
-
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
-
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // t = 0
- // Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
-
- // t = 500
- // Lux isn't steady yet
- mClock.fastForward(500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
-
- // t = 1500
- // Lux is steady now
- mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
- }
-
- @Test
- public void testDarkeningLightDebounceIdle() throws Exception {
- clearInvocations(mSensorManager);
- when(mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(anyFloat()))
- .thenReturn(10000f);
- when(mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(anyFloat()))
- .thenReturn(10000f);
- setupController(BrightnessMappingStrategy.INVALID_LUX,
- BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
- /* useHorizon= */ false);
-
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
-
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // t = 0
- // Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
-
- // t = 1000
- // Lux isn't steady yet
- mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(1200, mController.getAmbientLux(), EPSILON);
-
- // t = 2500
- // Lux is steady now
- mClock.fastForward(1500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
- assertEquals(500, mController.getAmbientLux(), EPSILON);
- }
-
- @Test
public void testBrightnessBasedOnLastObservedLux() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
// Set up system to return 0.3f as a brightness value
float lux = 100.0f;
// Brightness as float (from 0.0f to 1.0f)
float normalizedBrightness = 0.3f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mLightSensorController.getLastObservedLux()).thenReturn(lux);
when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
/* category= */ anyInt())).thenReturn(normalizedBrightness);
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
// Send a new sensor value, disable the sensor and verify
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
mController.configure(AUTO_BRIGHTNESS_DISABLED, /* configuration= */ null,
/* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
/* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
@@ -1059,21 +643,19 @@
@Test
public void testAutoBrightnessInDoze() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Set up system to return 0.3f as a brightness value
float lux = 100.0f;
// Brightness as float (from 0.0f to 1.0f)
float normalizedBrightness = 0.3f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
/* category= */ anyInt())).thenReturn(normalizedBrightness);
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mLightSensorController.getLastObservedLux()).thenReturn(lux);
// Set policy to DOZE
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
@@ -1082,7 +664,7 @@
/* shouldResetShortTermModel= */ true);
// Send a new sensor value
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+ listener.onAmbientLuxChange(lux);
// The brightness should be scaled by the doze factor
assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
@@ -1095,21 +677,19 @@
@Test
public void testAutoBrightnessInDoze_ShouldNotScaleIfUsingDozeCurve() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Set up system to return 0.3f as a brightness value
float lux = 100.0f;
// Brightness as float (from 0.0f to 1.0f)
float normalizedBrightness = 0.3f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
/* category= */ anyInt())).thenReturn(normalizedBrightness);
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mLightSensorController.getLastObservedLux()).thenReturn(lux);
// Switch mode to DOZE
mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
@@ -1121,7 +701,7 @@
/* shouldResetShortTermModel= */ true);
// Send a new sensor value
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+ listener.onAmbientLuxChange(lux);
// The brightness should not be scaled by the doze factor
assertEquals(normalizedBrightness,
@@ -1133,21 +713,19 @@
@Test
public void testAutoBrightnessInDoze_ShouldNotScaleIfScreenOn() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ ArgumentCaptor<LightSensorController.LightSensorListener> listenerCaptor =
+ ArgumentCaptor.forClass(LightSensorController.LightSensorListener.class);
+ verify(mLightSensorController).setListener(listenerCaptor.capture());
+ LightSensorController.LightSensorListener listener = listenerCaptor.getValue();
// Set up system to return 0.3f as a brightness value
float lux = 100.0f;
// Brightness as float (from 0.0f to 1.0f)
float normalizedBrightness = 0.3f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
/* category= */ anyInt())).thenReturn(normalizedBrightness);
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mLightSensorController.getLastObservedLux()).thenReturn(lux);
// Set policy to DOZE
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
@@ -1156,7 +734,7 @@
/* shouldResetShortTermModel= */ true);
// Send a new sensor value
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+ listener.onAmbientLuxChange(lux);
// The brightness should not be scaled by the doze factor
assertEquals(normalizedBrightness,
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 740ffc9..a4d1f5c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -28,7 +28,6 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
@@ -79,6 +78,8 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.LightSensorController;
+import com.android.server.display.brightness.TestUtilsKt;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
@@ -1161,30 +1162,19 @@
any(AutomaticBrightnessController.Callbacks.class),
any(Looper.class),
eq(mSensorManagerMock),
- /* lightSensor= */ any(),
/* brightnessMappingStrategyMap= */ any(SparseArray.class),
- /* lightSensorWarmUpTime= */ anyInt(),
/* brightnessMin= */ anyFloat(),
/* brightnessMax= */ anyFloat(),
/* dozeScaleFactor */ anyFloat(),
- /* lightSensorRate= */ anyInt(),
- /* initialLightSensorRate= */ anyInt(),
- /* brighteningLightDebounceConfig */ anyLong(),
- /* darkeningLightDebounceConfig */ anyLong(),
- /* brighteningLightDebounceConfigIdle= */ anyLong(),
- /* darkeningLightDebounceConfigIdle= */ anyLong(),
- /* resetAmbientLuxAfterWarmUpConfig= */ anyBoolean(),
- any(HysteresisLevels.class),
- any(HysteresisLevels.class),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
eq(mContext),
any(BrightnessRangeController.class),
any(BrightnessThrottler.class),
- /* ambientLightHorizonShort= */ anyInt(),
- /* ambientLightHorizonLong= */ anyInt(),
eq(lux),
eq(nits),
+ eq(DISPLAY_ID),
+ any(LightSensorController.LightSensorControllerConfig.class),
any(BrightnessClamperController.class)
);
}
@@ -2107,22 +2097,22 @@
}
@Override
+ LightSensorController.LightSensorControllerConfig getLightSensorControllerConfig(
+ Context context, DisplayDeviceConfig displayDeviceConfig) {
+ return TestUtilsKt.createLightSensorControllerConfig();
+ }
+
+ @Override
AutomaticBrightnessController getAutomaticBrightnessController(
AutomaticBrightnessController.Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Sensor lightSensor,
+ SensorManager sensorManager,
SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
- int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
- float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
- long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
- long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
- boolean resetAmbientLuxAfterWarmUpConfig,
- HysteresisLevels ambientBrightnessThresholds,
+ float brightnessMin, float brightnessMax, float dozeScaleFactor,
HysteresisLevels screenBrightnessThresholds,
- HysteresisLevels ambientBrightnessThresholdsIdle,
HysteresisLevels screenBrightnessThresholdsIdle, Context context,
BrightnessRangeController brightnessRangeController,
- BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
- int ambientLightHorizonLong, float userLux, float userNits,
+ BrightnessThrottler brightnessThrottler, float userLux, float userNits,
+ int displayId, LightSensorController.LightSensorControllerConfig config,
BrightnessClamperController brightnessClamperController) {
return mAutomaticBrightnessController;
}
@@ -2135,18 +2125,12 @@
}
@Override
- HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
- float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
- float[] darkeningThresholdLevels, float minDarkeningThreshold,
- float minBrighteningThreshold) {
+ HysteresisLevels getBrightnessThresholdsIdleHysteresisLevels(DisplayDeviceConfig ddc) {
return mHysteresisLevels;
}
@Override
- HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages,
- float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels,
- float[] darkeningThresholdLevels, float minDarkeningThreshold,
- float minBrighteningThreshold, boolean potentialOldBrightnessRange) {
+ HysteresisLevels getBrightnessThresholdsHysteresisLevels(DisplayDeviceConfig ddc) {
return mHysteresisLevels;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/HysteresisLevelsTest.kt b/services/tests/displayservicetests/src/com/android/server/display/HysteresisLevelsTest.kt
new file mode 100644
index 0000000..02d6946
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/HysteresisLevelsTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 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 androidx.test.filters.SmallTest
+import com.android.server.display.brightness.createHysteresisLevels
+import kotlin.test.assertEquals
+import org.junit.Test
+
+private const val FLOAT_TOLERANCE = 0.001f
+@SmallTest
+class HysteresisLevelsTest {
+ @Test
+ fun `test hysteresis levels`() {
+ val hysteresisLevels = createHysteresisLevels(
+ brighteningThresholdsPercentages = floatArrayOf(50f, 100f),
+ darkeningThresholdsPercentages = floatArrayOf(10f, 20f),
+ brighteningThresholdLevels = floatArrayOf(0f, 500f),
+ darkeningThresholdLevels = floatArrayOf(0f, 500f),
+ minDarkeningThreshold = 3f,
+ minBrighteningThreshold = 1.5f
+ )
+
+ // test low, activate minimum change thresholds.
+ assertEquals(1.5f, hysteresisLevels.getBrighteningThreshold(0.0f), FLOAT_TOLERANCE)
+ assertEquals(0f, hysteresisLevels.getDarkeningThreshold(0.0f), FLOAT_TOLERANCE)
+ assertEquals(1f, hysteresisLevels.getDarkeningThreshold(4.0f), FLOAT_TOLERANCE)
+
+ // test max
+ // epsilon is x2 here, since the next floating point value about 20,000 is 0.0019531 greater
+ assertEquals(
+ 20000f, hysteresisLevels.getBrighteningThreshold(10000.0f), FLOAT_TOLERANCE * 2)
+ assertEquals(8000f, hysteresisLevels.getDarkeningThreshold(10000.0f), FLOAT_TOLERANCE)
+
+ // test just below threshold
+ assertEquals(748.5f, hysteresisLevels.getBrighteningThreshold(499f), FLOAT_TOLERANCE)
+ assertEquals(449.1f, hysteresisLevels.getDarkeningThreshold(499f), FLOAT_TOLERANCE)
+
+ // test at (considered above) threshold
+ assertEquals(1000f, hysteresisLevels.getBrighteningThreshold(500f), FLOAT_TOLERANCE)
+ assertEquals(400f, hysteresisLevels.getDarkeningThreshold(500f), FLOAT_TOLERANCE)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/AmbientLightRingBufferTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/AmbientLightRingBufferTest.kt
new file mode 100644
index 0000000..5fe9178
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/AmbientLightRingBufferTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 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.brightness
+
+import androidx.test.filters.SmallTest
+import com.android.internal.os.Clock
+import com.android.server.display.brightness.LightSensorController.AmbientLightRingBuffer
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.mockito.kotlin.mock
+
+
+private const val BUFFER_INITIAL_CAPACITY = 3
+
+@SmallTest
+class AmbientLightRingBufferTest {
+
+ private val buffer = AmbientLightRingBuffer(BUFFER_INITIAL_CAPACITY, mock<Clock>())
+
+ @Test
+ fun `test created empty`() {
+ assertThat(buffer.size()).isEqualTo(0)
+ }
+
+ @Test
+ fun `test push to empty buffer`() {
+ buffer.push(1000, 0.5f)
+
+ assertThat(buffer.size()).isEqualTo(1)
+ assertThat(buffer.getLux(0)).isEqualTo(0.5f)
+ assertThat(buffer.getTime(0)).isEqualTo(1000)
+ }
+
+ @Test
+ fun `test prune keeps youngest outside horizon and sets time to horizon`() {
+ buffer.push(1000, 0.5f)
+ buffer.push(2000, 0.6f)
+ buffer.push(3000, 0.7f)
+
+ buffer.prune(2500)
+
+ assertThat(buffer.size()).isEqualTo(2)
+
+ assertThat(buffer.getLux(0)).isEqualTo(0.6f)
+ assertThat(buffer.getTime(0)).isEqualTo(2500)
+ }
+
+ @Test
+ fun `test prune keeps inside horizon`() {
+ buffer.push(1000, 0.5f)
+ buffer.push(2000, 0.6f)
+ buffer.push(3000, 0.7f)
+
+ buffer.prune(2500)
+
+ assertThat(buffer.size()).isEqualTo(2)
+
+ assertThat(buffer.getLux(1)).isEqualTo(0.7f)
+ assertThat(buffer.getTime(1)).isEqualTo(3000)
+ }
+
+
+ @Test
+ fun `test pushes correctly after prune`() {
+ buffer.push(1000, 0.5f)
+ buffer.push(2000, 0.6f)
+ buffer.push(3000, 0.7f)
+ buffer.prune(2500)
+
+ buffer.push(4000, 0.8f)
+
+ assertThat(buffer.size()).isEqualTo(3)
+
+ assertThat(buffer.getLux(0)).isEqualTo(0.6f)
+ assertThat(buffer.getTime(0)).isEqualTo(2500)
+ assertThat(buffer.getLux(1)).isEqualTo(0.7f)
+ assertThat(buffer.getTime(1)).isEqualTo(3000)
+ assertThat(buffer.getLux(2)).isEqualTo(0.8f)
+ assertThat(buffer.getTime(2)).isEqualTo(4000)
+ }
+
+ @Test
+ fun `test increase buffer size`() {
+ buffer.push(1000, 0.5f)
+ buffer.push(2000, 0.6f)
+ buffer.push(3000, 0.7f)
+
+ buffer.push(4000, 0.8f)
+
+ assertThat(buffer.size()).isEqualTo(4)
+
+ assertThat(buffer.getLux(0)).isEqualTo(0.5f)
+ assertThat(buffer.getTime(0)).isEqualTo(1000)
+ assertThat(buffer.getLux(1)).isEqualTo(0.6f)
+ assertThat(buffer.getTime(1)).isEqualTo(2000)
+ assertThat(buffer.getLux(2)).isEqualTo(0.7f)
+ assertThat(buffer.getTime(2)).isEqualTo(3000)
+ assertThat(buffer.getLux(3)).isEqualTo(0.8f)
+ assertThat(buffer.getTime(3)).isEqualTo(4000)
+ }
+
+ @Test
+ fun `test buffer clear`() {
+ buffer.push(1000, 0.5f)
+ buffer.push(2000, 0.6f)
+ buffer.push(3000, 0.7f)
+
+ buffer.clear()
+
+ assertThat(buffer.size()).isEqualTo(0)
+ assertThrows(ArrayIndexOutOfBoundsException::class.java) {
+ buffer.getLux(0)
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/LightSensorControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/LightSensorControllerTest.kt
new file mode 100644
index 0000000..966134a
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/LightSensorControllerTest.kt
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2024 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.brightness
+
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.os.Handler
+import androidx.test.filters.SmallTest
+import com.android.internal.os.Clock
+import com.android.server.display.TestUtils
+import com.android.server.display.brightness.LightSensorController.Injector
+import com.android.server.display.brightness.LightSensorController.LightSensorControllerConfig
+import com.android.server.testutils.OffsettableClock
+import com.android.server.testutils.TestHandler
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.ceil
+import kotlin.test.assertEquals
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+private const val FLOAT_TOLERANCE = 0.001f
+
+@SmallTest
+class LightSensorControllerTest {
+
+ private val testHandler = TestHandler(null)
+ private val testInjector = TestInjector()
+
+ @Test
+ fun `test ambient light horizon`() {
+ val lightSensorController = LightSensorController(
+ createLightSensorControllerConfig(
+ lightSensorWarmUpTimeConfig = 0, // no warmUp time, can use first event
+ brighteningLightDebounceConfig = 0,
+ darkeningLightDebounceConfig = 0,
+ ambientLightHorizonShort = 1000,
+ ambientLightHorizonLong = 2000
+ ), testInjector, testHandler)
+
+ var reportedAmbientLux = 0f
+ lightSensorController.setListener { lux ->
+ reportedAmbientLux = lux
+ }
+ lightSensorController.enableLightSensorIfNeeded()
+
+ assertThat(testInjector.sensorEventListener).isNotNull()
+
+ val timeIncrement = 500L
+ // set ambient lux to low
+ // t = 0
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+
+ // t = 500
+ //
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+
+ // t = 1000
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertEquals(0f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t = 1500
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertEquals(0f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t = 2000
+ // ensure that our reading is at 0.
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertEquals(0f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t = 2500
+ // first 10000 lux sensor event reading
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(10_000f))
+ assertTrue(reportedAmbientLux > 0f)
+ assertTrue(reportedAmbientLux < 10_000f)
+
+ // t = 3000
+ // lux reading should still not yet be 10000.
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(10_000f))
+ assertTrue(reportedAmbientLux > 0)
+ assertTrue(reportedAmbientLux < 10_000f)
+
+ // t = 3500
+ testInjector.clock.fastForward(timeIncrement)
+ // at short horizon, first value outside will be used in calculation (t = 2000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(10_000f))
+ assertTrue(reportedAmbientLux > 0f)
+ assertTrue(reportedAmbientLux < 10_000f)
+
+ // t = 4000
+ // lux has been high (10000) for more than 1000ms.
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(10_000f))
+ assertEquals(10_000f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t = 4500
+ testInjector.clock.fastForward(timeIncrement)
+ // short horizon is high, long horizon is high too
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(10_000f))
+ assertEquals(10_000f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t = 5000
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertTrue(reportedAmbientLux > 0f)
+ assertTrue(reportedAmbientLux < 10_000f)
+
+ // t = 5500
+ testInjector.clock.fastForward(timeIncrement)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertTrue(reportedAmbientLux > 0f)
+ assertTrue(reportedAmbientLux < 10_000f)
+
+ // t = 6000
+ testInjector.clock.fastForward(timeIncrement)
+ // at short horizon, first value outside will be used in calculation (t = 4500)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertTrue(reportedAmbientLux > 0f)
+ assertTrue(reportedAmbientLux < 10_000f)
+
+ // t = 6500
+ testInjector.clock.fastForward(timeIncrement)
+ // ambient lux goes to 0
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(0f))
+ assertEquals(0f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // only the values within the horizon should be kept
+ assertArrayEquals(floatArrayOf(10_000f, 0f, 0f, 0f, 0f),
+ lightSensorController.lastSensorValues, FLOAT_TOLERANCE)
+ assertArrayEquals(longArrayOf(4_500, 5_000, 5_500, 6_000, 6_500),
+ lightSensorController.lastSensorTimestamps)
+ }
+
+ @Test
+ fun `test brightening debounce`() {
+ val lightSensorController = LightSensorController(
+ createLightSensorControllerConfig(
+ lightSensorWarmUpTimeConfig = 0, // no warmUp time, can use first event
+ brighteningLightDebounceConfig = 1500,
+ ambientLightHorizonShort = 0, // only last value will be used for lux calculation
+ ambientLightHorizonLong = 10_000,
+ // brightening threshold is set to previous lux value
+ ambientBrightnessThresholds = createHysteresisLevels(
+ brighteningThresholdLevels = floatArrayOf(),
+ brighteningThresholdsPercentages = floatArrayOf(),
+ minBrighteningThreshold = 0f
+ )
+ ), testInjector, testHandler)
+ lightSensorController.setIdleMode(false)
+
+ var reportedAmbientLux = 0f
+ lightSensorController.setListener { lux ->
+ reportedAmbientLux = lux
+ }
+ lightSensorController.enableLightSensorIfNeeded()
+
+ assertThat(testInjector.sensorEventListener).isNotNull()
+
+ // t0 (0)
+ // Initial lux, initial brightening threshold
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(1200f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t1 (1000)
+ // Lux increase, first brightening event
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(1800f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t2 (2000) (t2 - t1 < brighteningLightDebounceConfig)
+ // Lux increase, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(2000f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t3 (3000) (t3 - t1 < brighteningLightDebounceConfig)
+ // Lux increase, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(2200f))
+ assertEquals(2200f, reportedAmbientLux, FLOAT_TOLERANCE)
+ }
+
+ @Test
+ fun `test sensor readings`() {
+ val ambientLightHorizonLong = 2_500
+ val lightSensorController = LightSensorController(
+ createLightSensorControllerConfig(
+ ambientLightHorizonLong = ambientLightHorizonLong
+ ), testInjector, testHandler)
+ lightSensorController.setListener { }
+ lightSensorController.setIdleMode(false)
+ lightSensorController.enableLightSensorIfNeeded()
+
+ // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
+ val increment = 11
+ var lux = 5000
+ for (i in 0 until 1000) {
+ lux += increment
+ testInjector.clock.fastForward(increment.toLong())
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(lux.toFloat()))
+ }
+
+ val valuesCount = ceil(ambientLightHorizonLong.toDouble() / increment + 1).toInt()
+ val sensorValues = lightSensorController.lastSensorValues
+ val sensorTimestamps = lightSensorController.lastSensorTimestamps
+
+ // Only the values within the horizon should be kept
+ assertEquals(valuesCount, sensorValues.size)
+ assertEquals(valuesCount, sensorTimestamps.size)
+
+ var sensorTimestamp = testInjector.clock.now()
+ for (i in valuesCount - 1 downTo 1) {
+ assertEquals(lux.toFloat(), sensorValues[i], FLOAT_TOLERANCE)
+ assertEquals(sensorTimestamp, sensorTimestamps[i])
+ lux -= increment
+ sensorTimestamp -= increment
+ }
+ assertEquals(lux.toFloat(), sensorValues[0], FLOAT_TOLERANCE)
+ assertEquals(testInjector.clock.now() - ambientLightHorizonLong, sensorTimestamps[0])
+ }
+
+ @Test
+ fun `test darkening debounce`() {
+ val lightSensorController = LightSensorController(
+ createLightSensorControllerConfig(
+ lightSensorWarmUpTimeConfig = 0, // no warmUp time, can use first event
+ darkeningLightDebounceConfig = 1500,
+ ambientLightHorizonShort = 0, // only last value will be used for lux calculation
+ ambientLightHorizonLong = 10_000,
+ // darkening threshold is set to previous lux value
+ ambientBrightnessThresholds = createHysteresisLevels(
+ darkeningThresholdLevels = floatArrayOf(),
+ darkeningThresholdsPercentages = floatArrayOf(),
+ minDarkeningThreshold = 0f
+ )
+ ), testInjector, testHandler)
+
+ lightSensorController.setIdleMode(false)
+
+ var reportedAmbientLux = 0f
+ lightSensorController.setListener { lux ->
+ reportedAmbientLux = lux
+ }
+ lightSensorController.enableLightSensorIfNeeded()
+
+ assertThat(testInjector.sensorEventListener).isNotNull()
+
+ // t0 (0)
+ // Initial lux, initial darkening threshold
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(1200f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t1 (1000)
+ // Lux decreased, first darkening event
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(800f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t2 (2000) (t2 - t1 < darkeningLightDebounceConfig)
+ // Lux decreased, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(500f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t3 (3000) (t3 - t1 < darkeningLightDebounceConfig)
+ // Lux decreased, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(400f))
+ assertEquals(400f, reportedAmbientLux, FLOAT_TOLERANCE)
+ }
+
+ @Test
+ fun `test brightening debounce in idle mode`() {
+ val lightSensorController = LightSensorController(
+ createLightSensorControllerConfig(
+ lightSensorWarmUpTimeConfig = 0, // no warmUp time, can use first event
+ brighteningLightDebounceConfigIdle = 1500,
+ ambientLightHorizonShort = 0, // only last value will be used for lux calculation
+ ambientLightHorizonLong = 10_000,
+ // brightening threshold is set to previous lux value
+ ambientBrightnessThresholdsIdle = createHysteresisLevels(
+ brighteningThresholdLevels = floatArrayOf(),
+ brighteningThresholdsPercentages = floatArrayOf(),
+ minBrighteningThreshold = 0f
+ )
+ ), testInjector, testHandler)
+ lightSensorController.setIdleMode(true)
+
+ var reportedAmbientLux = 0f
+ lightSensorController.setListener { lux ->
+ reportedAmbientLux = lux
+ }
+ lightSensorController.enableLightSensorIfNeeded()
+
+ assertThat(testInjector.sensorEventListener).isNotNull()
+
+ // t0 (0)
+ // Initial lux, initial brightening threshold
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(1200f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t1 (1000)
+ // Lux increase, first brightening event
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(1800f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t2 (2000) (t2 - t1 < brighteningLightDebounceConfigIdle)
+ // Lux increase, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(2000f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t3 (3000) (t3 - t1 < brighteningLightDebounceConfigIdle)
+ // Lux increase, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(2200f))
+ assertEquals(2200f, reportedAmbientLux, FLOAT_TOLERANCE)
+ }
+
+ @Test
+ fun `test darkening debounce in idle mode`() {
+ val lightSensorController = LightSensorController(
+ createLightSensorControllerConfig(
+ lightSensorWarmUpTimeConfig = 0, // no warmUp time, can use first event
+ darkeningLightDebounceConfigIdle = 1500,
+ ambientLightHorizonShort = 0, // only last value will be used for lux calculation
+ ambientLightHorizonLong = 10_000,
+ // darkening threshold is set to previous lux value
+ ambientBrightnessThresholdsIdle = createHysteresisLevels(
+ darkeningThresholdLevels = floatArrayOf(),
+ darkeningThresholdsPercentages = floatArrayOf(),
+ minDarkeningThreshold = 0f
+ )
+ ), testInjector, testHandler)
+
+ lightSensorController.setIdleMode(true)
+
+ var reportedAmbientLux = 0f
+ lightSensorController.setListener { lux ->
+ reportedAmbientLux = lux
+ }
+ lightSensorController.enableLightSensorIfNeeded()
+
+ assertThat(testInjector.sensorEventListener).isNotNull()
+
+ // t0 (0)
+ // Initial lux, initial darkening threshold
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(1200f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t1 (1000)
+ // Lux decreased, first darkening event
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(800f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t2 (2000) (t2 - t1 < darkeningLightDebounceConfigIdle)
+ // Lux decreased, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(500f))
+ assertEquals(1200f, reportedAmbientLux, FLOAT_TOLERANCE)
+
+ // t3 (3000) (t3 - t1 < darkeningLightDebounceConfigIdle)
+ // Lux decreased, but isn't steady yet
+ testInjector.clock.fastForward(1000)
+ testInjector.sensorEventListener!!.onSensorChanged(sensorEvent(400f))
+ assertEquals(400f, reportedAmbientLux, FLOAT_TOLERANCE)
+ }
+
+
+ private fun sensorEvent(value: Float) = SensorEvent(
+ testInjector.testSensor, 0, 0, floatArrayOf(value)
+ )
+
+ private class TestInjector : Injector {
+ val testSensor: Sensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor")
+ val clock: OffsettableClock = OffsettableClock.Stopped()
+
+ var sensorEventListener: SensorEventListener? = null
+ override fun getClock(): Clock {
+ return object : Clock() {
+ override fun uptimeMillis(): Long {
+ return clock.now()
+ }
+ }
+ }
+
+ override fun getLightSensor(config: LightSensorControllerConfig): Sensor {
+ return testSensor
+ }
+
+ override fun registerLightSensorListener(
+ listener: SensorEventListener,
+ sensor: Sensor,
+ rate: Int,
+ handler: Handler
+ ): Boolean {
+ sensorEventListener = listener
+ return true
+ }
+
+ override fun unregisterLightSensorListener(listener: SensorEventListener) {
+ sensorEventListener = null
+ }
+
+ override fun getTag(): String {
+ return "LightSensorControllerTest"
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/TestUtils.kt
new file mode 100644
index 0000000..1328f5f
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/TestUtils.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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.brightness
+
+import com.android.server.display.HysteresisLevels
+import com.android.server.display.config.SensorData
+
+@JvmOverloads
+fun createLightSensorControllerConfig(
+ initialSensorRate: Int = 1,
+ normalSensorRate: Int = 2,
+ resetAmbientLuxAfterWarmUpConfig: Boolean = true,
+ ambientLightHorizonShort: Int = 1,
+ ambientLightHorizonLong: Int = 10_000,
+ lightSensorWarmUpTimeConfig: Int = 0,
+ weightingIntercept: Int = 10_000,
+ ambientBrightnessThresholds: HysteresisLevels = createHysteresisLevels(),
+ ambientBrightnessThresholdsIdle: HysteresisLevels = createHysteresisLevels(),
+ brighteningLightDebounceConfig: Long = 100_000,
+ darkeningLightDebounceConfig: Long = 100_000,
+ brighteningLightDebounceConfigIdle: Long = 100_000,
+ darkeningLightDebounceConfigIdle: Long = 100_000,
+ ambientLightSensor: SensorData = SensorData()
+) = LightSensorController.LightSensorControllerConfig(
+ initialSensorRate,
+ normalSensorRate,
+ resetAmbientLuxAfterWarmUpConfig,
+ ambientLightHorizonShort,
+ ambientLightHorizonLong,
+ lightSensorWarmUpTimeConfig,
+ weightingIntercept,
+ ambientBrightnessThresholds,
+ ambientBrightnessThresholdsIdle,
+ brighteningLightDebounceConfig,
+ darkeningLightDebounceConfig,
+ brighteningLightDebounceConfigIdle,
+ darkeningLightDebounceConfigIdle,
+ ambientLightSensor
+)
+
+fun createHysteresisLevels(
+ brighteningThresholdsPercentages: FloatArray = floatArrayOf(),
+ darkeningThresholdsPercentages: FloatArray = floatArrayOf(),
+ brighteningThresholdLevels: FloatArray = floatArrayOf(),
+ darkeningThresholdLevels: FloatArray = floatArrayOf(),
+ minDarkeningThreshold: Float = 0f,
+ minBrighteningThreshold: Float = 0f,
+ potentialOldBrightnessRange: Boolean = false
+) = HysteresisLevels(
+ brighteningThresholdsPercentages,
+ darkeningThresholdsPercentages,
+ brighteningThresholdLevels,
+ darkeningThresholdLevels,
+ minDarkeningThreshold,
+ minBrighteningThreshold,
+ potentialOldBrightnessRange
+)
\ No newline at end of file