Merge "Add an API for setting a new brightness curve."
This commit is contained in:
committed by
Android (Google) Code Review
commit
2c2ce763b7
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.hardware.display;
|
||||
|
||||
parcelable BrightnessConfiguration;
|
||||
175
core/java/android/hardware/display/BrightnessConfiguration.java
Normal file
175
core/java/android/hardware/display/BrightnessConfiguration.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.hardware.display;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** @hide */
|
||||
public final class BrightnessConfiguration implements Parcelable {
|
||||
private final float[] mLux;
|
||||
private final float[] mNits;
|
||||
|
||||
private BrightnessConfiguration(float[] lux, float[] nits) {
|
||||
mLux = lux;
|
||||
mNits = nits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base brightness as curve.
|
||||
*
|
||||
* The curve is returned as a pair of float arrays, the first representing all of the lux
|
||||
* points of the brightness curve and the second representing all of the nits values of the
|
||||
* brightness curve.
|
||||
*
|
||||
* @return the control points for the brightness curve.
|
||||
*/
|
||||
public Pair<float[], float[]> getCurve() {
|
||||
return Pair.create(Arrays.copyOf(mLux, mLux.length), Arrays.copyOf(mNits, mNits.length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeFloatArray(mLux);
|
||||
dest.writeFloatArray(mNits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("BrightnessConfiguration{[");
|
||||
final int size = mLux.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
|
||||
}
|
||||
sb.append("]}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = result * 31 + Arrays.hashCode(mLux);
|
||||
result = result * 31 + Arrays.hashCode(mNits);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof BrightnessConfiguration)) {
|
||||
return false;
|
||||
}
|
||||
final BrightnessConfiguration other = (BrightnessConfiguration) o;
|
||||
return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits);
|
||||
}
|
||||
|
||||
public static final Creator<BrightnessConfiguration> CREATOR =
|
||||
new Creator<BrightnessConfiguration>() {
|
||||
public BrightnessConfiguration createFromParcel(Parcel in) {
|
||||
Builder builder = new Builder();
|
||||
float[] lux = in.createFloatArray();
|
||||
float[] nits = in.createFloatArray();
|
||||
builder.setCurve(lux, nits);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public BrightnessConfiguration[] newArray(int size) {
|
||||
return new BrightnessConfiguration[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A builder class for {@link BrightnessConfiguration}s.
|
||||
*/
|
||||
public static class Builder {
|
||||
private float[] mCurveLux;
|
||||
private float[] mCurveNits;
|
||||
|
||||
/**
|
||||
* Sets the control points for the brightness curve.
|
||||
*
|
||||
* Brightness curves must have strictly increasing ambient brightness values in lux and
|
||||
* monotonically increasing display brightness values in nits. In addition, the initial
|
||||
* control point must be 0 lux.
|
||||
*
|
||||
* @throws IllegalArgumentException if the initial control point is not at 0 lux.
|
||||
* @throws IllegalArgumentException if the lux levels are not strictly increasing.
|
||||
* @throws IllegalArgumentException if the nit levels are not monotonically increasing.
|
||||
*/
|
||||
public Builder setCurve(float[] lux, float[] nits) {
|
||||
Preconditions.checkNotNull(lux);
|
||||
Preconditions.checkNotNull(nits);
|
||||
if (lux.length == 0 || nits.length == 0) {
|
||||
throw new IllegalArgumentException("Lux and nits arrays must not be empty");
|
||||
}
|
||||
if (lux.length != nits.length) {
|
||||
throw new IllegalArgumentException("Lux and nits arrays must be the same length");
|
||||
}
|
||||
if (lux[0] != 0) {
|
||||
throw new IllegalArgumentException("Initial control point must be for 0 lux");
|
||||
}
|
||||
Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux");
|
||||
Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
|
||||
checkMonotonic(lux, true/*strictly increasing*/, "lux");
|
||||
checkMonotonic(nits, false /*strictly increasing*/, "nits");
|
||||
mCurveLux = lux;
|
||||
mCurveNits = nits;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link BrightnessConfiguration}.
|
||||
*
|
||||
* A brightness curve <b>must</b> be set before calling this.
|
||||
*/
|
||||
public BrightnessConfiguration build() {
|
||||
if (mCurveLux == null || mCurveNits == null) {
|
||||
throw new IllegalStateException("A curve must be set!");
|
||||
}
|
||||
return new BrightnessConfiguration(mCurveLux, mCurveNits);
|
||||
}
|
||||
|
||||
private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
|
||||
if (vals.length <= 1) {
|
||||
return;
|
||||
}
|
||||
float prev = vals[0];
|
||||
for (int i = 1; i < vals.length; i++) {
|
||||
if (prev > vals[i] || prev == vals[i] && strictlyIncreasing) {
|
||||
String condition = strictlyIncreasing ? "strictly increasing" : "monotonic";
|
||||
throw new IllegalArgumentException(name + " values must be " + condition);
|
||||
}
|
||||
prev = vals[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.media.projection.MediaProjection;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
@@ -633,6 +634,27 @@ public final class DisplayManager {
|
||||
mGlobal.setBrightness(brightness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the global display brightness configuration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setBrightnessConfiguration(BrightnessConfiguration c) {
|
||||
setBrightnessConfigurationForUser(c, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the global display brightness configuration for a specific user.
|
||||
*
|
||||
* Note this requires the INTERACT_ACROSS_USERS permission if setting the configuration for a
|
||||
* user other than the one you're currently running as.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) {
|
||||
mGlobal.setBrightnessConfigurationForUser(c, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for changes in available display devices.
|
||||
*/
|
||||
|
||||
@@ -487,6 +487,19 @@ public final class DisplayManagerGlobal {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the global brightness configuration for a given user.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) {
|
||||
try {
|
||||
mDm.setBrightnessConfigurationForUser(c, userId);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
|
||||
@Override
|
||||
public void onDisplayEvent(int displayId, int event) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.hardware.display;
|
||||
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.hardware.display.IDisplayManagerCallback;
|
||||
import android.hardware.display.IVirtualDisplayCallback;
|
||||
import android.hardware.display.WifiDisplay;
|
||||
@@ -89,4 +90,9 @@ interface IDisplayManager {
|
||||
// STOPSHIP remove when adaptive brightness code is updated to accept curves.
|
||||
// Requires BRIGHTNESS_SLIDER_USAGE permission.
|
||||
void setBrightness(int brightness);
|
||||
|
||||
// Sets the global brightness configuration for a given user. Requires
|
||||
// CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not
|
||||
// the same as the calling user.
|
||||
void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId);
|
||||
}
|
||||
|
||||
@@ -494,4 +494,38 @@ public class Preconditions {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that all elements in the argument integer array are within the inclusive range
|
||||
*
|
||||
* @param value an integer array of values
|
||||
* @param lower the lower endpoint of the inclusive range
|
||||
* @param upper the upper endpoint of the inclusive range
|
||||
* @param valueName the name of the argument to use if the check fails
|
||||
*
|
||||
* @return the validated integer array
|
||||
*
|
||||
* @throws IllegalArgumentException if any of the elements in {@code value} were out of range
|
||||
* @throws NullPointerException if the {@code value} was {@code null}
|
||||
*/
|
||||
public static int[] checkArrayElementsInRange(int[] value, int lower, int upper,
|
||||
String valueName) {
|
||||
checkNotNull(value, valueName + " must not be null");
|
||||
|
||||
for (int i = 0; i < value.length; ++i) {
|
||||
int v = value[i];
|
||||
|
||||
if (v < lower) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("%s[%d] is out of range of [%d, %d] (too low)",
|
||||
valueName, i, lower, upper));
|
||||
} else if (v > upper) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("%s[%d] is out of range of [%d, %d] (too high)",
|
||||
valueName, i, lower, upper));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2924,6 +2924,11 @@
|
||||
<permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- Allows an application to modify the display brightness configuration
|
||||
@hide -->
|
||||
<permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"
|
||||
android:protectionLevel="signature|privileged|development" />
|
||||
|
||||
<!-- @SystemApi Allows an application to control VPN.
|
||||
<p>Not for use by third-party applications.</p>
|
||||
@hide -->
|
||||
|
||||
@@ -1295,6 +1295,22 @@
|
||||
<integer-array name="config_autoBrightnessLcdBacklightValues">
|
||||
</integer-array>
|
||||
|
||||
<!-- Array of desired screen brightness in nits corresponding to the lux values
|
||||
in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and
|
||||
config_screenBrightnessMaximumNits, the display brightness is defined as the measured
|
||||
brightness of an all-white image.
|
||||
|
||||
If this is defined then:
|
||||
- config_autoBrightnessLcdBacklightValues should not be defined
|
||||
- config_screenBrightnessMinimumNits must be defined
|
||||
- config_screenBrightnessMaximumNits must be defined
|
||||
|
||||
This array should have size one greater than the size of the config_autoBrightnessLevels
|
||||
array. The brightness values must be non-negative and non-decreasing. This must be
|
||||
overridden in platform specific overlays -->
|
||||
<array name="config_autoBrightnessDisplayValuesNits">
|
||||
</array>
|
||||
|
||||
<!-- Array of output values for button backlight corresponding to the LUX values
|
||||
in the config_autoBrightnessLevels array. This array should have size one greater
|
||||
than the size of the config_autoBrightnessLevels array.
|
||||
@@ -1331,6 +1347,29 @@
|
||||
<item>200</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- The minimum brightness of the display in nits. On OLED displays this should be measured
|
||||
with an all white image while the display is fully on and the backlight is set to
|
||||
config_screenBrightnessSettingMinimum or config_screenBrightnessSettingDark, whichever
|
||||
is darker.
|
||||
|
||||
If this and config_screenBrightnessMinimumNits are set, then the display's brightness
|
||||
range is assumed to be linear between
|
||||
(config_screenBrightnessSettingMinimum, config_screenBrightnessMinimumNits) and
|
||||
(config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). -->
|
||||
<item name="config_screenBrightnessMinimumNits" format="float" type="dimen">-1.0</item>
|
||||
|
||||
<!-- The maximum brightness of the display in nits. On OLED displays this should be measured
|
||||
with an all white image while the display is fully on and the "backlight" is set to
|
||||
config_screenBrightnessSettingMaximum. Note that this value should *not* reflect the
|
||||
maximum brightness value for any high brightness modes but only the maximum brightness
|
||||
value obtainable in a sustainable manner.
|
||||
|
||||
If this and config_screenBrightnessMinimumNits are set to something non-negative, then the
|
||||
display's brightness range is assumed to be linear between
|
||||
(config_screenBrightnessSettingMinimum, config_screenBrightnessMaximumNits) and
|
||||
(config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). -->
|
||||
<item name="config_screenBrightnessMaximumNits" format="float" type="dimen">-1.0</item>
|
||||
|
||||
<!-- Array of ambient lux threshold values. This is used for determining hysteresis constraint
|
||||
values by calculating the index to use for lookup and then setting the constraint value
|
||||
to the corresponding value of the array. The new brightening hysteresis constraint value
|
||||
|
||||
@@ -3185,4 +3185,8 @@
|
||||
|
||||
<java-symbol type="string" name="global_action_logout" />
|
||||
<java-symbol type="drawable" name="ic_logout" />
|
||||
|
||||
<java-symbol type="dimen" name="config_screenBrightnessMinimumNits" />
|
||||
<java-symbol type="dimen" name="config_screenBrightnessMaximumNits" />
|
||||
<java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" />
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.hardware.display;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BrightnessConfigurationTest {
|
||||
private static final float[] LUX_LEVELS = {
|
||||
0f,
|
||||
10f,
|
||||
100f,
|
||||
};
|
||||
|
||||
private static final float[] NITS_LEVELS = {
|
||||
0.5f,
|
||||
90f,
|
||||
100f,
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testSetCurveIsUnmodified() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(LUX_LEVELS, NITS_LEVELS);
|
||||
BrightnessConfiguration config = builder.build();
|
||||
Pair<float[], float[]> curve = config.getCurve();
|
||||
assertArrayEquals(LUX_LEVELS, curve.first, "lux");
|
||||
assertArrayEquals(NITS_LEVELS, curve.second, "nits");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCurveMustHaveZeroLuxPoint() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
|
||||
lux[0] = 1f;
|
||||
builder.setCurve(lux, NITS_LEVELS);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testCurveMustBeSet() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.build();
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testCurveMustNotHaveNullArrays() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(null, null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCurveMustNotHaveEmptyArrays() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(new float[0], new float[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurveMustNotHaveArraysOfDifferentLengths() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length + 1);
|
||||
lux[lux.length - 1] = lux[lux.length - 2] + 1;
|
||||
boolean exceptionThrown = false;
|
||||
builder.setCurve(lux, NITS_LEVELS);
|
||||
});
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length + 1);
|
||||
nits[nits.length - 1] = nits[nits.length - 2] + 1;
|
||||
builder.setCurve(LUX_LEVELS, nits);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurvesMustNotContainNaN() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
|
||||
lux[lux.length - 1] = Float.NaN;
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(lux, NITS_LEVELS);
|
||||
});
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length);
|
||||
nits[nits.length - 1] = Float.NaN;
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(LUX_LEVELS, nits);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParceledConfigIsEquivalent() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(LUX_LEVELS, NITS_LEVELS);
|
||||
BrightnessConfiguration config = builder.build();
|
||||
Parcel p = Parcel.obtain();
|
||||
p.writeParcelable(config, 0 /*flags*/);
|
||||
p.setDataPosition(0);
|
||||
BrightnessConfiguration newConfig =
|
||||
p.readParcelable(BrightnessConfiguration.class.getClassLoader());
|
||||
assertEquals(config, newConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(LUX_LEVELS, NITS_LEVELS);
|
||||
BrightnessConfiguration baseConfig = builder.build();
|
||||
|
||||
builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(LUX_LEVELS, NITS_LEVELS);
|
||||
BrightnessConfiguration identicalConfig = builder.build();
|
||||
assertEquals(baseConfig, identicalConfig);
|
||||
assertEquals("hashCodes must be equal for identical configs",
|
||||
baseConfig.hashCode(), identicalConfig.hashCode());
|
||||
|
||||
float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
|
||||
lux[lux.length - 1] = lux[lux.length - 1] * 2;
|
||||
builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(lux, NITS_LEVELS);
|
||||
BrightnessConfiguration luxDifferConfig = builder.build();
|
||||
assertNotEquals(baseConfig, luxDifferConfig);
|
||||
|
||||
float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length);
|
||||
nits[nits.length - 1] = nits[nits.length - 1] * 2;
|
||||
builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(LUX_LEVELS, nits);
|
||||
BrightnessConfiguration nitsDifferConfig = builder.build();
|
||||
assertNotEquals(baseConfig, nitsDifferConfig);
|
||||
}
|
||||
|
||||
private static void assertArrayEquals(float[] expected, float[] actual, String name) {
|
||||
assertEquals("Expected " + name + " arrays to be the same length!",
|
||||
expected.length, actual.length);
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals("Expected " + name + " arrays to be equivalent when value " + i
|
||||
+ "differs", expected[i], actual[i], 0.01 /*tolerance*/);
|
||||
}
|
||||
}
|
||||
|
||||
private interface ExceptionRunnable {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
private static void assertThrows(Class<? extends Throwable> exceptionClass,
|
||||
ExceptionRunnable r) {
|
||||
try {
|
||||
r.run();
|
||||
} catch (Throwable e) {
|
||||
assertTrue("Expected exception type " + exceptionClass.getName() + " but got "
|
||||
+ e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass()));
|
||||
return;
|
||||
}
|
||||
fail("Expected exception type " + exceptionClass.getName()
|
||||
+ ", but no exception was thrown");
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
@@ -34,7 +35,6 @@ import android.text.format.DateUtils;
|
||||
import android.util.EventLog;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Slog;
|
||||
import android.util.Spline;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -76,9 +76,8 @@ class AutomaticBrightnessController {
|
||||
// The light sensor, or null if not available or needed.
|
||||
private final Sensor mLightSensor;
|
||||
|
||||
// The auto-brightness spline adjustment.
|
||||
// The brightness values have been scaled to a range of 0..1.
|
||||
private final Spline mScreenAutoBrightnessSpline;
|
||||
// The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
|
||||
private final BrightnessMappingStrategy mBrightnessMapper;
|
||||
|
||||
// The minimum and maximum screen brightnesses.
|
||||
private final int mScreenBrightnessRangeMinimum;
|
||||
@@ -186,7 +185,7 @@ class AutomaticBrightnessController {
|
||||
private float mBrightnessAdjustmentSampleOldGamma;
|
||||
|
||||
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
|
||||
SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
|
||||
SensorManager sensorManager, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime,
|
||||
int brightnessMin, int brightnessMax, float dozeScaleFactor,
|
||||
int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
|
||||
long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
|
||||
@@ -194,7 +193,7 @@ class AutomaticBrightnessController {
|
||||
HysteresisLevels dynamicHysteresis) {
|
||||
mCallbacks = callbacks;
|
||||
mSensorManager = sensorManager;
|
||||
mScreenAutoBrightnessSpline = autoBrightnessSpline;
|
||||
mBrightnessMapper = mapper;
|
||||
mScreenBrightnessRangeMinimum = brightnessMin;
|
||||
mScreenBrightnessRangeMaximum = brightnessMax;
|
||||
mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
|
||||
@@ -228,15 +227,16 @@ class AutomaticBrightnessController {
|
||||
return mScreenAutoBrightness;
|
||||
}
|
||||
|
||||
public void configure(boolean enable, float adjustment, boolean dozing,
|
||||
boolean userInitiatedChange) {
|
||||
public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
|
||||
float adjustment, boolean dozing, boolean userInitiatedChange) {
|
||||
// While dozing, the application processor may be suspended which will prevent us from
|
||||
// receiving new information from the light sensor. On some devices, we may be able to
|
||||
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
|
||||
// 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 changed = setBrightnessConfiguration(configuration);
|
||||
changed |= setLightSensorEnabled(enable && !dozing);
|
||||
if (enable && !dozing && userInitiatedChange) {
|
||||
prepareBrightnessAdjustmentSample();
|
||||
}
|
||||
@@ -246,10 +246,13 @@ class AutomaticBrightnessController {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
|
||||
return mBrightnessMapper.setBrightnessConfiguration(configuration);
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println();
|
||||
pw.println("Automatic Brightness Controller Configuration:");
|
||||
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
|
||||
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
|
||||
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
|
||||
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
|
||||
@@ -274,9 +277,13 @@ class AutomaticBrightnessController {
|
||||
mInitialHorizonAmbientLightRingBuffer);
|
||||
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
|
||||
pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
|
||||
pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
|
||||
pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma="
|
||||
+ mScreenAutoBrightnessAdjustmentMaxGamma);
|
||||
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
|
||||
pw.println(" mDozing=" + mDozing);
|
||||
|
||||
pw.println();
|
||||
mBrightnessMapper.dump(pw);
|
||||
}
|
||||
|
||||
private boolean setLightSensorEnabled(boolean enable) {
|
||||
@@ -533,7 +540,7 @@ class AutomaticBrightnessController {
|
||||
return;
|
||||
}
|
||||
|
||||
float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
|
||||
float value = mBrightnessMapper.getBrightness(mAmbientLux);
|
||||
float gamma = 1.0f;
|
||||
|
||||
if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.display.BrightnessConfiguration;
|
||||
import android.os.PowerManager;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.Spline;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A utility to map from an ambient brightness to a display's "backlight" brightness based on the
|
||||
* available display information and brightness configuration.
|
||||
*
|
||||
* Note that without a mapping from the nits to a display backlight level, any
|
||||
* {@link BrightnessConfiguration}s that are set are just ignored.
|
||||
*/
|
||||
public abstract class BrightnessMappingStrategy {
|
||||
private static final String TAG = "BrightnessMappingStrategy";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@Nullable
|
||||
public static BrightnessMappingStrategy create(
|
||||
float[] luxLevels, int[] brightnessLevelsBacklight, float[] brightnessLevelsNits,
|
||||
float[] nitsRange, int[] backlightRange) {
|
||||
if (isValidMapping(nitsRange, backlightRange)
|
||||
&& isValidMapping(luxLevels, brightnessLevelsNits)) {
|
||||
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
builder.setCurve(luxLevels, brightnessLevelsNits);
|
||||
return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
|
||||
} else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
|
||||
return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidMapping(float[] x, float[] y) {
|
||||
if (x == null || y == null || x.length == 0 || y.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (x.length != y.length) {
|
||||
return false;
|
||||
}
|
||||
final int N = x.length;
|
||||
float prevX = x[0];
|
||||
float prevY = y[0];
|
||||
if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < N; i++) {
|
||||
if (prevX >= x[i] || prevY > y[i]) {
|
||||
return false;
|
||||
}
|
||||
if (Float.isNaN(x[i]) || Float.isNaN(y[i])) {
|
||||
return false;
|
||||
}
|
||||
prevX = x[i];
|
||||
prevY = y[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isValidMapping(float[] x, int[] y) {
|
||||
if (x == null || y == null || x.length == 0 || y.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (x.length != y.length) {
|
||||
return false;
|
||||
}
|
||||
final int N = x.length;
|
||||
float prevX = x[0];
|
||||
int prevY = y[0];
|
||||
if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < N; i++) {
|
||||
if (prevX >= x[i] || prevY > y[i]) {
|
||||
return false;
|
||||
}
|
||||
if (Float.isNaN(x[i])) {
|
||||
return false;
|
||||
}
|
||||
prevX = x[i];
|
||||
prevY = y[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link BrightnessConfiguration}.
|
||||
*
|
||||
* @param config The new configuration. If {@code null} is passed, the default configuration is
|
||||
* used.
|
||||
* @return Whether the brightness configuration has changed.
|
||||
*/
|
||||
public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
|
||||
|
||||
/**
|
||||
* Returns the desired brightness of the display based on the current ambient lux.
|
||||
*
|
||||
* The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
|
||||
* brightness and 0 is the display at minimum brightness.
|
||||
*
|
||||
* @param lux The current ambient brightness in lux.
|
||||
* @return The desired brightness of the display compressed to the range [0, 1.0].
|
||||
*/
|
||||
public abstract float getBrightness(float lux);
|
||||
|
||||
public abstract void dump(PrintWriter pw);
|
||||
|
||||
private static float normalizeAbsoluteBrightness(int brightness) {
|
||||
brightness = MathUtils.constrain(brightness,
|
||||
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
|
||||
return (float) brightness / PowerManager.BRIGHTNESS_ON;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the
|
||||
* backlight of the display.
|
||||
*
|
||||
* Since we don't have information about the display's physical brightness, any brightness
|
||||
* configurations that are set are just ignored.
|
||||
*/
|
||||
private static class SimpleMappingStrategy extends BrightnessMappingStrategy {
|
||||
private final Spline mSpline;
|
||||
|
||||
public SimpleMappingStrategy(float[] lux, int[] brightness) {
|
||||
Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
|
||||
"Lux and brightness arrays must not be empty!");
|
||||
Preconditions.checkArgument(lux.length == brightness.length,
|
||||
"Lux and brightness arrays must be the same length!");
|
||||
Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux");
|
||||
Preconditions.checkArrayElementsInRange(brightness,
|
||||
0, Integer.MAX_VALUE, "brightness");
|
||||
|
||||
final int N = brightness.length;
|
||||
float[] x = new float[N];
|
||||
float[] y = new float[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
x[i] = lux[i];
|
||||
y[i] = normalizeAbsoluteBrightness(brightness[i]);
|
||||
}
|
||||
|
||||
mSpline = Spline.createSpline(x, y);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Auto-brightness spline: " + mSpline);
|
||||
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
|
||||
Slog.d(TAG, String.format(" %7.1f: %7.1f", v, mSpline.interpolate(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
|
||||
Slog.e(TAG,
|
||||
"setBrightnessConfiguration called on device without display information.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBrightness(float lux) {
|
||||
return mSpline.interpolate(lux);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println("SimpleMappingStrategy");
|
||||
pw.println(" mSpline=" + mSpline);
|
||||
}
|
||||
}
|
||||
|
||||
/** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical
|
||||
* range of the display, rather than to the range of the backlight control (typically 0-255).
|
||||
*
|
||||
* By mapping through the physical brightness, the curve becomes portable across devices and
|
||||
* gives us more resolution in the resulting mapping.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static class PhysicalMappingStrategy extends BrightnessMappingStrategy {
|
||||
// The current brightness configuration.
|
||||
private BrightnessConfiguration mConfig;
|
||||
|
||||
// A spline mapping from the current ambient light in lux to the desired display brightness
|
||||
// in nits.
|
||||
private Spline mBrightnessSpline;
|
||||
|
||||
// A spline mapping from nits to the corresponding backlight value, normalized to the range
|
||||
// [0, 1.0].
|
||||
private final Spline mBacklightSpline;
|
||||
|
||||
// The default brightness configuration.
|
||||
private final BrightnessConfiguration mDefaultConfig;
|
||||
|
||||
public PhysicalMappingStrategy(BrightnessConfiguration config,
|
||||
float[] nits, int[] backlight) {
|
||||
Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
|
||||
"Nits and backlight arrays must not be empty!");
|
||||
Preconditions.checkArgument(nits.length == backlight.length,
|
||||
"Nits and backlight arrays must be the same length!");
|
||||
Preconditions.checkNotNull(config);
|
||||
Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
|
||||
Preconditions.checkArrayElementsInRange(backlight,
|
||||
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight");
|
||||
|
||||
// Setup the backlight spline
|
||||
final int N = nits.length;
|
||||
float[] x = new float[N];
|
||||
float[] y = new float[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
x[i] = nits[i];
|
||||
y[i] = normalizeAbsoluteBrightness(backlight[i]);
|
||||
}
|
||||
|
||||
mBacklightSpline = Spline.createSpline(x, y);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Backlight spline: " + mBacklightSpline);
|
||||
for (float v = 1f; v < nits[nits.length - 1] * 1.25f; v *= 1.25f) {
|
||||
Slog.d(TAG, String.format(
|
||||
" %7.1f: %7.1f", v, mBacklightSpline.interpolate(v)));
|
||||
}
|
||||
}
|
||||
|
||||
mDefaultConfig = config;
|
||||
setBrightnessConfiguration(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
|
||||
if (config == null) {
|
||||
config = mDefaultConfig;
|
||||
}
|
||||
if (config.equals(mConfig)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Tried to set an identical brightness config, ignoring");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Pair<float[], float[]> curve = config.getCurve();
|
||||
mBrightnessSpline = Spline.createSpline(curve.first /*lux*/, curve.second /*nits*/);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Brightness spline: " + mBrightnessSpline);
|
||||
final float[] lux = curve.first;
|
||||
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
|
||||
Slog.d(TAG, String.format(
|
||||
" %7.1f: %7.1f", v, mBrightnessSpline.interpolate(v)));
|
||||
}
|
||||
}
|
||||
mConfig = config;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBrightness(float lux) {
|
||||
return mBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.println("PhysicalMappingStrategy");
|
||||
pw.println(" mConfig=" + mConfig);
|
||||
pw.println(" mBrightnessSpline=" + mBrightnessSpline);
|
||||
pw.println(" mBacklightSpline=" + mBacklightSpline);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -37,6 +38,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.display.BrightnessChangeEvent;
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.display.DisplayViewport;
|
||||
@@ -62,6 +64,7 @@ import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.IntArray;
|
||||
import android.util.Slog;
|
||||
@@ -70,6 +73,7 @@ import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.AnimationThread;
|
||||
import com.android.server.DisplayThread;
|
||||
import com.android.server.LocalServices;
|
||||
@@ -145,6 +149,7 @@ public final class DisplayManagerService extends SystemService {
|
||||
private static final int MSG_REQUEST_TRAVERSAL = 4;
|
||||
private static final int MSG_UPDATE_VIEWPORT = 5;
|
||||
private static final int MSG_REGISTER_BRIGHTNESS_TRACKER = 6;
|
||||
private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 7;
|
||||
|
||||
private final Context mContext;
|
||||
private final DisplayManagerHandler mHandler;
|
||||
@@ -219,6 +224,9 @@ public final class DisplayManagerService extends SystemService {
|
||||
// The virtual display adapter, or null if not registered.
|
||||
private VirtualDisplayAdapter mVirtualDisplayAdapter;
|
||||
|
||||
// The User ID of the current user
|
||||
private @UserIdInt int mCurrentUserId;
|
||||
|
||||
// The stable device screen height and width. These are not tied to a specific display, even
|
||||
// the default display, because they need to be stable over the course of the device's entire
|
||||
// life, even if the default display changes (e.g. a new monitor is plugged into a PC-like
|
||||
@@ -278,17 +286,18 @@ public final class DisplayManagerService extends SystemService {
|
||||
mDisplayAdapterListener = new DisplayAdapterListener();
|
||||
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
|
||||
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
|
||||
com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
|
||||
|
||||
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
|
||||
mBrightnessTracker = new BrightnessTracker(context, null);
|
||||
mCurrentUserId = UserHandle.USER_SYSTEM;
|
||||
}
|
||||
|
||||
public void setupSchedulerPolicies() {
|
||||
// android.display and android.anim is critical to user experience and we should make sure
|
||||
// it is not in the default foregroup groups, add it to top-app to make sure it uses all the
|
||||
// cores and scheduling settings for top-app when it runs.
|
||||
// it is not in the default foregroup groups, add it to top-app to make sure it uses all
|
||||
// the cores and scheduling settings for top-app when it runs.
|
||||
Process.setThreadGroupAndCpuset(DisplayThread.get().getThreadId(),
|
||||
Process.THREAD_GROUP_TOP_APP);
|
||||
Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(),
|
||||
@@ -342,6 +351,19 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchUser(@UserIdInt int newUserId) {
|
||||
final int userSerial = getUserManager().getUserSerialNumber(newUserId);
|
||||
synchronized (mSyncRoot) {
|
||||
if (mCurrentUserId != newUserId) {
|
||||
mCurrentUserId = newUserId;
|
||||
BrightnessConfiguration config =
|
||||
mPersistentDataStore.getBrightnessConfiguration(userSerial);
|
||||
mDisplayPowerController.setBrightnessConfiguration(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use dependencies or a boot phase
|
||||
public void windowManagerAndInputReady() {
|
||||
synchronized (mSyncRoot) {
|
||||
@@ -985,6 +1007,30 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void setBrightnessConfigurationForUserInternal(
|
||||
@NonNull BrightnessConfiguration c, @UserIdInt int userId) {
|
||||
final int userSerial = getUserManager().getUserSerialNumber(userId);
|
||||
synchronized (mSyncRoot) {
|
||||
try {
|
||||
mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial);
|
||||
} finally {
|
||||
mPersistentDataStore.saveIfNeeded();
|
||||
}
|
||||
if (userId == mCurrentUserId) {
|
||||
mDisplayPowerController.setBrightnessConfiguration(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBrightnessConfiguration() {
|
||||
synchronized (mSyncRoot) {
|
||||
final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId);
|
||||
BrightnessConfiguration config =
|
||||
mPersistentDataStore.getBrightnessConfiguration(userSerial);
|
||||
mDisplayPowerController.setBrightnessConfiguration(config);
|
||||
}
|
||||
}
|
||||
|
||||
// Updates all existing logical displays given the current set of display devices.
|
||||
// Removes invalid logical displays.
|
||||
// Sends notifications if needed.
|
||||
@@ -1229,6 +1275,10 @@ public final class DisplayManagerService extends SystemService {
|
||||
return mProjectionService;
|
||||
}
|
||||
|
||||
private UserManager getUserManager() {
|
||||
return mContext.getSystemService(UserManager.class);
|
||||
}
|
||||
|
||||
private void dumpInternal(PrintWriter pw) {
|
||||
pw.println("DISPLAY MANAGER (dumpsys display)");
|
||||
|
||||
@@ -1371,6 +1421,10 @@ public final class DisplayManagerService extends SystemService {
|
||||
case MSG_REGISTER_BRIGHTNESS_TRACKER:
|
||||
mBrightnessTracker.start();
|
||||
break;
|
||||
|
||||
case MSG_LOAD_BRIGHTNESS_CONFIGURATION:
|
||||
loadBrightnessConfiguration();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1800,6 +1854,27 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void setBrightnessConfigurationForUser(
|
||||
BrightnessConfiguration c, @UserIdInt int userId) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
|
||||
"Permission required to change the display's brightness configuration");
|
||||
if (userId != UserHandle.getCallingUserId()) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
Manifest.permission.INTERACT_ACROSS_USERS,
|
||||
"Permission required to change the display brightness"
|
||||
+ " configuration of another user");
|
||||
}
|
||||
Preconditions.checkNotNull(c);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
setBrightnessConfigurationForUserInternal(c, userId);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validatePackageName(int uid, String packageName) {
|
||||
if (packageName != null) {
|
||||
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
|
||||
@@ -1871,18 +1946,24 @@ public final class DisplayManagerService extends SystemService {
|
||||
mDisplayPowerController = new DisplayPowerController(
|
||||
mContext, callbacks, handler, sensorManager, blanker);
|
||||
}
|
||||
|
||||
mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestPowerState(DisplayPowerRequest request,
|
||||
boolean waitForNegativeProximity) {
|
||||
return mDisplayPowerController.requestPowerState(request,
|
||||
waitForNegativeProximity);
|
||||
synchronized (mSyncRoot) {
|
||||
return mDisplayPowerController.requestPowerState(request,
|
||||
waitForNegativeProximity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProximitySensorAvailable() {
|
||||
return mDisplayPowerController.isProximitySensorAvailable();
|
||||
synchronized (mSyncRoot) {
|
||||
return mDisplayPowerController.isProximitySensorAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,10 +26,12 @@ import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
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.DisplayPowerCallbacks;
|
||||
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
|
||||
import android.os.Handler;
|
||||
@@ -93,6 +95,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
|
||||
private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
|
||||
private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
|
||||
private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
|
||||
private static final int MSG_USER_SWITCH = 6;
|
||||
|
||||
private static final int PROXIMITY_UNKNOWN = -1;
|
||||
private static final int PROXIMITY_NEGATIVE = 0;
|
||||
@@ -285,6 +289,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
// The controller for the automatic brightness level.
|
||||
private AutomaticBrightnessController mAutomaticBrightnessController;
|
||||
|
||||
// The default brightness configuration. Used for whenever we don't have a valid brightness
|
||||
// configuration set. This is typically seen with users that don't have a brightness
|
||||
// configuration that's different from the default.
|
||||
private BrightnessConfiguration mDefaultBrightnessConfiguration;
|
||||
|
||||
// The current brightness configuration.
|
||||
private BrightnessConfiguration mBrightnessConfiguration;
|
||||
|
||||
// Animators.
|
||||
private ObjectAnimator mColorFadeOnAnimator;
|
||||
private ObjectAnimator mColorFadeOffAnimator;
|
||||
@@ -333,7 +345,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
|
||||
mScreenBrightnessDarkConfig);
|
||||
|
||||
mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
|
||||
mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
|
||||
com.android.internal.R.integer.config_screenBrightnessSettingMaximum));
|
||||
|
||||
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
|
||||
com.android.internal.R.bool.config_automatic_brightness_available);
|
||||
@@ -348,60 +361,60 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
mSkipScreenOnBrightnessRamp = resources.getBoolean(
|
||||
com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
|
||||
|
||||
int lightSensorRate = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
|
||||
int initialLightSensorRate = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
|
||||
if (initialLightSensorRate == -1) {
|
||||
initialLightSensorRate = lightSensorRate;
|
||||
} else if (initialLightSensorRate > lightSensorRate) {
|
||||
Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
|
||||
+ initialLightSensorRate + ") to be less than or equal to "
|
||||
+ "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
|
||||
}
|
||||
long brighteningLightDebounce = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
|
||||
long darkeningLightDebounce = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
|
||||
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
|
||||
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
|
||||
int ambientLightHorizon = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
|
||||
float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
|
||||
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);
|
||||
int[] screenBrightness = resources.getIntArray(
|
||||
float[] luxLevels = getLuxLevels(resources.getIntArray(
|
||||
com.android.internal.R.array.config_autoBrightnessLevels));
|
||||
int[] backlightValues = resources.getIntArray(
|
||||
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
|
||||
int lightSensorWarmUpTimeConfig = resources.getInteger(
|
||||
com.android.internal.R.integer.config_lightSensorWarmupTime);
|
||||
float[] brightnessValuesNits = getFloatArray(resources.obtainTypedArray(
|
||||
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
|
||||
|
||||
final float screenMinimumNits = resources.getFloat(
|
||||
com.android.internal.R.dimen.config_screenBrightnessMinimumNits);
|
||||
final float screenMaximumNits = resources.getFloat(
|
||||
com.android.internal.R.dimen.config_screenBrightnessMaximumNits);
|
||||
|
||||
final float dozeScaleFactor = resources.getFraction(
|
||||
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
|
||||
1, 1);
|
||||
|
||||
Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
|
||||
if (screenAutoBrightnessSpline == null) {
|
||||
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
|
||||
+ "(size " + screenBrightness.length + ") "
|
||||
+ "must be monotic and have exactly one more entry than "
|
||||
+ "config_autoBrightnessLevels (size " + lux.length + ") "
|
||||
+ "which must be strictly increasing. "
|
||||
+ "Auto-brightness will be disabled.");
|
||||
mUseSoftwareAutoBrightnessConfig = false;
|
||||
} else {
|
||||
int bottom = clampAbsoluteBrightness(screenBrightness[0]);
|
||||
int[] brightLevels = resources.getIntArray(
|
||||
com.android.internal.R.array.config_dynamicHysteresisBrightLevels);
|
||||
int[] darkLevels = resources.getIntArray(
|
||||
com.android.internal.R.array.config_dynamicHysteresisDarkLevels);
|
||||
int[] luxHysteresisLevels = resources.getIntArray(
|
||||
com.android.internal.R.array.config_dynamicHysteresisLuxLevels);
|
||||
HysteresisLevels dynamicHysteresis = new HysteresisLevels(
|
||||
brightLevels, darkLevels, luxHysteresisLevels);
|
||||
|
||||
long brighteningLightDebounce = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
|
||||
long darkeningLightDebounce = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
|
||||
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
|
||||
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
|
||||
int ambientLightHorizon = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
|
||||
float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
|
||||
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
|
||||
1, 1);
|
||||
|
||||
int lightSensorWarmUpTimeConfig = resources.getInteger(
|
||||
com.android.internal.R.integer.config_lightSensorWarmupTime);
|
||||
int lightSensorRate = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
|
||||
int initialLightSensorRate = resources.getInteger(
|
||||
com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
|
||||
if (initialLightSensorRate == -1) {
|
||||
initialLightSensorRate = lightSensorRate;
|
||||
} else if (initialLightSensorRate > lightSensorRate) {
|
||||
Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
|
||||
+ initialLightSensorRate + ") to be less than or equal to "
|
||||
+ "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
|
||||
}
|
||||
|
||||
if (backlightValues != null && backlightValues.length > 0) {
|
||||
final int bottom = backlightValues[0];
|
||||
if (mScreenBrightnessDarkConfig > bottom) {
|
||||
Slog.w(TAG, "config_screenBrightnessDark (" + mScreenBrightnessDarkConfig
|
||||
+ ") should be less than or equal to the first value of "
|
||||
@@ -411,13 +424,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
if (bottom < screenBrightnessRangeMinimum) {
|
||||
screenBrightnessRangeMinimum = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
float[] nitsRange = { screenMinimumNits, screenMaximumNits };
|
||||
int[] backlightRange = { screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum };
|
||||
|
||||
BrightnessMappingStrategy mapper = BrightnessMappingStrategy.create(
|
||||
luxLevels, backlightValues, brightnessValuesNits,
|
||||
nitsRange, backlightRange);
|
||||
if (mapper != null) {
|
||||
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
|
||||
handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
|
||||
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
|
||||
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
|
||||
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
|
||||
handler.getLooper(), sensorManager, mapper, lightSensorWarmUpTimeConfig,
|
||||
screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum,
|
||||
dozeScaleFactor, lightSensorRate, initialLightSensorRate,
|
||||
brighteningLightDebounce, darkeningLightDebounce,
|
||||
autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon,
|
||||
autoBrightnessAdjustmentMaxGamma, dynamicHysteresis);
|
||||
} else {
|
||||
mUseSoftwareAutoBrightnessConfig = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,6 +468,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
|
||||
}
|
||||
|
||||
private static float[] getLuxLevels(int[] lux) {
|
||||
// The first control point is implicit and always at 0 lux.
|
||||
float[] levels = new float[lux.length + 1];
|
||||
for (int i = 0; i < lux.length; i++) {
|
||||
levels[i + 1] = (float) lux[i];
|
||||
}
|
||||
return levels;
|
||||
}
|
||||
|
||||
private static float[] getFloatArray(TypedArray array) {
|
||||
final int N = array.length();
|
||||
float[] vals = new float[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
vals[i] = array.getFloat(i, -1.0f);
|
||||
}
|
||||
array.recycle();
|
||||
return vals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the proximity sensor screen-off function is available.
|
||||
*/
|
||||
@@ -512,7 +555,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
if (!mPendingUpdatePowerStateLocked) {
|
||||
mPendingUpdatePowerStateLocked = true;
|
||||
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
@@ -691,8 +733,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
final boolean userInitiatedChange = autoBrightnessAdjustmentChanged
|
||||
&& mPowerRequest.brightnessSetByUser;
|
||||
mAutomaticBrightnessController.configure(autoBrightnessEnabled,
|
||||
mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON,
|
||||
userInitiatedChange);
|
||||
mBrightnessConfiguration, mPowerRequest.screenAutoBrightnessAdjustment,
|
||||
state != Display.STATE_ON, userInitiatedChange);
|
||||
}
|
||||
|
||||
// Apply brightness boost.
|
||||
@@ -874,6 +916,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
sendUpdatePowerState();
|
||||
}
|
||||
|
||||
public void setBrightnessConfiguration(BrightnessConfiguration c) {
|
||||
Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
private void blockScreenOn() {
|
||||
if (mPendingScreenOnUnblocker == null) {
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
|
||||
@@ -1241,7 +1288,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
// Need to wait a little longer.
|
||||
// Debounce again later. We continue holding a wake lock while waiting.
|
||||
Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
|
||||
}
|
||||
}
|
||||
@@ -1402,39 +1448,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
}
|
||||
}
|
||||
|
||||
private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
|
||||
if (lux == null || lux.length == 0 || brightness == null || brightness.length == 0) {
|
||||
Slog.e(TAG, "Could not create auto-brightness spline.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final int n = brightness.length;
|
||||
float[] x = new float[n];
|
||||
float[] y = new float[n];
|
||||
y[0] = normalizeAbsoluteBrightness(brightness[0]);
|
||||
for (int i = 1; i < n; i++) {
|
||||
x[i] = lux[i - 1];
|
||||
y[i] = normalizeAbsoluteBrightness(brightness[i]);
|
||||
}
|
||||
|
||||
Spline spline = Spline.createSpline(x, y);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Auto-brightness spline: " + spline);
|
||||
for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
|
||||
Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
|
||||
}
|
||||
}
|
||||
return spline;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Slog.e(TAG, "Could not create auto-brightness spline.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static float normalizeAbsoluteBrightness(int value) {
|
||||
return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON;
|
||||
}
|
||||
|
||||
private static int clampAbsoluteBrightness(int value) {
|
||||
return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
|
||||
}
|
||||
@@ -1467,6 +1480,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
updatePowerState();
|
||||
}
|
||||
break;
|
||||
case MSG_CONFIGURE_BRIGHTNESS:
|
||||
BrightnessConfiguration c = (BrightnessConfiguration) msg.obj;
|
||||
mBrightnessConfiguration = c != null ? c : mDefaultBrightnessConfiguration;
|
||||
updatePowerState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1492,17 +1510,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
@Override
|
||||
public void onScreenOn() {
|
||||
Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
|
||||
|
||||
@Override
|
||||
public void onScreenOff() {
|
||||
Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
|
||||
msg.setAsynchronous(true);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,17 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.hardware.display.WifiDisplay;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Pair;
|
||||
import android.util.Xml;
|
||||
import android.view.Display;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
@@ -37,10 +42,12 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
@@ -57,14 +64,22 @@ import libcore.util.Objects;
|
||||
* <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
|
||||
* <remembered-wifi-displays>
|
||||
* <display-states>
|
||||
* <display>
|
||||
* <display unique-id="XXXXXXX">
|
||||
* <color-mode>0</color-mode>
|
||||
* </display>
|
||||
* </display-states>
|
||||
* <stable-device-values>
|
||||
* <stable-display-height>1920<stable-display-height>
|
||||
* <stable-display-width>1080<stable-display-width>
|
||||
* <stable-display-height>1920</stable-display-height>
|
||||
* <stable-display-width>1080</stable-display-width>
|
||||
* </stable-device-values>
|
||||
* <brightness-configurations>
|
||||
* <brightness-configuration user-id="0">
|
||||
* <brightness-curve>
|
||||
* <brightness-point lux="0" nits="13.25"/>
|
||||
* <brightness-point lux="20" nits="35.94"/>
|
||||
* </brightness-curve>
|
||||
* </brightness-configuration>
|
||||
* </brightness-configurations>
|
||||
* </display-manager-state>
|
||||
* </code>
|
||||
*
|
||||
@@ -73,6 +88,31 @@ import libcore.util.Objects;
|
||||
final class PersistentDataStore {
|
||||
static final String TAG = "DisplayManager";
|
||||
|
||||
private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state";
|
||||
|
||||
private static final String TAG_REMEMBERED_WIFI_DISPLAYS = "remembered-wifi-displays";
|
||||
private static final String TAG_WIFI_DISPLAY = "wifi-display";
|
||||
private static final String ATTR_DEVICE_ADDRESS = "deviceAddress";
|
||||
private static final String ATTR_DEVICE_NAME = "deviceName";
|
||||
private static final String ATTR_DEVICE_ALIAS = "deviceAlias";
|
||||
|
||||
private static final String TAG_DISPLAY_STATES = "display-states";
|
||||
private static final String TAG_DISPLAY = "display";
|
||||
private static final String TAG_COLOR_MODE = "color-mode";
|
||||
private static final String ATTR_UNIQUE_ID = "unique-id";
|
||||
|
||||
private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values";
|
||||
private static final String TAG_STABLE_DISPLAY_HEIGHT = "stable-display-height";
|
||||
private static final String TAG_STABLE_DISPLAY_WIDTH = "stable-display-width";
|
||||
|
||||
private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
|
||||
private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
|
||||
private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
|
||||
private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
|
||||
private static final String ATTR_USER_SERIAL = "user-serial";
|
||||
private static final String ATTR_LUX = "lux";
|
||||
private static final String ATTR_NITS = "nits";
|
||||
|
||||
// Remembered Wifi display devices.
|
||||
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
|
||||
|
||||
@@ -83,8 +123,8 @@ final class PersistentDataStore {
|
||||
// Display values which should be stable across the device's lifetime.
|
||||
private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
|
||||
|
||||
// The atomic file used to safely read or write the file.
|
||||
private final AtomicFile mAtomicFile;
|
||||
// Brightness configuration by user
|
||||
private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations();
|
||||
|
||||
// True if the data has been loaded.
|
||||
private boolean mLoaded;
|
||||
@@ -92,8 +132,16 @@ final class PersistentDataStore {
|
||||
// True if there are changes to be saved.
|
||||
private boolean mDirty;
|
||||
|
||||
// The interface for methods which should be replaced by the test harness.
|
||||
private Injector mInjector;
|
||||
|
||||
public PersistentDataStore() {
|
||||
mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
|
||||
this(new Injector());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PersistentDataStore(Injector injector) {
|
||||
mInjector = injector;
|
||||
}
|
||||
|
||||
public void saveIfNeeded() {
|
||||
@@ -225,6 +273,18 @@ final class PersistentDataStore {
|
||||
}
|
||||
}
|
||||
|
||||
public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial) {
|
||||
loadIfNeeded();
|
||||
if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial)) {
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
|
||||
loadIfNeeded();
|
||||
return mBrightnessConfigurations.getBrightnessConfiguration(userSerial);
|
||||
}
|
||||
|
||||
private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
|
||||
loadIfNeeded();
|
||||
DisplayState state = mDisplayStates.get(uniqueId);
|
||||
@@ -256,7 +316,7 @@ final class PersistentDataStore {
|
||||
|
||||
final InputStream is;
|
||||
try {
|
||||
is = mAtomicFile.openRead();
|
||||
is = mInjector.openRead();
|
||||
} catch (FileNotFoundException ex) {
|
||||
return;
|
||||
}
|
||||
@@ -278,9 +338,9 @@ final class PersistentDataStore {
|
||||
}
|
||||
|
||||
private void save() {
|
||||
final FileOutputStream os;
|
||||
final OutputStream os;
|
||||
try {
|
||||
os = mAtomicFile.startWrite();
|
||||
os = mInjector.startWrite();
|
||||
boolean success = false;
|
||||
try {
|
||||
XmlSerializer serializer = new FastXmlSerializer();
|
||||
@@ -289,11 +349,7 @@ final class PersistentDataStore {
|
||||
serializer.flush();
|
||||
success = true;
|
||||
} finally {
|
||||
if (success) {
|
||||
mAtomicFile.finishWrite(os);
|
||||
} else {
|
||||
mAtomicFile.failWrite(os);
|
||||
}
|
||||
mInjector.finishWrite(os, success);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
|
||||
@@ -302,18 +358,21 @@ final class PersistentDataStore {
|
||||
|
||||
private void loadFromXml(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
XmlUtils.beginDocument(parser, "display-manager-state");
|
||||
XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE);
|
||||
final int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (parser.getName().equals("remembered-wifi-displays")) {
|
||||
if (parser.getName().equals(TAG_REMEMBERED_WIFI_DISPLAYS)) {
|
||||
loadRememberedWifiDisplaysFromXml(parser);
|
||||
}
|
||||
if (parser.getName().equals("display-states")) {
|
||||
if (parser.getName().equals(TAG_DISPLAY_STATES)) {
|
||||
loadDisplaysFromXml(parser);
|
||||
}
|
||||
if (parser.getName().equals("stable-device-values")) {
|
||||
if (parser.getName().equals(TAG_STABLE_DEVICE_VALUES)) {
|
||||
mStableDeviceValues.loadFromXml(parser);
|
||||
}
|
||||
if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
|
||||
mBrightnessConfigurations.loadFromXml(parser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,10 +380,10 @@ final class PersistentDataStore {
|
||||
throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (parser.getName().equals("wifi-display")) {
|
||||
String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
|
||||
String deviceName = parser.getAttributeValue(null, "deviceName");
|
||||
String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
|
||||
if (parser.getName().equals(TAG_WIFI_DISPLAY)) {
|
||||
String deviceAddress = parser.getAttributeValue(null, ATTR_DEVICE_ADDRESS);
|
||||
String deviceName = parser.getAttributeValue(null, ATTR_DEVICE_NAME);
|
||||
String deviceAlias = parser.getAttributeValue(null, ATTR_DEVICE_ALIAS);
|
||||
if (deviceAddress == null || deviceName == null) {
|
||||
throw new XmlPullParserException(
|
||||
"Missing deviceAddress or deviceName attribute on wifi-display.");
|
||||
@@ -345,8 +404,8 @@ final class PersistentDataStore {
|
||||
throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (parser.getName().equals("display")) {
|
||||
String uniqueId = parser.getAttributeValue(null, "unique-id");
|
||||
if (parser.getName().equals(TAG_DISPLAY)) {
|
||||
String uniqueId = parser.getAttributeValue(null, ATTR_UNIQUE_ID);
|
||||
if (uniqueId == null) {
|
||||
throw new XmlPullParserException(
|
||||
"Missing unique-id attribute on display.");
|
||||
@@ -365,32 +424,35 @@ final class PersistentDataStore {
|
||||
private void saveToXml(XmlSerializer serializer) throws IOException {
|
||||
serializer.startDocument(null, true);
|
||||
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||
serializer.startTag(null, "display-manager-state");
|
||||
serializer.startTag(null, "remembered-wifi-displays");
|
||||
serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE);
|
||||
serializer.startTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
|
||||
for (WifiDisplay display : mRememberedWifiDisplays) {
|
||||
serializer.startTag(null, "wifi-display");
|
||||
serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
|
||||
serializer.attribute(null, "deviceName", display.getDeviceName());
|
||||
serializer.startTag(null, TAG_WIFI_DISPLAY);
|
||||
serializer.attribute(null, ATTR_DEVICE_ADDRESS, display.getDeviceAddress());
|
||||
serializer.attribute(null, ATTR_DEVICE_NAME, display.getDeviceName());
|
||||
if (display.getDeviceAlias() != null) {
|
||||
serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
|
||||
serializer.attribute(null, ATTR_DEVICE_ALIAS, display.getDeviceAlias());
|
||||
}
|
||||
serializer.endTag(null, "wifi-display");
|
||||
serializer.endTag(null, TAG_WIFI_DISPLAY);
|
||||
}
|
||||
serializer.endTag(null, "remembered-wifi-displays");
|
||||
serializer.startTag(null, "display-states");
|
||||
serializer.endTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
|
||||
serializer.startTag(null, TAG_DISPLAY_STATES);
|
||||
for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
|
||||
final String uniqueId = entry.getKey();
|
||||
final DisplayState state = entry.getValue();
|
||||
serializer.startTag(null, "display");
|
||||
serializer.attribute(null, "unique-id", uniqueId);
|
||||
serializer.startTag(null, TAG_DISPLAY);
|
||||
serializer.attribute(null, ATTR_UNIQUE_ID, uniqueId);
|
||||
state.saveToXml(serializer);
|
||||
serializer.endTag(null, "display");
|
||||
serializer.endTag(null, TAG_DISPLAY);
|
||||
}
|
||||
serializer.endTag(null, "display-states");
|
||||
serializer.startTag(null, "stable-device-values");
|
||||
serializer.endTag(null, TAG_DISPLAY_STATES);
|
||||
serializer.startTag(null, TAG_STABLE_DEVICE_VALUES);
|
||||
mStableDeviceValues.saveToXml(serializer);
|
||||
serializer.endTag(null, "stable-device-values");
|
||||
serializer.endTag(null, "display-manager-state");
|
||||
serializer.endTag(null, TAG_STABLE_DEVICE_VALUES);
|
||||
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
|
||||
mBrightnessConfigurations.saveToXml(serializer);
|
||||
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
|
||||
serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
|
||||
serializer.endDocument();
|
||||
}
|
||||
|
||||
@@ -411,6 +473,8 @@ final class PersistentDataStore {
|
||||
}
|
||||
pw.println(" StableDeviceValues:");
|
||||
mStableDeviceValues.dump(pw, " ");
|
||||
pw.println(" BrightnessConfigurations:");
|
||||
mBrightnessConfigurations.dump(pw, " ");
|
||||
}
|
||||
|
||||
private static final class DisplayState {
|
||||
@@ -433,7 +497,7 @@ final class PersistentDataStore {
|
||||
final int outerDepth = parser.getDepth();
|
||||
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (parser.getName().equals("color-mode")) {
|
||||
if (parser.getName().equals(TAG_COLOR_MODE)) {
|
||||
String value = parser.nextText();
|
||||
mColorMode = Integer.parseInt(value);
|
||||
}
|
||||
@@ -441,9 +505,9 @@ final class PersistentDataStore {
|
||||
}
|
||||
|
||||
public void saveToXml(XmlSerializer serializer) throws IOException {
|
||||
serializer.startTag(null, "color-mode");
|
||||
serializer.startTag(null, TAG_COLOR_MODE);
|
||||
serializer.text(Integer.toString(mColorMode));
|
||||
serializer.endTag(null, "color-mode");
|
||||
serializer.endTag(null, TAG_COLOR_MODE);
|
||||
}
|
||||
|
||||
public void dump(final PrintWriter pw, final String prefix) {
|
||||
@@ -472,10 +536,10 @@ final class PersistentDataStore {
|
||||
final int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
switch (parser.getName()) {
|
||||
case "stable-display-width":
|
||||
case TAG_STABLE_DISPLAY_WIDTH:
|
||||
mWidth = loadIntValue(parser);
|
||||
break;
|
||||
case "stable-display-height":
|
||||
case TAG_STABLE_DISPLAY_HEIGHT:
|
||||
mHeight = loadIntValue(parser);
|
||||
break;
|
||||
}
|
||||
@@ -494,12 +558,12 @@ final class PersistentDataStore {
|
||||
|
||||
public void saveToXml(XmlSerializer serializer) throws IOException {
|
||||
if (mWidth > 0 && mHeight > 0) {
|
||||
serializer.startTag(null, "stable-display-width");
|
||||
serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH);
|
||||
serializer.text(Integer.toString(mWidth));
|
||||
serializer.endTag(null, "stable-display-width");
|
||||
serializer.startTag(null, "stable-display-height");
|
||||
serializer.endTag(null, TAG_STABLE_DISPLAY_WIDTH);
|
||||
serializer.startTag(null, TAG_STABLE_DISPLAY_HEIGHT);
|
||||
serializer.text(Integer.toString(mHeight));
|
||||
serializer.endTag(null, "stable-display-height");
|
||||
serializer.endTag(null, TAG_STABLE_DISPLAY_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,4 +572,158 @@ final class PersistentDataStore {
|
||||
pw.println(prefix + "StableDisplayHeight=" + mHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class BrightnessConfigurations {
|
||||
// Maps from a user ID to the users' given brightness configuration
|
||||
private SparseArray<BrightnessConfiguration> mConfigurations;
|
||||
|
||||
public BrightnessConfigurations() {
|
||||
mConfigurations = new SparseArray<>();
|
||||
}
|
||||
|
||||
private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c,
|
||||
int userSerial) {
|
||||
BrightnessConfiguration currentConfig = mConfigurations.get(userSerial);
|
||||
if (currentConfig == null || !currentConfig.equals(c)) {
|
||||
mConfigurations.put(userSerial, c);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
|
||||
return mConfigurations.get(userSerial);
|
||||
}
|
||||
|
||||
public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) {
|
||||
int userSerial;
|
||||
try {
|
||||
userSerial = Integer.parseInt(
|
||||
parser.getAttributeValue(null, ATTR_USER_SERIAL));
|
||||
} catch (NumberFormatException nfe) {
|
||||
userSerial= -1;
|
||||
Slog.e(TAG, "Failed to read in brightness configuration", nfe);
|
||||
}
|
||||
|
||||
try {
|
||||
BrightnessConfiguration config = loadConfigurationFromXml(parser);
|
||||
if (userSerial>= 0 && config != null) {
|
||||
mConfigurations.put(userSerial, config);
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Slog.e(TAG, "Failed to load brightness configuration!", iae);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
|
||||
Pair<float[], float[]> curve = loadCurveFromXml(parser, builder);
|
||||
builder.setCurve(curve.first /*lux*/, curve.second /*nits*/);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser,
|
||||
BrightnessConfiguration.Builder builder)
|
||||
throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
List<Float> luxLevels = new ArrayList<>();
|
||||
List<Float> nitLevels = new ArrayList<>();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
|
||||
luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
|
||||
nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
|
||||
}
|
||||
}
|
||||
final int N = luxLevels.size();
|
||||
float[] lux = new float[N];
|
||||
float[] nits = new float[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
lux[i] = luxLevels.get(i);
|
||||
nits[i] = nitLevels.get(i);
|
||||
}
|
||||
return Pair.create(lux, nits);
|
||||
}
|
||||
|
||||
private static float loadFloat(String val) {
|
||||
try {
|
||||
return Float.parseFloat(val);
|
||||
} catch (NullPointerException | NumberFormatException e) {
|
||||
Slog.e(TAG, "Failed to parse float loading brightness config", e);
|
||||
return Float.NEGATIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveToXml(XmlSerializer serializer) throws IOException {
|
||||
for (int i = 0; i < mConfigurations.size(); i++) {
|
||||
final int userSerial= mConfigurations.keyAt(i);
|
||||
final BrightnessConfiguration config = mConfigurations.valueAt(i);
|
||||
|
||||
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION);
|
||||
serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial));
|
||||
saveConfigurationToXml(serializer, config);
|
||||
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveConfigurationToXml(XmlSerializer serializer,
|
||||
BrightnessConfiguration config) throws IOException {
|
||||
serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
|
||||
final Pair<float[], float[]> curve = config.getCurve();
|
||||
for (int i = 0; i < curve.first.length; i++) {
|
||||
serializer.startTag(null, TAG_BRIGHTNESS_POINT);
|
||||
serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
|
||||
serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
|
||||
serializer.endTag(null, TAG_BRIGHTNESS_POINT);
|
||||
}
|
||||
serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
|
||||
}
|
||||
|
||||
public void dump(final PrintWriter pw, final String prefix) {
|
||||
for (int i = 0; i < mConfigurations.size(); i++) {
|
||||
final int userSerial= mConfigurations.keyAt(i);
|
||||
pw.println(prefix + "User " + userSerial + ":");
|
||||
pw.println(prefix + " " + mConfigurations.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class Injector {
|
||||
private final AtomicFile mAtomicFile;
|
||||
|
||||
public Injector() {
|
||||
mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
|
||||
}
|
||||
|
||||
public InputStream openRead() throws FileNotFoundException {
|
||||
return mAtomicFile.openRead();
|
||||
}
|
||||
|
||||
public OutputStream startWrite() throws IOException {
|
||||
return mAtomicFile.startWrite();
|
||||
}
|
||||
|
||||
public void finishWrite(OutputStream os, boolean success) {
|
||||
if (!(os instanceof FileOutputStream)) {
|
||||
throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
|
||||
}
|
||||
FileOutputStream fos = (FileOutputStream) os;
|
||||
if (success) {
|
||||
mAtomicFile.finishWrite(fos);
|
||||
} else {
|
||||
mAtomicFile.failWrite(fos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2017 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.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.os.PowerManager;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Spline;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BrightnessMappingStrategyTest {
|
||||
|
||||
private static final float[] LUX_LEVELS = {
|
||||
0f,
|
||||
5f,
|
||||
20f,
|
||||
40f,
|
||||
100f,
|
||||
325f,
|
||||
600f,
|
||||
1250f,
|
||||
2200f,
|
||||
4000f,
|
||||
5000f
|
||||
};
|
||||
|
||||
private static final float[] DISPLAY_LEVELS_NITS = {
|
||||
13.25f,
|
||||
54.0f,
|
||||
78.85f,
|
||||
105.02f,
|
||||
132.7f,
|
||||
170.12f,
|
||||
212.1f,
|
||||
265.2f,
|
||||
335.8f,
|
||||
415.2f,
|
||||
478.5f,
|
||||
};
|
||||
|
||||
private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
|
||||
9,
|
||||
30,
|
||||
45,
|
||||
62,
|
||||
78,
|
||||
96,
|
||||
119,
|
||||
146,
|
||||
178,
|
||||
221,
|
||||
255
|
||||
};
|
||||
|
||||
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
|
||||
private static final int[] BACKLIGHT_RANGE = { 1, 255 };
|
||||
|
||||
@Test
|
||||
public void testSimpleStrategyMappingAtControlPoints() {
|
||||
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
|
||||
null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
|
||||
assertNotNull("BrightnessMappingStrategy should not be null", simple);
|
||||
for (int i = 0; i < LUX_LEVELS.length; i++) {
|
||||
final float expectedLevel =
|
||||
(float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON;
|
||||
assertEquals(expectedLevel,
|
||||
simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStrategyMappingBetweenControlPoints() {
|
||||
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
|
||||
null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
|
||||
assertNotNull("BrightnessMappingStrategy should not be null", simple);
|
||||
for (int i = 1; i < LUX_LEVELS.length; i++) {
|
||||
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
|
||||
final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
|
||||
assertTrue("Desired brightness should be between adjacent control points.",
|
||||
backlight > DISPLAY_LEVELS_BACKLIGHT[i-1]
|
||||
&& backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPhysicalStrategyMappingAtControlPoints() {
|
||||
BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertNotNull("BrightnessMappingStrategy should not be null", physical);
|
||||
for (int i = 0; i < LUX_LEVELS.length; i++) {
|
||||
final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1];
|
||||
assertEquals(expectedLevel,
|
||||
physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPhysicalStrategyMappingBetweenControlPoints() {
|
||||
BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertNotNull("BrightnessMappingStrategy should not be null", physical);
|
||||
Spline backlightToBrightness =
|
||||
Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS);
|
||||
for (int i = 1; i < LUX_LEVELS.length; i++) {
|
||||
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
|
||||
final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
|
||||
final float nits = backlightToBrightness.interpolate(backlight);
|
||||
assertTrue("Desired brightness should be between adjacent control points.",
|
||||
nits > DISPLAY_LEVELS_NITS[i-1] && nits < DISPLAY_LEVELS_NITS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultStrategyIsPhysical() {
|
||||
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonStrictlyIncreasingLuxLevelsFails() {
|
||||
final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
|
||||
final int idx = lux.length / 2;
|
||||
float tmp = lux[idx];
|
||||
lux[idx] = lux[idx+1];
|
||||
lux[idx+1] = tmp;
|
||||
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
|
||||
lux, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertNull(strategy);
|
||||
|
||||
// And make sure we get the same result even if it's monotone but not increasing.
|
||||
lux[idx] = lux[idx+1];
|
||||
strategy = BrightnessMappingStrategy.create(
|
||||
lux, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertNull(strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentNumberOfControlPointValuesFails() {
|
||||
//Extra lux level
|
||||
final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
|
||||
// Make sure it's strictly increasing so that the only failure is the differing array
|
||||
// lengths
|
||||
lux[lux.length - 1] = lux[lux.length - 2] + 1;
|
||||
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
|
||||
lux, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertNull(strategy);
|
||||
|
||||
strategy = BrightnessMappingStrategy.create(
|
||||
lux, DISPLAY_LEVELS_BACKLIGHT,
|
||||
null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
|
||||
assertNull(strategy);
|
||||
|
||||
// Extra backlight level
|
||||
final int[] backlight = Arrays.copyOf(
|
||||
DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
|
||||
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
|
||||
strategy = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, backlight,
|
||||
null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
|
||||
assertNull(strategy);
|
||||
|
||||
// Extra nits level
|
||||
final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
|
||||
nits[nits.length - 1] = nits[nits.length - 2] + 1;
|
||||
strategy = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, null /*brightnessLevelsBacklight*/,
|
||||
nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
|
||||
assertNull(strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPhysicalStrategyRequiresNitsMapping() {
|
||||
BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, null, BACKLIGHT_RANGE);
|
||||
assertNull(physical);
|
||||
|
||||
physical = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, null);
|
||||
assertNull(physical);
|
||||
|
||||
physical = BrightnessMappingStrategy.create(
|
||||
LUX_LEVELS, null /*brightnessLevelsBacklight*/,
|
||||
DISPLAY_LEVELS_NITS, null, null);
|
||||
assertNull(physical);
|
||||
}
|
||||
|
||||
private static float[] toFloatArray(int[] vals) {
|
||||
float[] newVals = new float[vals.length];
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
newVals[i] = (float) vals[i];
|
||||
}
|
||||
return newVals;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2017 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.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PersistentDataStoreTest {
|
||||
private PersistentDataStore mDataStore;
|
||||
private TestInjector mInjector;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mInjector = new TestInjector();
|
||||
mDataStore = new PersistentDataStore(mInjector);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadingBrightnessConfigurations() {
|
||||
String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
|
||||
+ "<display-manager-state>\n"
|
||||
+ " <brightness-configurations>\n"
|
||||
+ " <brightness-configuration user-serial=\"1\">\n"
|
||||
+ " <brightness-curve>\n"
|
||||
+ " <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
|
||||
+ " <brightness-point lux=\"25\" nits=\"35.94\"/>\n"
|
||||
+ " </brightness-curve>\n"
|
||||
+ " </brightness-configuration>\n"
|
||||
+ " <brightness-configuration user-serial=\"3\">\n"
|
||||
+ " <brightness-curve>\n"
|
||||
+ " <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
|
||||
+ " <brightness-point lux=\"10.2\" nits=\"15\"/>\n"
|
||||
+ " </brightness-curve>\n"
|
||||
+ " </brightness-configuration>\n"
|
||||
+ " </brightness-configurations>\n"
|
||||
+ "</display-manager-state>\n";
|
||||
InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
|
||||
mInjector.setReadStream(is);
|
||||
mDataStore.loadIfNeeded();
|
||||
BrightnessConfiguration config = mDataStore.getBrightnessConfiguration(1 /*userSerial*/);
|
||||
Pair<float[], float[]> curve = config.getCurve();
|
||||
float[] expectedLux = { 0f, 25f };
|
||||
float[] expectedNits = { 13.25f, 35.94f };
|
||||
assertArrayEquals(expectedLux, curve.first, "lux");
|
||||
assertArrayEquals(expectedNits, curve.second, "nits");
|
||||
|
||||
config = mDataStore.getBrightnessConfiguration(3 /*userSerial*/);
|
||||
curve = config.getCurve();
|
||||
expectedLux = new float[] { 0f, 10.2f };
|
||||
expectedNits = new float[] { 13.25f, 15f };
|
||||
assertArrayEquals(expectedLux, curve.first, "lux");
|
||||
assertArrayEquals(expectedNits, curve.second, "nits");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessConfigWithInvalidCurveIsIgnored() {
|
||||
String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
|
||||
+ "<display-manager-state>\n"
|
||||
+ " <brightness-configurations>\n"
|
||||
+ " <brightness-configuration user-serial=\"0\">\n"
|
||||
+ " <brightness-curve>\n"
|
||||
+ " <brightness-point lux=\"1\" nits=\"13.25\"/>\n"
|
||||
+ " <brightness-point lux=\"25\" nits=\"35.94\"/>\n"
|
||||
+ " </brightness-curve>\n"
|
||||
+ " </brightness-configuration>\n"
|
||||
+ " </brightness-configurations>\n"
|
||||
+ "</display-manager-state>\n";
|
||||
InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
|
||||
mInjector.setReadStream(is);
|
||||
mDataStore.loadIfNeeded();
|
||||
assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessConfigWithInvalidFloatsIsIgnored() {
|
||||
String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
|
||||
+ "<display-manager-state>\n"
|
||||
+ " <brightness-configurations>\n"
|
||||
+ " <brightness-configuration user-serial=\"0\">\n"
|
||||
+ " <brightness-curve>\n"
|
||||
+ " <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
|
||||
+ " <brightness-point lux=\"0xFF\" nits=\"foo\"/>\n"
|
||||
+ " </brightness-curve>\n"
|
||||
+ " </brightness-configuration>\n"
|
||||
+ " </brightness-configurations>\n"
|
||||
+ "</display-manager-state>\n";
|
||||
InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
|
||||
mInjector.setReadStream(is);
|
||||
mDataStore.loadIfNeeded();
|
||||
assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyBrightnessConfigurationsDoesntCrash() {
|
||||
String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
|
||||
+ "<display-manager-state>\n"
|
||||
+ " <brightness-configurations />\n"
|
||||
+ "</display-manager-state>\n";
|
||||
InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
|
||||
mInjector.setReadStream(is);
|
||||
mDataStore.loadIfNeeded();
|
||||
assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreAndReloadOfBrightnessConfigurations() {
|
||||
final float[] lux = { 0f, 10f };
|
||||
final float[] nits = {1f, 100f };
|
||||
final BrightnessConfiguration config = new BrightnessConfiguration.Builder()
|
||||
.setCurve(lux, nits)
|
||||
.build();
|
||||
mDataStore.loadIfNeeded();
|
||||
assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
|
||||
mDataStore.setBrightnessConfigurationForUser(config, 0);
|
||||
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
mInjector.setWriteStream(baos);
|
||||
mDataStore.saveIfNeeded();
|
||||
assertTrue(mInjector.wasWriteSuccessful());
|
||||
|
||||
TestInjector newInjector = new TestInjector();
|
||||
PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
newInjector.setReadStream(bais);
|
||||
newDataStore.loadIfNeeded();
|
||||
assertNotNull(newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
|
||||
assertEquals(mDataStore.getBrightnessConfiguration(0 /*userSerial*/),
|
||||
newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
|
||||
}
|
||||
|
||||
public class TestInjector extends PersistentDataStore.Injector {
|
||||
private InputStream mReadStream;
|
||||
private OutputStream mWriteStream;
|
||||
|
||||
private boolean mWasSuccessful;
|
||||
|
||||
@Override
|
||||
public InputStream openRead() throws FileNotFoundException {
|
||||
if (mReadStream != null) {
|
||||
return mReadStream;
|
||||
} else {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream startWrite() {
|
||||
return mWriteStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishWrite(OutputStream os, boolean success) {
|
||||
mWasSuccessful = success;
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// This method can't throw IOException since the super implementation doesn't, so
|
||||
// we just wrap it in a RuntimeException so we end up crashing the test all the
|
||||
// same.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setReadStream(InputStream is) {
|
||||
mReadStream = is;
|
||||
}
|
||||
|
||||
public void setWriteStream(OutputStream os) {
|
||||
mWriteStream = os;
|
||||
}
|
||||
|
||||
public boolean wasWriteSuccessful() {
|
||||
return mWasSuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertArrayEquals(float[] expected, float[] actual, String name) {
|
||||
assertEquals("Expected " + name + " arrays to be the same length!",
|
||||
expected.length, actual.length);
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals("Expected " + name + " arrays to be equivalent when value " + i
|
||||
+ "differs", expected[i], actual[i], 0.01 /*tolerance*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user