diff --git a/api/current.txt b/api/current.txt
index 9b5481407d5ac..860ae0907f3c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22876,6 +22876,7 @@ package android.util {
method public static float cos(float);
method public static float exp(float);
method public static float floor(float);
+ method public static float hypot(float, float);
method public static float sin(float);
method public static float sqrt(float);
}
diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java
index 1d4eda41473c7..e05169ad6ddfb 100644
--- a/core/java/android/util/FloatMath.java
+++ b/core/java/android/util/FloatMath.java
@@ -80,4 +80,14 @@ public class FloatMath {
* @return the exponential of value
*/
public static native float exp(float value);
+
+ /**
+ * Returns {@code sqrt(}{@code x}{@code 2}{@code +}
+ * {@code y}{@code 2}{@code )}.
+ *
+ * @param x a float number
+ * @param y a float number
+ * @return the hypotenuse
+ */
+ public static native float hypot(float x, float y);
}
diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java
new file mode 100644
index 0000000000000..ed027eb18166e
--- /dev/null
+++ b/core/java/android/util/Spline.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 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.util;
+
+/**
+ * Performs spline interpolation given a set of control points.
+ * @hide
+ */
+public final class Spline {
+ private final float[] mX;
+ private final float[] mY;
+ private final float[] mM;
+
+ private Spline(float[] x, float[] y, float[] m) {
+ mX = x;
+ mY = y;
+ mM = m;
+ }
+
+ /**
+ * Creates a monotone cubic spline from a given set of control points.
+ *
+ * The spline is guaranteed to pass through each control point exactly.
+ * Moreover, assuming the control points are monotonic (Y is non-decreasing or
+ * non-increasing) then the interpolated values will also be monotonic.
+ *
+ * This function uses the Fritsch-Carlson method for computing the spline parameters.
+ * http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
+ *
+ * @param x The X component of the control points, strictly increasing.
+ * @param y The Y component of the control points, monotonic.
+ * @return
+ *
+ * @throws IllegalArgumentException if the X or Y arrays are null, have
+ * different lengths or have fewer than 2 values.
+ * @throws IllegalArgumentException if the control points are not monotonic.
+ */
+ public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
+ if (x == null || y == null || x.length != y.length || x.length < 2) {
+ throw new IllegalArgumentException("There must be at least two control "
+ + "points and the arrays must be of equal length.");
+ }
+
+ final int n = x.length;
+ float[] d = new float[n - 1]; // could optimize this out
+ float[] m = new float[n];
+
+ // Compute slopes of secant lines between successive points.
+ for (int i = 0; i < n - 1; i++) {
+ float h = x[i + 1] - x[i];
+ if (h <= 0f) {
+ throw new IllegalArgumentException("The control points must all "
+ + "have strictly increasing X values.");
+ }
+ d[i] = (y[i + 1] - y[i]) / h;
+ }
+
+ // Initialize the tangents as the average of the secants.
+ m[0] = d[0];
+ for (int i = 1; i < n - 1; i++) {
+ m[i] = (d[i - 1] + d[i]) * 0.5f;
+ }
+ m[n - 1] = d[n - 2];
+
+ // Update the tangents to preserve monotonicity.
+ for (int i = 0; i < n - 1; i++) {
+ if (d[i] == 0f) { // successive Y values are equal
+ m[i] = 0f;
+ m[i + 1] = 0f;
+ } else {
+ float a = m[i] / d[i];
+ float b = m[i + 1] / d[i];
+ if (a < 0f || b < 0f) {
+ throw new IllegalArgumentException("The control points must have "
+ + "monotonic Y values.");
+ }
+ float h = FloatMath.hypot(a, b);
+ if (h > 9f) {
+ float t = 3f / h;
+ m[i] = t * a * d[i];
+ m[i + 1] = t * b * d[i];
+ }
+ }
+ }
+ return new Spline(x, y, m);
+ }
+
+ /**
+ * Interpolates the value of Y = f(X) for given X.
+ * Clamps X to the domain of the spline.
+ *
+ * @param x The X value.
+ * @return The interpolated Y = f(X) value.
+ */
+ public float interpolate(float x) {
+ // Handle the boundary cases.
+ final int n = mX.length;
+ if (Float.isNaN(x)) {
+ return x;
+ }
+ if (x <= mX[0]) {
+ return mY[0];
+ }
+ if (x >= mX[n - 1]) {
+ return mY[n - 1];
+ }
+
+ // Find the index 'i' of the last point with smaller X.
+ // We know this will be within the spline due to the boundary tests.
+ int i = 0;
+ while (x >= mX[i + 1]) {
+ i += 1;
+ if (x == mX[i]) {
+ return mY[i];
+ }
+ }
+
+ // Perform cubic Hermite spline interpolation.
+ float h = mX[i + 1] - mX[i];
+ float t = (x - mX[i]) / h;
+ return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+ + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+ }
+
+ // For debugging.
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ final int n = mX.length;
+ str.append("[");
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ str.append(", ");
+ }
+ str.append("(").append(mX[i]);
+ str.append(", ").append(mY[i]);
+ str.append(": ").append(mM[i]).append(")");
+ }
+ str.append("]");
+ return str.toString();
+ }
+}
diff --git a/core/jni/android_util_FloatMath.cpp b/core/jni/android_util_FloatMath.cpp
index e30756b5dee3b..529fbe9b64a88 100644
--- a/core/jni/android_util_FloatMath.cpp
+++ b/core/jni/android_util_FloatMath.cpp
@@ -29,6 +29,10 @@ public:
static float ExpF(JNIEnv* env, jobject clazz, float x) {
return expf(x);
}
+
+ static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
+ return hypotf(x, y);
+ }
};
static JNINativeMethod gMathUtilsMethods[] = {
@@ -38,6 +42,7 @@ static JNINativeMethod gMathUtilsMethods[] = {
{"cos", "(F)F", (void*) MathUtilsGlue::CosF},
{"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
{"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
+ {"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
};
int register_android_util_FloatMath(JNIEnv* env)
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ee0ff8ed04500..e3c957beb7e94 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -533,13 +533,22 @@
2
@@ -552,6 +561,7 @@
@@ -559,6 +569,7 @@
@@ -566,6 +577,7 @@
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 54e2b574a59c8..b0c79fa13d95c 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -33,11 +33,11 @@ import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.Spline;
import android.util.TimeUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -98,9 +98,9 @@ final class DisplayPowerController {
// average of light samples. Different constants are used
// to calculate the average light level when adapting to brighter or
// dimmer environments.
- // This parameter only controls the averaging of light samples.
- private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 1500;
- private static final long DIMMING_LIGHT_TIME_CONSTANT = 3000;
+ // This parameter only controls the filtering of light samples.
+ private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 500;
+ private static final long DIMMING_LIGHT_TIME_CONSTANT = 2000;
// Stability requirements in milliseconds for accepting a new brightness
// level. This is used for debouncing the light sensor. Different constants
@@ -144,8 +144,7 @@ final class DisplayPowerController {
// Auto-brightness.
private boolean mUseSoftwareAutoBrightnessConfig;
- private int[] mAutoBrightnessLevelsConfig;
- private int[] mAutoBrightnessLcdBacklightValuesConfig;
+ private Spline mScreenAutoBrightnessSpline;
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
@@ -289,17 +288,18 @@ final class DisplayPowerController {
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
if (mUseSoftwareAutoBrightnessConfig) {
- mAutoBrightnessLevelsConfig = resources.getIntArray(
+ int[] lux = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels);
- mAutoBrightnessLcdBacklightValuesConfig = resources.getIntArray(
+ int[] screenBrightness = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
- if (mAutoBrightnessLcdBacklightValuesConfig.length
- != mAutoBrightnessLevelsConfig.length + 1) {
+
+ mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
+ if (mScreenAutoBrightnessSpline == null) {
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
- + "(size " + mAutoBrightnessLcdBacklightValuesConfig.length + ") "
- + "should have exactly one more entry than "
- + "config_autoBrightnessLevels (size "
- + mAutoBrightnessLevelsConfig.length + "). "
+ + "(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;
}
@@ -322,6 +322,31 @@ final class DisplayPowerController {
}
}
+ private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+ try {
+ final int n = brightness.length;
+ float[] x = new float[n];
+ float[] y = new float[n];
+ y[0] = brightness[0];
+ for (int i = 1; i < n; i++) {
+ x[i] = lux[i - 1];
+ y[i] = brightness[i];
+ }
+
+ Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ if (false) {
+ 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;
+ }
+ }
+
/**
* Returns true if the proximity sensor screen-off function is available.
*/
@@ -768,13 +793,13 @@ final class DisplayPowerController {
return;
}
- final int newScreenAutoBrightness = mapLuxToBrightness(mLightMeasurement,
- mAutoBrightnessLevelsConfig,
- mAutoBrightnessLcdBacklightValuesConfig);
+ final int newScreenAutoBrightness = interpolateBrightness(
+ mScreenAutoBrightnessSpline, mLightMeasurement);
if (mScreenAutoBrightness != newScreenAutoBrightness) {
if (DEBUG) {
Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
- + mScreenAutoBrightness);
+ + mScreenAutoBrightness + "newScreenAutoBrightness="
+ + newScreenAutoBrightness);
}
mScreenAutoBrightness = newScreenAutoBrightness;
@@ -784,20 +809,8 @@ final class DisplayPowerController {
}
}
- /**
- * Maps a light sensor measurement in lux to a brightness value given
- * a table of lux breakpoint values and a table of brightnesses that
- * is one element larger.
- */
- private static int mapLuxToBrightness(float lux,
- int[] fromLux, int[] toBrightness) {
- // TODO implement interpolation and possibly range expansion
- int level = 0;
- final int count = fromLux.length;
- while (level < count && lux >= fromLux[level]) {
- level += 1;
- }
- return toBrightness[level];
+ private static int interpolateBrightness(Spline spline, float lux) {
+ return Math.min(255, Math.max(0, (int)Math.round(spline.interpolate(lux))));
}
private void sendOnStateChanged() {
@@ -839,10 +852,7 @@ final class DisplayPowerController {
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
pw.println(" mUseSoftwareAutoBrightnessConfig="
+ mUseSoftwareAutoBrightnessConfig);
- pw.println(" mAutoBrightnessLevelsConfig="
- + Arrays.toString(mAutoBrightnessLevelsConfig));
- pw.println(" mAutoBrightnessLcdBacklightValuesConfig="
- + Arrays.toString(mAutoBrightnessLcdBacklightValuesConfig));
+ pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
if (Looper.myLooper() == mHandler.getLooper()) {