From eef0e13f018f33bf3db2f8311bbb429369e13394 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 21 Nov 2017 17:57:52 +0000 Subject: [PATCH] Add an API for setting a new brightness curve. In addition, this also provides multiple strategies for mapping from ambient room brightness in lux to display brightness. The default one is the classic strategy, where we map directly from lux to backlight brightness in an arbitrary unit. The newer and preferred strategy is to use the physical brightness of the display, but requires that the brightness properties of the display are appropriately configured. Bug: 69406783 Test: atest com.android.server.display.BrightnessMappingStrategyTest && atest android.hardware.display.BrightnessConfigurationTest && atest android.hardware.display.PersistentDataStoreTeset Change-Id: I60227bdb6c299d0fa92686cbf3e5994b336a3a79 --- .../display/BrightnessConfiguration.aidl | 19 ++ .../display/BrightnessConfiguration.java | 175 ++++++++++ .../hardware/display/DisplayManager.java | 22 ++ .../display/DisplayManagerGlobal.java | 13 + .../hardware/display/IDisplayManager.aidl | 6 + .../android/internal/util/Preconditions.java | 34 ++ core/res/AndroidManifest.xml | 5 + core/res/res/values/config.xml | 39 +++ core/res/res/values/symbols.xml | 4 + .../display/BrightnessConfigurationTest.java | 187 +++++++++++ .../AutomaticBrightnessController.java | 31 +- .../display/BrightnessMappingStrategy.java | 289 ++++++++++++++++ .../server/display/DisplayManagerService.java | 93 +++++- .../display/DisplayPowerController.java | 201 +++++------ .../server/display/PersistentDataStore.java | 316 +++++++++++++++--- .../BrightnessMappingStrategyTest.java | 230 +++++++++++++ .../display/PersistentDataStoreTest.java | 223 ++++++++++++ 17 files changed, 1727 insertions(+), 160 deletions(-) create mode 100644 core/java/android/hardware/display/BrightnessConfiguration.aidl create mode 100644 core/java/android/hardware/display/BrightnessConfiguration.java create mode 100644 core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java create mode 100644 services/core/java/com/android/server/display/BrightnessMappingStrategy.java create mode 100644 services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java create mode 100644 services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java diff --git a/core/java/android/hardware/display/BrightnessConfiguration.aidl b/core/java/android/hardware/display/BrightnessConfiguration.aidl new file mode 100644 index 0000000000000..5b6b4645fa454 --- /dev/null +++ b/core/java/android/hardware/display/BrightnessConfiguration.aidl @@ -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; diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java new file mode 100644 index 0000000000000..6c3be816e87be --- /dev/null +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -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 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 CREATOR = + new Creator() { + 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 must 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]; + } + } + } +} diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 97ca231bf68c6..7de667dcaa2b5 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -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. */ diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index c3f82f56dad83..bf4cc1d826a93 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -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) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index f2ed9e7571d39..8afae6ec90101 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -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); } diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index e5d571672ce2e..91c76afdf5b2b 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -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; + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 09204261811d3..5645b6475bb81 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2923,6 +2923,11 @@ + + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0b38d1b1fca1a..e09ed18127f57 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1301,6 +1301,22 @@ + + + + + -1.0 + + + -1.0 +