From 6ddd0586cde96808f48fb55d66b6c2e52a83af0d 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 Merged-In: Ib4c4d9e0d6b385b0d309be6b8c27b71af72d4ecc --- .../server/display/DisplayDeviceConfig.java | 57 +++++++ .../display/DisplayPowerController.java | 52 +++++- .../ScreenOffBrightnessSensorController.java | 127 +++++++++++++++ .../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 +- 8 files changed, 429 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 401d184dd7fb9..5bdfa70096c05 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; @@ -1198,6 +1216,10 @@ public class DisplayDeviceConfig { return mAmbientLightSensor; } + SensorData getScreenOffBrightnessSensor() { + return mScreenOffBrightnessSensor; + } + SensorData getProximitySensor() { return mProximitySensor; } @@ -1321,6 +1343,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{" @@ -1399,6 +1429,7 @@ public class DisplayDeviceConfig { mScreenDarkeningPercentagesIdle) + "\n" + ", mAmbientLightSensor=" + mAmbientLightSensor + + ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor + ", mProximitySensor=" + mProximitySensor + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray()) + ", mDensityMapping= " + mDensityMapping @@ -1421,6 +1452,9 @@ public class DisplayDeviceConfig { + Arrays.toString(mHighDisplayBrightnessThresholds) + ", mHighAmbientBrightnessThresholds= " + Arrays.toString(mHighAmbientBrightnessThresholds) + + "\n" + + ", mScreenOffBrightnessSensorValueToLux=" + Arrays.toString( + mScreenOffBrightnessSensorValueToLux) + "}"; } @@ -1474,11 +1508,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"); } @@ -2175,6 +2211,14 @@ public class DisplayDeviceConfig { mProximitySensor.type = ""; } + 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) { @@ -2587,6 +2631,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 2fc6fd2254a76..24f39cbbd3b7d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -411,7 +411,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. @@ -1088,6 +1093,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; } @@ -1265,6 +1283,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) { @@ -1442,6 +1466,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; } @@ -1469,6 +1496,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); @@ -2038,6 +2078,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; @@ -3186,7 +3234,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call static final int REASON_OVERRIDE = 7; static final int REASON_TEMPORARY = 8; static final int REASON_BOOST = 9; - static final int REASON_MAX = REASON_BOOST; + static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 10; + static final int REASON_MAX = REASON_SCREEN_OFF_BRIGHTNESS_SENSOR; static final int MODIFIER_DIMMED = 0x1; static final int MODIFIER_LOW_POWER = 0x2; @@ -3296,6 +3345,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case REASON_OVERRIDE: return "override"; case REASON_TEMPORARY: 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/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/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 7bc89313c75e3..f628fbad892d4 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 6b705aaf9721d..86c59379a61ef 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -161,6 +161,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. } @@ -232,6 +240,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. } @@ -283,6 +292,10 @@ public final class DisplayDeviceConfigTest { + "light\n" + "false\n" + "\n" + + "\n" + + "sensor_12345\n" + + "Sensor 12345\n" + + "\n" + "\n" + "\n" + "10\n" @@ -467,6 +480,13 @@ public final class DisplayDeviceConfigTest { + "\n" + "\n" + "\n" + + "\n" + + "-1\n" + + "10\n" + + "20\n" + + "30\n" + + "40\n" + + "\n" + "\n"; } @@ -544,6 +564,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 0454587bfefe6..f3f04b8bf5ec7 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; }