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 ab7b07ec96ba4..15e439ed35869 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2924,6 +2924,11 @@ + + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9a26229d59462..8377751f4236c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1295,6 +1295,22 @@ + + + + + -1.0 + + + -1.0 +