Merge "Added app context to smart screen brightness."
diff --git a/api/system-current.txt b/api/system-current.txt
index dfcae63..fb9d951 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1471,6 +1471,8 @@
public final class BrightnessConfiguration implements android.os.Parcelable {
method public int describeContents();
+ method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+ method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
method public android.util.Pair<float[], float[]> getCurve();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1478,10 +1480,22 @@
public static class BrightnessConfiguration.Builder {
ctor public BrightnessConfiguration.Builder(float[], float[]);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
method public android.hardware.display.BrightnessConfiguration build();
+ method public int getMaxCorrectionsByCategory();
+ method public int getMaxCorrectionsByPackageName();
method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
}
+ public final class BrightnessCorrection implements android.os.Parcelable {
+ method public float apply(float);
+ method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+ }
+
public final class DisplayManager {
method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 7e52ca3..be054297 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,26 +19,54 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
/** @hide */
@SystemApi
@TestApi
public final class BrightnessConfiguration implements Parcelable {
+ private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+ private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+ private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
+ private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
+ private static final String ATTR_LUX = "lux";
+ private static final String ATTR_NITS = "nits";
+ private static final String ATTR_DESCRIPTION = "description";
+ private static final String ATTR_PACKAGE_NAME = "package-name";
+ private static final String ATTR_CATEGORY = "category";
+
private final float[] mLux;
private final float[] mNits;
+ private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private final String mDescription;
- private BrightnessConfiguration(float[] lux, float[] nits, String description) {
+ private BrightnessConfiguration(float[] lux, float[] nits,
+ Map<String, BrightnessCorrection> correctionsByPackageName,
+ Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
mLux = lux;
mNits = nits;
+ mCorrectionsByPackageName = correctionsByPackageName;
+ mCorrectionsByCategory = correctionsByCategory;
mDescription = description;
}
@@ -56,6 +84,38 @@
}
/**
+ * Returns a brightness correction by app, or null.
+ *
+ * @param packageName
+ * The app's package name.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+ return mCorrectionsByPackageName.get(packageName);
+ }
+
+ /**
+ * Returns a brightness correction by app category, or null.
+ *
+ * @param category
+ * The app category.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByCategory(int category) {
+ return mCorrectionsByCategory.get(category);
+ }
+
+ /**
* Returns description string.
* @hide
*/
@@ -67,6 +127,20 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloatArray(mLux);
dest.writeFloatArray(mNits);
+ dest.writeInt(mCorrectionsByPackageName.size());
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeString(packageName);
+ correction.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mCorrectionsByCategory.size());
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeInt(category);
+ correction.writeToParcel(dest, flags);
+ }
dest.writeString(mDescription);
}
@@ -85,7 +159,14 @@
}
sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
}
- sb.append("], '");
+ sb.append("], {");
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
+ }
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
+ }
+ sb.append("}, '");
if (mDescription != null) {
sb.append(mDescription);
}
@@ -98,6 +179,8 @@
int result = 1;
result = result * 31 + Arrays.hashCode(mLux);
result = result * 31 + Arrays.hashCode(mNits);
+ result = result * 31 + mCorrectionsByPackageName.hashCode();
+ result = result * 31 + mCorrectionsByCategory.hashCode();
if (mDescription != null) {
result = result * 31 + mDescription.hashCode();
}
@@ -114,6 +197,8 @@
}
final BrightnessConfiguration other = (BrightnessConfiguration) o;
return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
+ && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
+ && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
&& Objects.equals(mDescription, other.mDescription);
}
@@ -123,7 +208,25 @@
float[] lux = in.createFloatArray();
float[] nits = in.createFloatArray();
Builder builder = new Builder(lux, nits);
- builder.setDescription(in.readString());
+
+ int n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final String packageName = in.readString();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+
+ n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final int category = in.readInt();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByCategory(category, correction);
+ }
+
+ final String description = in.readString();
+ builder.setDescription(description);
return builder.build();
}
@@ -133,11 +236,146 @@
};
/**
+ * Writes the configuration to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+ if (mDescription != null) {
+ serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
+ }
+ for (int i = 0; i < mLux.length; i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+ serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
+ serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+ serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ for (Map.Entry<String, BrightnessCorrection> entry :
+ mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ }
+
+ /**
+ * Read a configuration from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String description = null;
+ List<Float> luxList = new ArrayList<>();
+ List<Float> nitsList = new ArrayList<>();
+ Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
+ Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
+ final int configDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, configDepth)) {
+ if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+ description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+ final int curveDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, curveDepth)) {
+ if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+ continue;
+ }
+ final float lux = loadFloatFromXml(parser, ATTR_LUX);
+ final float nits = loadFloatFromXml(parser, ATTR_NITS);
+ luxList.add(lux);
+ nitsList.add(nits);
+ }
+ }
+ if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
+ final int correctionsDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
+ if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
+ continue;
+ }
+ final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+ BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
+ if (packageName != null) {
+ correctionsByPackageName.put(packageName, correction);
+ } else if (categoryText != null) {
+ try {
+ final int category = Integer.parseInt(categoryText);
+ correctionsByCategory.put(category, correction);
+ } catch (NullPointerException | NumberFormatException e) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ final int n = luxList.size();
+ float[] lux = new float[n];
+ float[] nits = new float[n];
+ for (int i = 0; i < n; i++) {
+ lux[i] = luxList.get(i);
+ nits[i] = nitsList.get(i);
+ }
+ final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
+ nits);
+ builder.setDescription(description);
+ for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByCategory(category, correction);
+ }
+ return builder.build();
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ /**
* A builder class for {@link BrightnessConfiguration}s.
*/
public static class Builder {
+ private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
+ private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
+
private float[] mCurveLux;
private float[] mCurveNits;
+ private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private String mDescription;
/**
@@ -169,6 +407,88 @@
checkMonotonic(nits, false /*strictly increasing*/, "nits");
mCurveLux = lux;
mCurveNits = nits;
+ mCorrectionsByPackageName = new HashMap<>();
+ mCorrectionsByCategory = new HashMap<>();
+ }
+
+ /**
+ * Returns the maximum number of corrections by package name allowed.
+ *
+ * @return The maximum number of corrections by package name allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByPackageName() {
+ return MAX_CORRECTIONS_BY_PACKAGE_NAME;
+ }
+
+ /**
+ * Returns the maximum number of corrections by category allowed.
+ *
+ * @return The maximum number of corrections by category allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByCategory() {
+ return MAX_CORRECTIONS_BY_CATEGORY;
+ }
+
+ /**
+ * Add a brightness correction by app package name.
+ * This correction is applied whenever an app with this package name has the top activity
+ * of the focused stack.
+ *
+ * @param packageName
+ * The app's package name.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentExceptions
+ * Maximum number of corrections by package name exceeded (see
+ * {@link #getMaxCorrectionsByPackageName}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByPackageName(String packageName,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
+ throw new IllegalArgumentException("Too many corrections by package name");
+ }
+ mCorrectionsByPackageName.put(packageName, correction);
+ return this;
+ }
+
+ /**
+ * Add a brightness correction by app category.
+ * This correction is applied whenever an app with this category has the top activity of
+ * the focused stack, and only if a correction by package name has not been applied.
+ *
+ * @param category
+ * The {@link android.content.pm.ApplicationInfo#category app category}.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentException
+ * Maximum number of corrections by category exceeded (see
+ * {@link #getMaxCorrectionsByCategory}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
+ throw new IllegalArgumentException("Too many corrections by category");
+ }
+ mCorrectionsByCategory.put(category, correction);
+ return this;
}
/**
@@ -191,7 +511,8 @@
if (mCurveLux == null || mCurveNits == null) {
throw new IllegalStateException("A curve must be set!");
}
- return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
+ return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
+ mCorrectionsByCategory, mDescription);
}
private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl
new file mode 100644
index 0000000..3abe29c
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 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 BrightnessCorrection;
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
new file mode 100644
index 0000000..c4e0e3b
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 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.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MathUtils;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
+ * actual correction scheme.
+ * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
+ * name and category) to corrections that need to be applied to the brightness within that context.
+ * Corrections are currently done by the app that has the top activity of the focused stack, either
+ * by its package name, or (if its package name is not mapped to any correction) by its category.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BrightnessCorrection implements Parcelable {
+
+ private static final int SCALE_AND_TRANSLATE_LOG = 1;
+
+ private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
+
+ private BrightnessCorrectionImplementation mImplementation;
+
+ // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
+ // make this class abstract and use composition instead of inheritence.
+ private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
+ mImplementation = implementation;
+ }
+
+ /**
+ * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @param scale
+ * How much to scale the log brightness.
+ * @param translate
+ * How much to translate the log brightness.
+ *
+ * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @throws IllegalArgumentException
+ * - scale or translate are NaN.
+ */
+ @NonNull
+ public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
+ BrightnessCorrectionImplementation implementation =
+ new ScaleAndTranslateLog(scale, translate);
+ return new BrightnessCorrection(implementation);
+ }
+
+ /**
+ * Applies the brightness correction to a given brightness.
+ *
+ * @param brightness
+ * The brightness.
+ *
+ * @return The corrected brightness.
+ */
+ public float apply(float brightness) {
+ return mImplementation.apply(brightness);
+ }
+
+ /**
+ * Returns a string representation.
+ *
+ * @return A string representation.
+ */
+ public String toString() {
+ return mImplementation.toString();
+ }
+
+ public static final Creator<BrightnessCorrection> CREATOR =
+ new Creator<BrightnessCorrection>() {
+ public BrightnessCorrection createFromParcel(Parcel in) {
+ final int type = in.readInt();
+ switch (type) {
+ case SCALE_AND_TRANSLATE_LOG:
+ return ScaleAndTranslateLog.readFromParcel(in);
+ }
+ return null;
+ }
+
+ public BrightnessCorrection[] newArray(int size) {
+ return new BrightnessCorrection[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mImplementation.writeToParcel(dest);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes the correction to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ mImplementation.saveToXml(serializer);
+ }
+
+ /**
+ * Read a correction from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
+ return ScaleAndTranslateLog.loadFromXml(parser);
+ }
+ }
+ return null;
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ private interface BrightnessCorrectionImplementation {
+ float apply(float brightness);
+ String toString();
+ void writeToParcel(Parcel dest);
+ void saveToXml(XmlSerializer serializer) throws IOException;
+ // Package-private static methods:
+ // static BrightnessCorrection readFromParcel(Parcel in);
+ // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ // XmlPullParserException;
+ }
+
+ /**
+ * A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ */
+ private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
+ private static final float MIN_SCALE = 0.5f;
+ private static final float MAX_SCALE = 2.0f;
+ private static final float MIN_TRANSLATE = -0.6f;
+ private static final float MAX_TRANSLATE = 0.7f;
+
+ private static final String ATTR_SCALE = "scale";
+ private static final String ATTR_TRANSLATE = "translate";
+
+ private final float mScale;
+ private final float mTranslate;
+
+ ScaleAndTranslateLog(float scale, float translate) {
+ if (Float.isNaN(scale) || Float.isNaN(translate)) {
+ throw new IllegalArgumentException("scale and translate must be numbers");
+ }
+ mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
+ }
+
+ @Override
+ public float apply(float brightness) {
+ return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
+ }
+
+ @Override
+ public String toString() {
+ return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(SCALE_AND_TRANSLATE_LOG);
+ dest.writeFloat(mScale);
+ dest.writeFloat(mTranslate);
+ }
+
+ @Override
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
+ serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+ serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ }
+
+ static BrightnessCorrection readFromParcel(Parcel in) {
+ float scale = in.readFloat();
+ float translate = in.readFloat();
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+
+ static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final float scale = loadFloatFromXml(parser, ATTR_SCALE);
+ final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15..52eccca 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,6 +17,13 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -27,6 +34,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.util.EventLog;
@@ -34,14 +43,15 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
class AutomaticBrightnessController {
private static final String TAG = "AutomaticBrightnessController";
- private static final boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
// If true, enables the use of the screen auto-brightness adjustment setting.
@@ -66,6 +76,8 @@
private static final int MSG_UPDATE_AMBIENT_LUX = 1;
private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+ private static final int MSG_UPDATE_FOREGROUND_APP = 4;
+ private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
@@ -126,6 +138,8 @@
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ private boolean mLoggingEnabled;
+
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
// May be 0 if no warm-up is required.
@@ -192,6 +206,19 @@
private float mShortTermModelAnchor;
private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
+ // Context-sensitive brightness configurations require keeping track of the foreground app's
+ // package name and category, which is done by registering a TaskStackListener to call back to
+ // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
+ // package namd and PackageManager to get its category (so might as well cache them).
+ private int mUserId;
+ private String mForegroundAppPackageName;
+ private String mPendingForegroundAppPackageName;
+ private @ApplicationInfo.Category int mForegroundAppCategory;
+ private @ApplicationInfo.Category int mPendingForegroundAppCategory;
+ private TaskStackListenerImpl mTaskStackListener;
+ private IActivityTaskManager mActivityTaskManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, BrightnessMappingStrategy mapper,
int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -226,6 +253,42 @@
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
+
+ mUserId = ActivityManager.getCurrentUser();
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mTaskStackListener = new TaskStackListenerImpl();
+ mForegroundAppPackageName = null;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Update the current user's ID.
+ *
+ * @param userId
+ * The current user's ID.
+ */
+ public void onSwitchUser(int userId) {
+ mUserId = userId;
}
public int getAutomaticScreenBrightness() {
@@ -290,7 +353,7 @@
}
final int oldPolicy = mDisplayPolicy;
mDisplayPolicy = policy;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
@@ -317,7 +380,7 @@
mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
mShortTermModelValid = true;
mShortTermModelAnchor = mAmbientLux;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
}
return true;
@@ -330,7 +393,7 @@
}
private void invalidateShortTermModel() {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: invalidate user data");
}
mShortTermModelValid = false;
@@ -383,7 +446,11 @@
pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ mBrightnessAdjustmentSampleOldBrightness);
- pw.println(" mShortTermModelValid=" + mShortTermModelValid);
+ pw.println(" mUserId=" + mUserId);
+ pw.println(" mForegroundAppPackageName=" + mForegroundAppPackageName);
+ pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+ pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
+ pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
pw.println();
mBrightnessMapper.dump(pw);
@@ -399,6 +466,7 @@
mLightSensorEnabled = true;
mLightSensorEnableTime = SystemClock.uptimeMillis();
mCurrentLightSensorRate = mInitialLightSensorRate;
+ registerForegroundAppUpdater();
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
mCurrentLightSensorRate * 1000, mHandler);
return true;
@@ -411,6 +479,7 @@
mAmbientLightRingBuffer.clear();
mCurrentLightSensorRate = -1;
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ unregisterForegroundAppUpdater();
mSensorManager.unregisterListener(mLightSensorListener);
}
return false;
@@ -441,7 +510,7 @@
private void adjustLightSensorRate(int lightSensorRate) {
// if the light sensor rate changed, update the sensor listener
if (lightSensorRate != mCurrentLightSensorRate) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "adjustLightSensorRate: " +
"previousRate=" + mCurrentLightSensorRate + ", " +
"currentRate=" + lightSensorRate);
@@ -458,7 +527,7 @@
}
private void setAmbientLux(float lux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAmbientLux(" + lux + ")");
}
if (lux < 0) {
@@ -476,7 +545,7 @@
final float maxAmbientLux =
mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
}
@@ -490,7 +559,7 @@
}
private float calculateAmbientLux(long now, long horizon) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
}
final int N = mAmbientLightRingBuffer.size();
@@ -509,7 +578,7 @@
break;
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
mAmbientLightRingBuffer.getTime(endIndex) + ", " +
mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -527,7 +596,7 @@
final long startTime = eventTime - now;
float weight = calculateWeight(startTime, endTime);
float lux = mAmbientLightRingBuffer.getLux(i);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
"lux=" + lux + ", " +
"weight=" + weight);
@@ -536,7 +605,7 @@
sum += lux * weight;
endTime = startTime;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: " +
"totalWeight=" + totalWeight + ", " +
"newAmbientLux=" + (sum / totalWeight));
@@ -591,7 +660,7 @@
final long timeWhenSensorWarmedUp =
mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
if (time < timeWhenSensorWarmedUp) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
"time=" + time + ", " +
"timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -602,7 +671,7 @@
}
setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
mAmbientLuxValid = true;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Initializing: " +
"mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
"mAmbientLux=" + mAmbientLux);
@@ -630,10 +699,10 @@
&& fastAmbientLux <= mAmbientDarkeningThreshold
&& nextDarkenTransition <= time)) {
setAmbientLux(fastAmbientLux);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
+ ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
- + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ "mAmbientLux=" + mAmbientLux);
}
@@ -650,7 +719,7 @@
// weighted ambient lux or not.
nextTransitionTime =
nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
@@ -662,7 +731,8 @@
return;
}
- float value = mBrightnessMapper.getBrightness(mAmbientLux);
+ float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+ mForegroundAppCategory);
int newScreenAutoBrightness =
clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -673,7 +743,7 @@
if (mScreenAutoBrightness != -1
&& newScreenAutoBrightness > mScreenDarkeningThreshold
&& newScreenAutoBrightness < mScreenBrighteningThreshold) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
+ " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
}
@@ -681,8 +751,7 @@
}
if (mScreenAutoBrightness != newScreenAutoBrightness) {
-
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAutoBrightness: " +
"mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
"newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -718,18 +787,11 @@
BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
}
- private void cancelBrightnessAdjustmentSample() {
- if (mBrightnessAdjustmentSamplePending) {
- mBrightnessAdjustmentSamplePending = false;
- mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
- }
- }
-
private void collectBrightnessAdjustmentSample() {
if (mBrightnessAdjustmentSamplePending) {
mBrightnessAdjustmentSamplePending = false;
if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
"lux=" + mAmbientLux + ", " +
"brightness=" + mScreenAutoBrightness + ", " +
@@ -745,6 +807,68 @@
}
}
+ // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
+ // foreground app's package name and category and correct the brightness accordingly.
+ private void registerForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ // This will not get called until the foreground app changes for the first time, so
+ // call it explicitly to get the current foreground app's info.
+ updateForegroundApp();
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+
+ private void unregisterForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ mForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ // Set the foreground app's package name and category, so brightness can be corrected per app.
+ private void updateForegroundApp() {
+ // The ActivityTaskManager's lock tends to get contended, so this is done in a background
+ // thread and applied via this thread's handler synchronously.
+ BackgroundThread.getHandler().post(new Runnable() {
+ public void run() {
+ try {
+ // The foreground app is the top activity of the focused tasks stack.
+ final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+ if (info == null || info.topActivity == null) {
+ return;
+ }
+ final String packageName = info.topActivity.getPackageName();
+ // If the app didn't change, there's nothing to do. Otherwise, we have to
+ // update the category and re-apply the brightness correction.
+ if (mForegroundAppPackageName != null
+ && mForegroundAppPackageName.equals(packageName)) {
+ return;
+ }
+ mPendingForegroundAppPackageName = packageName;
+ ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
+ 0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
+ mPendingForegroundAppCategory = app.category;
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ }
+ });
+ }
+
+ private void updateForegroundAppSync() {
+ mForegroundAppPackageName = mPendingForegroundAppPackageName;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = mPendingForegroundAppCategory;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ updateAutoBrightness(true /* sendUpdate */);
+ }
+
private final class AutomaticBrightnessHandler extends Handler {
public AutomaticBrightnessHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -764,6 +888,14 @@
case MSG_INVALIDATE_SHORT_TERM_MODEL:
invalidateShortTermModel();
break;
+
+ case MSG_UPDATE_FOREGROUND_APP:
+ updateForegroundApp();
+ break;
+
+ case MSG_UPDATE_FOREGROUND_APP_SYNC:
+ updateForegroundAppSync();
+ break;
}
}
}
@@ -784,6 +916,15 @@
}
};
+ // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
+ // moving to top.
+ class TaskStackListenerImpl extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
+ }
+ }
+
/** Callbacks to request updates to the display's power state. */
interface Callbacks {
void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 76c191d..9fce644 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,9 +17,11 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessCorrection;
import android.os.PowerManager;
import android.util.MathUtils;
import android.util.Pair;
@@ -42,11 +44,12 @@
*/
public abstract class BrightnessMappingStrategy {
private static final String TAG = "BrightnessMappingStrategy";
- private static final boolean DEBUG = false;
private static final float LUX_GRAD_SMOOTHING = 0.25f;
private static final float MAX_GRAD = 1.0f;
+ protected boolean mLoggingEnabled;
+
private static final Plog PLOG = Plog.createSystemPlog(TAG);
@Nullable
@@ -161,6 +164,22 @@
}
/**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
* Sets the {@link BrightnessConfiguration}.
*
* @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -170,15 +189,33 @@
public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
/**
- * Returns the desired brightness of the display based on the current ambient lux.
+ * Returns the desired brightness of the display based on the current ambient lux, including
+ * any context-related corrections.
*
* 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.
+ * @param packageName the foreground app package name.
+ * @param category the foreground app package category.
* @return The desired brightness of the display normalized to the range [0, 1.0].
*/
- public abstract float getBrightness(float lux);
+ public abstract float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category);
+
+ /**
+ * Returns the desired brightness of the display based on the current ambient lux.
+ *
+ * The returned brightness wil 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 normalized to the range [0, 1.0].
+ */
+ public float getBrightness(float lux) {
+ return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
+ }
/**
* Returns the current auto-brightness adjustment.
@@ -239,13 +276,13 @@
public abstract void dump(PrintWriter pw);
- private static float normalizeAbsoluteBrightness(int brightness) {
+ protected float normalizeAbsoluteBrightness(int brightness) {
brightness = MathUtils.constrain(brightness,
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
return (float) brightness / PowerManager.BRIGHTNESS_ON;
}
- private static Pair<float[], float[]> insertControlPoint(
+ private Pair<float[], float[]> insertControlPoint(
float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
final int idx = findInsertionPoint(luxLevels, lux);
final float[] newLuxLevels;
@@ -278,7 +315,7 @@
* This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
* than val, then it will return the length of arr as the insertion point.
*/
- private static int findInsertionPoint(float[] arr, float val) {
+ private int findInsertionPoint(float[] arr, float val) {
for (int i = 0; i < arr.length; i++) {
if (val <= arr[i]) {
return i;
@@ -287,8 +324,8 @@
return arr.length;
}
- private static void smoothCurve(float[] lux, float[] brightness, int idx) {
- if (DEBUG) {
+ private void smoothCurve(float[] lux, float[] brightness, int idx) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unsmoothed curve", lux, brightness);
}
float prevLux = lux[idx];
@@ -323,19 +360,19 @@
prevBrightness = newBrightness;
brightness[i] = newBrightness;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("smoothed curve", lux, brightness);
}
}
- private static float permissibleRatio(float currLux, float prevLux) {
+ private float permissibleRatio(float currLux, float prevLux) {
return MathUtils.exp(MAX_GRAD
* (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
- MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
}
- private static float inferAutoBrightnessAdjustment(float maxGamma,
- float desiredBrightness, float currentBrightness) {
+ protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
+ float currentBrightness) {
float adjustment = 0;
float gamma = Float.NaN;
// Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -355,7 +392,7 @@
adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
}
adjustment = MathUtils.constrain(adjustment, -1, +1);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -364,16 +401,16 @@
return adjustment;
}
- private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+ protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
float userLux, float userBrightness, float adjustment, float maxGamma) {
float[] newLux = lux;
float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unadjusted curve", newLux, newBrightness);
}
adjustment = MathUtils.constrain(adjustment, -1, 1);
float gamma = MathUtils.pow(maxGamma, -adjustment);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
}
@@ -382,7 +419,7 @@
newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
}
if (userLux != -1) {
@@ -390,7 +427,7 @@
userBrightness);
newLux = curve.first;
newBrightness = curve.second;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
// This is done for comparison.
curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -440,7 +477,7 @@
mAutoBrightnessAdjustment = 0;
mUserLux = -1;
mUserBrightness = -1;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("simple mapping strategy");
}
computeSpline();
@@ -452,7 +489,8 @@
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
return mSpline.interpolate(lux);
}
@@ -467,7 +505,7 @@
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -485,7 +523,7 @@
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -494,7 +532,7 @@
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -507,7 +545,7 @@
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -614,7 +652,7 @@
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
mDefaultConfig = config;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("physical mapping strategy");
}
mConfig = config;
@@ -629,7 +667,7 @@
if (config.equals(mConfig)) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("brightness configuration");
}
mConfig = config;
@@ -638,9 +676,17 @@
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
float nits = mBrightnessSpline.interpolate(lux);
float backlight = mNitsToBacklightSpline.interpolate(nits);
+ // Correct the brightness according to the current application and its category, but
+ // only if no user data point is set (as this will oevrride the user setting).
+ if (mUserLux == -1) {
+ backlight = correctBrightness(backlight, packageName, category);
+ } else if (mLoggingEnabled) {
+ Slog.d(TAG, "user point set, correction not applied");
+ }
return backlight;
}
@@ -655,7 +701,7 @@
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -673,7 +719,7 @@
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -682,7 +728,7 @@
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -695,7 +741,7 @@
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -758,5 +804,21 @@
Spline spline = Spline.createSpline(curve.first, curve.second);
return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
}
+
+ private float correctBrightness(float brightness, String packageName, int category) {
+ if (packageName != null) {
+ BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
+ BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ return brightness;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a1a9a2..b1ba05c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2144,6 +2144,14 @@
mContext.getPackageName());
}
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+ }
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 27cad1e..abbfc7b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,14 +17,9 @@
package com.android.server.display;
import android.content.Intent;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.NumberFormatException;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -46,6 +41,10 @@
return setBrightness();
case "reset-brightness-configuration":
return resetBrightnessConfiguration();
+ case "ab-logging-enable":
+ return setAutoBrightnessLoggingEnabled(true);
+ case "ab-logging-disable":
+ return setAutoBrightnessLoggingEnabled(false);
default:
return handleDefaultCommands(cmd);
}
@@ -62,6 +61,10 @@
pw.println(" Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
pw.println(" reset-brightness-configuration");
pw.println(" Reset the brightness to its default configuration.");
+ pw.println(" ab-logging-enable");
+ pw.println(" Enable auto-brightness logging.");
+ pw.println(" ab-logging-disable");
+ pw.println(" Disable auto-brightness logging.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -89,4 +92,9 @@
mService.resetBrightnessConfiguration();
return 0;
}
+
+ private int setAutoBrightnessLoggingEnabled(boolean enabled) {
+ mService.setAutoBrightnessLoggingEnabled(enabled);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270b..c9ed9f7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -523,6 +523,9 @@
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
mBrightnessTracker.onSwitchUser(newUserId);
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.onSwitchUser(newUserId);
+ }
}
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -1836,4 +1839,10 @@
mHandler.sendMessage(msg);
}
}
+
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.setLoggingEnabled(enabled);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 89cef62..9aec43b 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,13 +16,6 @@
package com.android.server.display;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -30,13 +23,20 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.Pair;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -50,12 +50,9 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
-import libcore.io.IoUtils;
-
/**
* Manages persistent state recorded by the display manager service as an XML file.
* Caller must acquire lock on the data store before accessing it.
@@ -110,14 +107,9 @@
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_PACKAGE_NAME = "package-name";
private static final String ATTR_TIME_STAMP = "timestamp";
- private static final String ATTR_LUX = "lux";
- private static final String ATTR_NITS = "nits";
- private static final String ATTR_DESCRIPTION = "description";
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -646,7 +638,8 @@
}
try {
- BrightnessConfiguration config = loadConfigurationFromXml(parser);
+ BrightnessConfiguration config =
+ BrightnessConfiguration.loadFromXml(parser);
if (userSerial >= 0 && config != null) {
mConfigurations.put(userSerial, config);
if (timeStamp != -1) {
@@ -663,56 +656,6 @@
}
}
- private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- String description = null;
- Pair<float[], float[]> curve = null;
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
- description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
- curve = loadCurveFromXml(parser);
- }
- }
- if (curve == null) {
- return null;
- }
- final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
- curve.first, curve.second);
- builder.setDescription(description);
- return builder.build();
- }
-
- private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
- 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);
@@ -728,27 +671,11 @@
if (timestamp != -1) {
serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
}
- saveConfigurationToXml(serializer, config);
+ config.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
}
}
- private static void saveConfigurationToXml(XmlSerializer serializer,
- BrightnessConfiguration config) throws IOException {
- serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
- if (config.getDescription() != null) {
- serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
- }
- 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);