From 8999bbcfd9b6a2f480c8c47b8d36bbeb5ce96cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wilczy=C5=84ski?= Date: Thu, 6 Oct 2022 13:46:30 +0000 Subject: [PATCH] Use a brightness sensor to determine the screen's initial brightness - Create a controller to listen to a brightness sensor when the screen is off - Estimate the ambient lux based on the sensor reading - Determine the brightness using the ambient lux estimate - Specify the sensor and the sensor value to lux mapping in the config Bug: 193224604 Test: Use the device in a bright room, turn the screen off, go to a dark room, turn the screen on, see that the screen doesn't start as too bright. Test: atest ScreenOffBrightnessSensorControllerTest Test: atest DisplayDeviceConfigTest Change-Id: Ib4c4d9e0d6b385b0d309be6b8c27b71af72d4ecc --- .../server/display/DisplayDeviceConfig.java | 57 +++++++ .../display/DisplayPowerController.java | 48 ++++++ .../display/DisplayPowerController2.java | 49 ++++++ .../ScreenOffBrightnessSensorController.java | 127 +++++++++++++++ .../display/brightness/BrightnessReason.java | 5 +- .../display-device-config.xsd | 14 ++ .../display-device-config/schema/current.txt | 9 ++ .../display/DisplayDeviceConfigTest.java | 21 +++ ...reenOffBrightnessSensorControllerTest.java | 148 ++++++++++++++++++ .../com/android/server/display/TestUtils.java | 4 +- 10 files changed, 479 insertions(+), 3 deletions(-) create mode 100644 services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java create mode 100644 services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 98a0af7f7c152..1217e7423634d 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -46,6 +46,7 @@ import com.android.server.display.config.DisplayConfiguration; import com.android.server.display.config.DisplayQuirks; import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; +import com.android.server.display.config.IntegerArray; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; import com.android.server.display.config.RefreshRateConfigs; @@ -213,6 +214,10 @@ import javax.xml.datatype.DatatypeConfigurationException; * android.sensor.light * 1234 Ambient Light Sensor * + * + * com.google.sensor.binned_brightness + * Binned Brightness 0 (wake-up) + * * * android.sensor.proximity * 1234 Proximity Sensor @@ -368,6 +373,13 @@ import javax.xml.datatype.DatatypeConfigurationException; * * * + * + * -1 + * 0 + * 5 + * 80 + * 1500 + * * * } * @@ -428,6 +440,9 @@ public class DisplayDeviceConfig { // The details of the ambient light sensor associated with this display. private final SensorData mAmbientLightSensor = new SensorData(); + // The details of the doze brightness sensor associated with this display. + private final SensorData mScreenOffBrightnessSensor = new SensorData(); + // The details of the proximity sensor associated with this display. private final SensorData mProximitySensor = new SensorData(); @@ -523,6 +538,9 @@ public class DisplayDeviceConfig { private float[] mAmbientDarkeningLevelsIdle = DEFAULT_AMBIENT_THRESHOLD_LEVELS; private float[] mAmbientDarkeningPercentagesIdle = DEFAULT_AMBIENT_DARKENING_THRESHOLDS; + // A mapping between screen off sensor values and lux values + private int[] mScreenOffBrightnessSensorValueToLux; + private Spline mBrightnessToBacklightSpline; private Spline mBacklightToBrightnessSpline; private Spline mBacklightToNitsSpline; @@ -1197,6 +1215,10 @@ public class DisplayDeviceConfig { return mAmbientLightSensor; } + SensorData getScreenOffBrightnessSensor() { + return mScreenOffBrightnessSensor; + } + SensorData getProximitySensor() { return mProximitySensor; } @@ -1320,6 +1342,14 @@ public class DisplayDeviceConfig { return mHighAmbientBrightnessThresholds; } + /** + * @return A mapping from screen off brightness sensor readings to lux values. This estimates + * the ambient lux when the screen is off to determine the initial brightness + */ + public int[] getScreenOffBrightnessSensorValueToLux() { + return mScreenOffBrightnessSensorValueToLux; + } + @Override public String toString() { return "DisplayDeviceConfig{" @@ -1398,6 +1428,7 @@ public class DisplayDeviceConfig { mScreenDarkeningPercentagesIdle) + "\n" + ", mAmbientLightSensor=" + mAmbientLightSensor + + ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor + ", mProximitySensor=" + mProximitySensor + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray()) + ", mDensityMapping= " + mDensityMapping @@ -1420,6 +1451,9 @@ public class DisplayDeviceConfig { + Arrays.toString(mHighDisplayBrightnessThresholds) + ", mHighAmbientBrightnessThresholds= " + Arrays.toString(mHighAmbientBrightnessThresholds) + + "\n" + + ", mScreenOffBrightnessSensorValueToLux=" + Arrays.toString( + mScreenOffBrightnessSensorValueToLux) + "}"; } @@ -1473,11 +1507,13 @@ public class DisplayDeviceConfig { loadQuirks(config); loadBrightnessRamps(config); loadAmbientLightSensorFromDdc(config); + loadScreenOffBrightnessSensorFromDdc(config); loadProxSensorFromDdc(config); loadAmbientHorizonFromDdc(config); loadBrightnessChangeThresholds(config); loadAutoBrightnessConfigValues(config); loadRefreshRateSetting(config); + loadScreenOffBrightnessSensorValueToLuxFromDdc(config); } else { Slog.w(TAG, "DisplayDeviceConfig file is null"); } @@ -2169,6 +2205,14 @@ public class DisplayDeviceConfig { mProximitySensor.type = null; } + private void loadScreenOffBrightnessSensorFromDdc(DisplayConfiguration config) { + final SensorDetails sensorDetails = config.getScreenOffBrightnessSensor(); + if (sensorDetails != null) { + mScreenOffBrightnessSensor.type = sensorDetails.getType(); + mScreenOffBrightnessSensor.name = sensorDetails.getName(); + } + } + private void loadProxSensorFromDdc(DisplayConfiguration config) { SensorDetails sensorDetails = config.getProxSensor(); if (sensorDetails != null) { @@ -2573,6 +2617,19 @@ public class DisplayDeviceConfig { && mDdcAutoBrightnessAvailable; } + private void loadScreenOffBrightnessSensorValueToLuxFromDdc(DisplayConfiguration config) { + IntegerArray sensorValueToLux = config.getScreenOffBrightnessSensorValueToLux(); + if (sensorValueToLux == null) { + return; + } + + List items = sensorValueToLux.getItem(); + mScreenOffBrightnessSensorValueToLux = new int[items.size()]; + for (int i = 0; i < items.size(); i++) { + mScreenOffBrightnessSensorValueToLux[i] = items.get(i).intValue(); + } + } + static class SensorData { public String type; public String name; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 75415cd9997f6..e4fcdbd071887 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -408,7 +408,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Nullable private AutomaticBrightnessController mAutomaticBrightnessController; + // The controller for the sensor used to estimate ambient lux while the display is off. + @Nullable + private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; + private Sensor mLightSensor; + private Sensor mScreenOffBrightnessSensor; // The mappers between ambient lux, display backlight values, and display brightness. // We will switch between the idle mapper and active mapper in AutomaticBrightnessController. @@ -1077,6 +1082,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessEventRingBuffer = new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); + + loadScreenOffBrightnessSensor(); + int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux(); + if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) { + mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController( + mSensorManager, + mScreenOffBrightnessSensor, + mHandler, + SystemClock::uptimeMillis, + sensorValueToLux, + mInteractiveModeBrightnessMapper + ); + } } else { mUseSoftwareAutoBrightnessConfig = false; } @@ -1262,6 +1280,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } assert (state != Display.STATE_UNKNOWN); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness + && (state == Display.STATE_OFF || (state == Display.STATE_DOZE + && !mAllowAutoBrightnessWhileDozingConfig))); + } + boolean skipRampBecauseOfProximityChangeToNegative = false; // Apply the proximity sensor. if (mProximitySensor != null) { @@ -1433,6 +1457,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; mAppliedAutoBrightness = true; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.setLightSensorEnabled(false); + } } else { mAppliedAutoBrightness = false; } @@ -1460,6 +1487,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); } + // The ALS is not available yet - use the screen off sensor to determine the initial + // brightness + if (Float.isNaN(brightnessState) && autoBrightnessEnabled + && mScreenOffBrightnessSensorController != null) { + brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness(); + if (isValidBrightnessValue(brightnessState)) { + brightnessState = clampScreenBrightness(brightnessState); + updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; + mBrightnessReasonTemp.setReason( + BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR); + } + } + // Apply manual brightness. if (Float.isNaN(brightnessState)) { brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting); @@ -2034,6 +2074,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call fallbackType); } + private void loadScreenOffBrightnessSensor() { + DisplayDeviceConfig.SensorData screenOffBrightnessSensor = + mDisplayDeviceConfig.getScreenOffBrightnessSensor(); + mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager, + screenOffBrightnessSensor.type, screenOffBrightnessSensor.name, + SensorUtils.NO_FALLBACK); + } + private void loadProximitySensor() { if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { return; diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 111caefa34da4..81011dcb5bc75 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -360,7 +360,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal @Nullable private AutomaticBrightnessController mAutomaticBrightnessController; + // The controller for the sensor used to estimate ambient lux while the display is off. + @Nullable + private ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; + private Sensor mLightSensor; + private Sensor mScreenOffBrightnessSensor; // The mappers between ambient lux, display backlight values, and display brightness. // We will switch between the idle mapper and active mapper in AutomaticBrightnessController. @@ -994,6 +999,19 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBrightnessEventRingBuffer = new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX); + + loadScreenOffBrightnessSensor(); + int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux(); + if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) { + mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController( + mSensorManager, + mScreenOffBrightnessSensor, + mHandler, + SystemClock::uptimeMillis, + sensorValueToLux, + mInteractiveModeBrightnessMapper + ); + } } else { mUseSoftwareAutoBrightnessConfig = false; } @@ -1135,6 +1153,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal int state = mDisplayStateController .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition); + + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness + && (state == Display.STATE_OFF || (state == Display.STATE_DOZE + && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()))); + } + // Initialize things the first time the power state is changed. if (mustInitialize) { initialize(state); @@ -1224,6 +1249,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; mAppliedAutoBrightness = true; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController.setLightSensorEnabled(false); + } } else { mAppliedAutoBrightness = false; } @@ -1251,6 +1279,19 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); } + // The ALS is not available yet - use the screen off sensor to determine the initial + // brightness + if (Float.isNaN(brightnessState) && autoBrightnessEnabled + && mScreenOffBrightnessSensorController != null) { + brightnessState = mScreenOffBrightnessSensorController.getAutomaticScreenBrightness(); + if (isValidBrightnessValue(brightnessState)) { + brightnessState = clampScreenBrightness(brightnessState); + updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState; + mBrightnessReasonTemp.setReason( + BrightnessReason.REASON_SCREEN_OFF_BRIGHTNESS_SENSOR); + } + } + // Apply manual brightness. if (Float.isNaN(brightnessState)) { brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting); @@ -1819,6 +1860,14 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal fallbackType); } + private void loadScreenOffBrightnessSensor() { + DisplayDeviceConfig.SensorData screenOffBrightnessSensor = + mDisplayDeviceConfig.getScreenOffBrightnessSensor(); + mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager, + screenOffBrightnessSensor.type, screenOffBrightnessSensor.name, + SensorUtils.NO_FALLBACK); + } + private float clampScreenBrightness(float value) { if (Float.isNaN(value)) { value = PowerManager.BRIGHTNESS_MIN; diff --git a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java new file mode 100644 index 0000000000000..6f50dac07b99a --- /dev/null +++ b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.annotation.Nullable; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.PowerManager; +import android.util.IndentingPrintWriter; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.PrintWriter; + +/** + * Controls the light sensor when the screen is off. The sensor used here does not report lux values + * but an index that needs to be mapped to a lux value. + */ +public class ScreenOffBrightnessSensorController implements SensorEventListener { + private static final String TAG = "ScreenOffBrightnessSensorController"; + + private static final int SENSOR_INVALID_VALUE = -1; + private static final long SENSOR_VALUE_VALID_TIME_MILLIS = 1500; + + private final Handler mHandler; + private final Clock mClock; + private final SensorManager mSensorManager; + private final Sensor mLightSensor; + private final int[] mSensorValueToLux; + + private boolean mRegistered; + private int mLastSensorValue = SENSOR_INVALID_VALUE; + private long mSensorDisableTime = -1; + + // The mapper to translate ambient lux to screen brightness in the range [0, 1.0]. + @Nullable + private final BrightnessMappingStrategy mBrightnessMapper; + + public ScreenOffBrightnessSensorController( + SensorManager sensorManager, + Sensor lightSensor, + Handler handler, + Clock clock, + int[] sensorValueToLux, + BrightnessMappingStrategy brightnessMapper) { + mSensorManager = sensorManager; + mLightSensor = lightSensor; + mHandler = handler; + mClock = clock; + mSensorValueToLux = sensorValueToLux; + mBrightnessMapper = brightnessMapper; + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (mRegistered) { + mLastSensorValue = (int) event.values[0]; + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + void setLightSensorEnabled(boolean enabled) { + if (enabled && !mRegistered) { + // Wait until we get an event from the sensor indicating ready. + mRegistered = mSensorManager.registerListener(this, mLightSensor, + SensorManager.SENSOR_DELAY_NORMAL, mHandler); + mLastSensorValue = SENSOR_INVALID_VALUE; + } else if (!enabled && mRegistered) { + mSensorManager.unregisterListener(this); + mRegistered = false; + mSensorDisableTime = mClock.uptimeMillis(); + } + } + + float getAutomaticScreenBrightness() { + if (mLastSensorValue < 0 || mLastSensorValue >= mSensorValueToLux.length + || (!mRegistered + && mClock.uptimeMillis() - mSensorDisableTime > SENSOR_VALUE_VALID_TIME_MILLIS)) { + return PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + + int lux = mSensorValueToLux[mLastSensorValue]; + if (lux < 0) { + return PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + + return mBrightnessMapper.getBrightness(lux); + } + + /** Dump current state */ + public void dump(PrintWriter pw) { + pw.println("ScreenOffBrightnessSensorController:"); + IndentingPrintWriter idpw = new IndentingPrintWriter(pw); + idpw.increaseIndent(); + idpw.println("registered=" + mRegistered); + idpw.println("lastSensorValue=" + mLastSensorValue); + } + + /** Functional interface for providing time. */ + @VisibleForTesting + interface Clock { + /** + * Returns current time in milliseconds since boot, not counting time spent in deep sleep. + */ + long uptimeMillis(); + } +} diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java index b6be713d344bc..a952004e7f9d1 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java @@ -37,7 +37,8 @@ public final class BrightnessReason { public static final int REASON_OVERRIDE = 6; public static final int REASON_TEMPORARY = 7; public static final int REASON_BOOST = 8; - public static final int REASON_MAX = REASON_BOOST; + public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9; + public static final int REASON_MAX = REASON_SCREEN_OFF_BRIGHTNESS_SENSOR; public static final int MODIFIER_DIMMED = 0x1; public static final int MODIFIER_LOW_POWER = 0x2; @@ -190,6 +191,8 @@ public final class BrightnessReason { return "temporary"; case REASON_BOOST: return "boost"; + case REASON_SCREEN_OFF_BRIGHTNESS_SENSOR: + return "screen_off_brightness_sensor"; default: return Integer.toString(reason); } diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index f96c929375c5f..7f85347b67318 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -74,6 +74,9 @@ + + + @@ -109,6 +112,11 @@ + + + + @@ -485,4 +493,10 @@ + + + + + + diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 6276edaf3ebc8..cb081791ffce0 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -98,6 +98,8 @@ package com.android.server.display.config { method public final java.math.BigInteger getScreenBrightnessRampIncreaseMaxMillis(); method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease(); method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease(); + method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor(); + method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux(); method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling(); method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds); method public final void setAmbientBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds); @@ -120,6 +122,8 @@ package com.android.server.display.config { method public final void setScreenBrightnessRampIncreaseMaxMillis(java.math.BigInteger); method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal); method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal); + method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails); + method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray); method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling); } @@ -160,6 +164,11 @@ package com.android.server.display.config { method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal); } + public class IntegerArray { + ctor public IntegerArray(); + method public java.util.List getItem(); + } + public class NitsMap { ctor public NitsMap(); method public String getInterpolation(); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java index c1eafd9e35a68..82b89bbb04645 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -156,6 +156,14 @@ public final class DisplayDeviceConfigTest { assertArrayEquals(new int[]{70, 80}, mDisplayDeviceConfig.getHighAmbientBrightnessThresholds()); + assertEquals("sensor_12345", + mDisplayDeviceConfig.getScreenOffBrightnessSensor().type); + assertEquals("Sensor 12345", + mDisplayDeviceConfig.getScreenOffBrightnessSensor().name); + + assertArrayEquals(new int[]{-1, 10, 20, 30, 40}, + mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux()); + // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping, // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor. } @@ -227,6 +235,7 @@ public final class DisplayDeviceConfigTest { HIGH_BRIGHTNESS_THRESHOLD_OF_PEAK_REFRESH_RATE); assertArrayEquals(mDisplayDeviceConfig.getHighAmbientBrightnessThresholds(), HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE); + // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping, // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor. } @@ -278,6 +287,10 @@ public final class DisplayDeviceConfigTest { + "light\n" + "false\n" + "\n" + + "\n" + + "sensor_12345\n" + + "Sensor 12345\n" + + "\n" + "\n" + "\n" + "10\n" @@ -458,6 +471,13 @@ public final class DisplayDeviceConfigTest { + "\n" + "\n" + "\n" + + "\n" + + "-1\n" + + "10\n" + + "20\n" + + "30\n" + + "40\n" + + "\n" + "\n"; } @@ -535,6 +555,7 @@ public final class DisplayDeviceConfigTest { when(mResources.getIntArray( R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate)) .thenReturn(HIGH_AMBIENT_THRESHOLD_OF_PEAK_REFRESH_RATE); + mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true); } diff --git a/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java b/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java new file mode 100644 index 0000000000000..ea04a193e5693 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2022 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.PowerManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.testutils.OffsettableClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ScreenOffBrightnessSensorControllerTest { + + private static final int[] SENSOR_TO_LUX = new int[]{-1, 10, 20, 30, 40}; + + private ScreenOffBrightnessSensorController mController; + private OffsettableClock mClock; + private Sensor mLightSensor; + + @Mock SensorManager mSensorManager; + @Mock Handler mNoOpHandler; + @Mock BrightnessMappingStrategy mBrightnessMappingStrategy; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mClock = new OffsettableClock.Stopped(); + mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); + mController = new ScreenOffBrightnessSensorController( + mSensorManager, + mLightSensor, + mNoOpHandler, + mClock::now, + SENSOR_TO_LUX, + mBrightnessMappingStrategy + ); + } + + @Test + public void testBrightness() throws Exception { + when(mSensorManager.registerListener(any(SensorEventListener.class), eq(mLightSensor), + eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class))) + .thenReturn(true); + mController.setLightSensorEnabled(true); + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); + assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, + mController.getAutomaticScreenBrightness(), 0); + + int sensorValue = 1; + float brightness = 0.2f; + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue)); + when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue])) + .thenReturn(brightness); + assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0); + + sensorValue = 2; + brightness = 0.4f; + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue)); + when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue])) + .thenReturn(brightness); + assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0); + + sensorValue = 3; + brightness = 0.6f; + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue)); + when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue])) + .thenReturn(brightness); + assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0); + + sensorValue = 4; + brightness = 0.8f; + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue)); + when(mBrightnessMappingStrategy.getBrightness(SENSOR_TO_LUX[sensorValue])) + .thenReturn(brightness); + assertEquals(brightness, mController.getAutomaticScreenBrightness(), 0); + + sensorValue = 5; + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, sensorValue)); + assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, + mController.getAutomaticScreenBrightness(), 0); + } + + @Test + public void testSensorValueValidTime() throws Exception { + when(mSensorManager.registerListener(any(SensorEventListener.class), eq(mLightSensor), + eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class))) + .thenReturn(true); + mController.setLightSensorEnabled(true); + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + eq(SensorManager.SENSOR_DELAY_NORMAL), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1)); + mController.setLightSensorEnabled(false); + assertNotEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, + mController.getAutomaticScreenBrightness(), 0); + + mClock.fastForward(2000); + mController.setLightSensorEnabled(false); + assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, + mController.getAutomaticScreenBrightness(), 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java index a419b3f80aac1..90d9baed53d7a 100644 --- a/services/tests/servicestests/src/com/android/server/display/TestUtils.java +++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java @@ -28,13 +28,13 @@ import java.lang.reflect.Method; public final class TestUtils { - public static SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception { + public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception { final Constructor constructor = SensorEvent.class.getDeclaredConstructor(int.class); constructor.setAccessible(true); final SensorEvent event = constructor.newInstance(1); event.sensor = sensor; - event.values[0] = lux; + event.values[0] = value; event.timestamp = SystemClock.elapsedRealtimeNanos(); return event; }