diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8a77fc94a48a5..479ba1ec2cd1d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1178,6 +1178,38 @@ + + false + + + + + + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7f093827204bc..92d987edbf7dd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1648,6 +1648,8 @@ + + @@ -1665,6 +1667,7 @@ + diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index a3febd63e2ed9..935fa13adb47e 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -109,8 +109,8 @@ class AutomaticBrightnessController { // weighting values positive. private final int mWeightingIntercept; - // accessor object for determining thresholds to change brightness dynamically - private final HysteresisLevels mDynamicHysteresis; + // accessor object for determining lux levels + private final LuxLevels mLuxLevels; // Amount of time to delay auto-brightness after screen on while waiting for // the light sensor to warm-up in milliseconds. @@ -172,6 +172,9 @@ class AutomaticBrightnessController { // Are we going to adjust brightness while dozing. private boolean mDozing; + // True if we are collecting one last light sample when dozing to set the screen brightness + private boolean mActiveDozeLightSensor = false; + // True if we are collecting a brightness adjustment sample, along with some data // for the initial state of the sample. private boolean mBrightnessAdjustmentSamplePending; @@ -188,7 +191,7 @@ class AutomaticBrightnessController { int lightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma, - HysteresisLevels dynamicHysteresis) { + LuxLevels luxLevels) { mCallbacks = callbacks; mTwilight = LocalServices.getService(TwilightManager.class); mSensorManager = sensorManager; @@ -204,7 +207,7 @@ class AutomaticBrightnessController { mAmbientLightHorizon = ambientLightHorizon; mWeightingIntercept = ambientLightHorizon; mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma; - mDynamicHysteresis = dynamicHysteresis; + mLuxLevels = luxLevels; mHandler = new AutomaticBrightnessHandler(looper); mAmbientLightRingBuffer = @@ -218,7 +221,7 @@ class AutomaticBrightnessController { } public int getAutomaticScreenBrightness() { - if (mDozing) { + if (mDozing && !mLuxLevels.hasDynamicDozeBrightness()) { return (int) (mScreenAutoBrightness * mDozeScaleFactor); } return mScreenAutoBrightness; @@ -232,13 +235,26 @@ class AutomaticBrightnessController { // and hold onto the last computed screen auto brightness. We save the dozing flag for // debugging purposes. mDozing = dozing; - boolean changed = setLightSensorEnabled(enable && !dozing); + boolean enableSensor = enable && !dozing; + if (enableSensor && !mLightSensorEnabled && mActiveDozeLightSensor) { + mActiveDozeLightSensor = false; + } else if (!enableSensor && mLightSensorEnabled && mLuxLevels.hasDynamicDozeBrightness()) { + // keep the light sensor active until another light sample is taken in doze mode + mActiveDozeLightSensor = true; + if (mLuxLevels.useNewSensorSamplesForDoze()) { + mAmbientLightRingBuffer.clear(); + mInitialHorizonAmbientLightRingBuffer.clear(); + mAmbientLuxValid = false; + return; + } + } + boolean changed = setLightSensorEnabled(enableSensor); changed |= setScreenAutoBrightnessAdjustment(adjustment); changed |= setUseTwilight(useTwilight); if (changed) { updateAutoBrightness(false /*sendUpdate*/); } - if (enable && !dozing && userInitiatedChange) { + if (enableSensor && userInitiatedChange) { prepareBrightnessAdjustmentSample(); } } @@ -292,6 +308,9 @@ class AutomaticBrightnessController { if (enable) { if (!mLightSensorEnabled) { mLightSensorEnabled = true; + mAmbientLightRingBuffer.clear(); + mInitialHorizonAmbientLightRingBuffer.clear(); + mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig; mLightSensorEnableTime = SystemClock.uptimeMillis(); mSensorManager.registerListener(mLightSensorListener, mLightSensor, mLightSensorRate * 1000, mHandler); @@ -300,11 +319,9 @@ class AutomaticBrightnessController { } else { if (mLightSensorEnabled) { mLightSensorEnabled = false; - mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig; mRecentLightSamples = 0; - mAmbientLightRingBuffer.clear(); - mInitialHorizonAmbientLightRingBuffer.clear(); mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX); + Slog.d(TAG, "disabling light sensor"); mSensorManager.unregisterListener(mLightSensorListener); } } @@ -316,6 +333,11 @@ class AutomaticBrightnessController { applyLightSensorMeasurement(time, lux); updateAmbientLux(time); + if (mActiveDozeLightSensor) { + // disable the ambient light sensor and update the screen brightness + setLightSensorEnabled(false); + updateAutoBrightness(true /*sendUpdate*/); + } } private void applyLightSensorMeasurement(long time, float lux) { @@ -327,6 +349,7 @@ class AutomaticBrightnessController { } mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon); mAmbientLightRingBuffer.push(time, lux); + Slog.d(TAG, "pushing lux: " + lux); // Remember this sample value. mLastObservedLux = lux; @@ -343,8 +366,8 @@ class AutomaticBrightnessController { private void setAmbientLux(float lux) { mAmbientLux = lux; - mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux); - mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux); + mBrighteningLuxThreshold = mLuxLevels.getBrighteningThreshold(lux); + mDarkeningLuxThreshold = mLuxLevels.getDarkeningThreshold(lux); } private float calculateAmbientLux(long now) { @@ -516,8 +539,14 @@ class AutomaticBrightnessController { } } - int newScreenAutoBrightness = + int newScreenAutoBrightness; + if (mActiveDozeLightSensor) { + newScreenAutoBrightness = mLuxLevels.getDozeBrightness(mAmbientLux); + } else { + newScreenAutoBrightness = clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON)); + } + if (mScreenAutoBrightness != newScreenAutoBrightness) { if (DEBUG) { Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness=" diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index df5def9ad9b90..dfd42543c0783 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -322,15 +322,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1); - int[] brightLevels = resources.getIntArray( - com.android.internal.R.array.config_dynamicHysteresisBrightLevels); - int[] darkLevels = resources.getIntArray( - com.android.internal.R.array.config_dynamicHysteresisDarkLevels); - int[] luxLevels = resources.getIntArray( - com.android.internal.R.array.config_dynamicHysteresisLuxLevels); - HysteresisLevels dynamicHysteresis = new HysteresisLevels( - brightLevels, darkLevels, luxLevels); - if (mUseSoftwareAutoBrightnessConfig) { int[] lux = resources.getIntArray( com.android.internal.R.array.config_autoBrightnessLevels); @@ -342,6 +333,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); + // hysteresis configs + int[] brightHysteresisLevels = resources.getIntArray( + com.android.internal.R.array.config_dynamicHysteresisBrightLevels); + int[] darkHysteresisLevels = resources.getIntArray( + com.android.internal.R.array.config_dynamicHysteresisDarkLevels); + int[] luxHysteresisLevels = resources.getIntArray( + com.android.internal.R.array.config_dynamicHysteresisLuxLevels); + // doze brightness configs + int[] dozeSensorLuxLevels = resources.getIntArray( + com.android.internal.R.array.config_dozeSensorLuxLevels); + int[] dozeBrightnessBacklightValues = resources.getIntArray( + com.android.internal.R.array.config_dozeBrightnessBacklightValues); + boolean useNewSensorSamplesForDoze = resources.getBoolean( + com.android.internal.R.bool.config_useNewSensorSamplesForDoze); + LuxLevels luxLevels = new LuxLevels(brightHysteresisLevels, darkHysteresisLevels, + luxHysteresisLevels, useNewSensorSamplesForDoze, dozeSensorLuxLevels, + dozeBrightnessBacklightValues); + Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness); if (screenAutoBrightnessSpline == null) { Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues " @@ -368,7 +377,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon, - autoBrightnessAdjustmentMaxGamma, dynamicHysteresis); + autoBrightnessAdjustmentMaxGamma, luxLevels); } } diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java deleted file mode 100644 index b062225638118..0000000000000 --- a/services/core/java/com/android/server/display/HysteresisLevels.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2016 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.util.Slog; - -/** - * A helper class for handling access to illuminance hysteresis level values. - */ -final class HysteresisLevels { - private static final String TAG = "HysteresisLevels"; - - // Default hysteresis constraints for brightening or darkening. - // The recent lux must have changed by at least this fraction relative to the - // current ambient lux before a change will be considered. - private static final float DEFAULT_BRIGHTENING_HYSTERESIS = 0.10f; - private static final float DEFAULT_DARKENING_HYSTERESIS = 0.20f; - - private static final boolean DEBUG = false; - - private final float[] mBrightLevels; - private final float[] mDarkLevels; - private final float[] mLuxLevels; - - /** - * Creates a {@code HysteresisLevels} object with the given equal-length - * integer arrays. - * @param brightLevels an array of brightening hysteresis constraint constants - * @param darkLevels an array of darkening hysteresis constraint constants - * @param luxLevels a monotonically increasing array of illuminance - * thresholds in units of lux - */ - public HysteresisLevels(int[] brightLevels, int[] darkLevels, int[] luxLevels) { - if (brightLevels.length != darkLevels.length || darkLevels.length != luxLevels.length + 1) { - throw new IllegalArgumentException("Mismatch between hysteresis array lengths."); - } - mBrightLevels = setArrayFormat(brightLevels, 1000.0f); - mDarkLevels = setArrayFormat(darkLevels, 1000.0f); - mLuxLevels = setArrayFormat(luxLevels, 1.0f); - } - - /** - * Return the brightening hysteresis threshold for the given lux level. - */ - public float getBrighteningThreshold(float lux) { - float brightConstant = getReferenceLevel(lux, mBrightLevels); - float brightThreshold = lux * (1.0f + brightConstant); - if (DEBUG) { - Slog.d(TAG, "bright hysteresis constant=: " + brightConstant + ", threshold=" - + brightThreshold + ", lux=" + lux); - } - return brightThreshold; - } - - /** - * Return the darkening hysteresis threshold for the given lux level. - */ - public float getDarkeningThreshold(float lux) { - float darkConstant = getReferenceLevel(lux, mDarkLevels); - float darkThreshold = lux * (1.0f - darkConstant); - if (DEBUG) { - Slog.d(TAG, "dark hysteresis constant=: " + darkConstant + ", threshold=" - + darkThreshold + ", lux=" + lux); - } - return darkThreshold; - } - - /** - * Return the hysteresis constant for the closest lux threshold value to the - * current illuminance from the given array. - */ - private float getReferenceLevel(float lux, float[] referenceLevels) { - int index = 0; - while (mLuxLevels.length > index && lux >= mLuxLevels[index]) { - ++index; - } - return referenceLevels[index]; - } - - /** - * Return a float array where each i-th element equals {@code configArray[i]/divideFactor}. - */ - private float[] setArrayFormat(int[] configArray, float divideFactor) { - float[] levelArray = new float[configArray.length]; - for (int index = 0; levelArray.length > index; ++index) { - levelArray[index] = (float)configArray[index] / divideFactor; - } - return levelArray; - } -} diff --git a/services/core/java/com/android/server/display/LuxLevels.java b/services/core/java/com/android/server/display/LuxLevels.java new file mode 100644 index 0000000000000..a13626d8f820c --- /dev/null +++ b/services/core/java/com/android/server/display/LuxLevels.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2016 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.util.Slog; + +/** + * A helper class for handling access to illuminance level values. + */ +final class LuxLevels { + private static final String TAG = "LuxLevels"; + + private static final boolean DEBUG = true; + + private final boolean mUseNewSensorSamplesForDoze; + + private final float[] mBrightLevels; + private final float[] mDarkLevels; + private final float[] mLuxHysteresisLevels; + private final float[] mDozeBacklightLevels; + private final float[] mDozeSensorLuxLevels; + + /** + * Creates a {@code LuxLevels} object with the given integer arrays. The following arrays + * are either empty or have the following relations: + * {@code brightLevels} and {@code darkLevels} have the same length n. + * {@code luxLevels} has length n+1. + * + * {@code dozeSensorLuxLevels} has length r. + * {@code dozeBacklightLevels} has length r+1. + * + * @param brightLevels an array of brightening hysteresis constraint constants + * @param darkLevels an array of darkening hysteresis constraint constants + * @param luxHysteresisLevels a monotonically increasing array of illuminance thresholds in lux + * @param dozeSensorLuxLevels a monotonically increasing array of ALS thresholds in lux + * @param dozeBacklightLevels an array of screen brightness values for doze mode in lux + */ + public LuxLevels(int[] brightLevels, int[] darkLevels, int[] luxHysteresisLevels, + boolean useNewSensorSamplesForDoze, int[] dozeSensorLuxLevels, + int[] dozeBacklightLevels) { + if (brightLevels.length != darkLevels.length || + darkLevels.length !=luxHysteresisLevels.length + 1) { + throw new IllegalArgumentException("Mismatch between hysteresis array lengths."); + } + if (dozeBacklightLevels.length > 0 && dozeSensorLuxLevels.length > 0 + && dozeBacklightLevels.length != dozeSensorLuxLevels.length + 1) { + throw new IllegalArgumentException("Mismatch between doze lux array lengths."); + } + mBrightLevels = setArrayFormat(brightLevels, 1000.0f); + mDarkLevels = setArrayFormat(darkLevels, 1000.0f); + mLuxHysteresisLevels = setArrayFormat(luxHysteresisLevels, 1.0f); + mUseNewSensorSamplesForDoze = useNewSensorSamplesForDoze; + mDozeSensorLuxLevels = setArrayFormat(dozeSensorLuxLevels, 1.0f); + mDozeBacklightLevels = setArrayFormat(dozeBacklightLevels, 1.0f); + } + + /** + * Return the brightening hysteresis threshold for the given lux level. + */ + public float getBrighteningThreshold(float lux) { + float brightConstant = getReferenceLevel(lux, mBrightLevels, mLuxHysteresisLevels); + float brightThreshold = lux * (1.0f + brightConstant); + if (DEBUG) { + Slog.d(TAG, "bright hysteresis constant= " + brightConstant + ", threshold=" + + brightThreshold + ", lux=" + lux); + } + return brightThreshold; + } + + /** + * Return the darkening hysteresis threshold for the given lux level. + */ + public float getDarkeningThreshold(float lux) { + float darkConstant = getReferenceLevel(lux, mDarkLevels, mLuxHysteresisLevels); + float darkThreshold = lux * (1.0f - darkConstant); + if (DEBUG) { + Slog.d(TAG, "dark hysteresis constant= " + darkConstant + ", threshold=" + + darkThreshold + ", lux=" + lux); + } + return darkThreshold; + } + + /** + * Return the doze backlight brightness level for the given ambient sensor lux level. + */ + public int getDozeBrightness(float lux) { + int dozeBrightness = (int) getReferenceLevel(lux, mDozeBacklightLevels, + mDozeSensorLuxLevels); + if (DEBUG) { + Slog.d(TAG, "doze brightness: " + dozeBrightness + ", lux=" + lux); + } + return dozeBrightness; + } + + /** + * Find the index of the closest value in {@code thresholdLevels} to {@code lux} and return + * the {@code referenceLevels} entry with that index. + */ + private float getReferenceLevel(float lux, float[] referenceLevels, float[] thresholdLevels) { + int index = 0; + while (thresholdLevels.length > index && lux >= thresholdLevels[index]) { + ++index; + } + return referenceLevels[index]; + } + + /** + * Return if the doze backlight brightness level is specified dynamically. + */ + public boolean hasDynamicDozeBrightness() { + return mDozeSensorLuxLevels.length > 0; + } + + /** + * Return if new ALS samples should be used for determining screen brightness while dozing. + */ + public boolean useNewSensorSamplesForDoze() { + return mUseNewSensorSamplesForDoze; + } + + /** + * Return a float array where each i-th element equals {@code configArray[i]/divideFactor}. + */ + private float[] setArrayFormat(int[] configArray, float divideFactor) { + float[] levelArray = new float[configArray.length]; + for (int index = 0; levelArray.length > index; ++index) { + levelArray[index] = (float)configArray[index] / divideFactor; + } + return levelArray; + } +}