Merge "Added system-wide minimum brightness curve." into pi-dev

This commit is contained in:
TreeHugger Robot
2018-04-10 11:28:44 +00:00
committed by Android (Google) Code Review
12 changed files with 280 additions and 3 deletions

View File

@@ -1255,6 +1255,7 @@ package android.hardware.display {
method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
method public android.util.Pair<float[], float[]> getMinimumBrightnessCurve();
method public android.graphics.Point getStableDisplaySize();
method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
method public void setSaturationLevel(float);

View File

@@ -86,7 +86,9 @@ public final class BrightnessConfiguration implements Parcelable {
sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
}
sb.append("], '");
sb.append(mDescription);
if (mDescription != null) {
sb.append(mDescription);
}
sb.append("'}");
return sb.toString();
}
@@ -96,7 +98,9 @@ public final class BrightnessConfiguration implements Parcelable {
int result = 1;
result = result * 31 + Arrays.hashCode(mLux);
result = result * 31 + Arrays.hashCode(mNits);
result = result * 31 + mDescription.hashCode();
if (mDescription != null) {
result = result * 31 + mDescription.hashCode();
}
return result;
}

View File

@@ -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 Curve;

View File

@@ -0,0 +1,62 @@
/*
* 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.os.Parcel;
import android.os.Parcelable;
/** @hide */
public final class Curve implements Parcelable {
private final float[] mX;
private final float[] mY;
public Curve(float[] x, float[] y) {
mX = x;
mY = y;
}
public float[] getX() {
return mX;
}
public float[] getY() {
return mY;
}
public static final Creator<Curve> CREATOR = new Creator<Curve>() {
public Curve createFromParcel(Parcel in) {
float[] x = in.createFloatArray();
float[] y = in.createFloatArray();
return new Curve(x, y);
}
public Curve[] newArray(int size) {
return new Curve[size];
}
};
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeFloatArray(mX);
out.writeFloatArray(mY);
}
@Override
public int describeContents() {
return 0;
}
}

View File

@@ -28,6 +28,7 @@ import android.content.Context;
import android.graphics.Point;
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
import android.view.Surface;
@@ -747,6 +748,22 @@ public final class DisplayManager {
mGlobal.setTemporaryAutoBrightnessAdjustment(adjustment);
}
/**
* Returns the minimum brightness curve, which guarantess that any brightness curve that dips
* below it is rejected by the system.
* This prevent auto-brightness from setting the screen so dark as to prevent the user from
* resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
* in that ambient brightness.
*
* @return The minimum brightness curve (as lux values and their corresponding nits values).
*
* @hide
*/
@SystemApi
public Pair<float[], float[]> getMinimumBrightnessCurve() {
return mGlobal.getMinimumBrightnessCurve();
}
/**
* Listens for changes in available display devices.
*/

View File

@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAdjustments;
@@ -562,6 +563,24 @@ public final class DisplayManagerGlobal {
}
}
/**
* Returns the minimum brightness curve, which guarantess that any brightness curve that dips
* below it is rejected by the system.
* This prevent auto-brightness from setting the screen so dark as to prevent the user from
* resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
* in that ambient brightness.
*
* @return The minimum brightness curve (as lux values and their corresponding nits values).
*/
public Pair<float[], float[]> getMinimumBrightnessCurve() {
try {
Curve curve = mDm.getMinimumBrightnessCurve();
return Pair.create(curve.getX(), curve.getY());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Retrieves ambient brightness stats.
*/

View File

@@ -19,6 +19,7 @@ package android.hardware.display;
import android.content.pm.ParceledListSlice;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.WifiDisplay;
@@ -112,4 +113,7 @@ interface IDisplayManager {
// Temporarily sets the auto brightness adjustment factor.
void setTemporaryAutoBrightnessAdjustment(float adjustment);
// Get the minimum brightness curve.
Curve getMinimumBrightnessCurve();
}

View File

@@ -1289,6 +1289,32 @@
in darkness (although they may not be visible in a bright room). -->
<integer name="config_screenBrightnessDark">1</integer>
<!-- Array of lux values to define the minimum brightness curve, which guarantees that any
brightness curve that dips below it is rejected by the system.
This prevents auto-brightness from setting the screen so dark as to prevent the user from
resetting or disabling it.
The values must be non-negative and strictly increasing, and correspond to the values in
the config_minimumBrightnessCurveNits array. -->
<array name="config_minimumBrightnessCurveLux">
<item>0.0</item>
<item>2000.0</item>
<item>4000.0</item>
</array>
<!-- Array of nits values to define the minimum brightness curve, which guarantees that any
brightness curve that dips below it is rejected by the system.
This should map lux to the absolute minimum nits that are still readable in that ambient
brightness.
The values must be non-negative and non-decreasing, and correspond to the values in the
config_minimumBrightnessCurveLux array. -->
<array name="config_minimumBrightnessCurveNits">
<item>0.0</item>
<item>50.0</item>
<item>90.0</item>
</array>
<!-- Array of light sensor lux values to define our levels for auto backlight brightness support.
The N entries of this array define N + 1 control points as follows:
(1-based arrays)

View File

@@ -1810,6 +1810,8 @@
<java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
<java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
<java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
<java-symbol type="array" name="config_minimumBrightnessCurveLux" />
<java-symbol type="array" name="config_minimumBrightnessCurveNits" />
<java-symbol type="array" name="config_protectedNetworks" />
<java-symbol type="array" name="config_statusBarIcons" />
<java-symbol type="array" name="config_tether_bluetooth_regexs" />

View File

@@ -41,7 +41,7 @@ public class BrightnessConfigurationTest {
};
private static final float[] NITS_LEVELS = {
0.5f,
1f,
90f,
100f,
};

View File

@@ -36,11 +36,13 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
@@ -72,8 +74,10 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Spline;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -276,6 +280,11 @@ public final class DisplayManagerService extends SystemService {
private final Injector mInjector;
// The minimum brightness curve, which guarantess that any brightness curve that dips below it
// is rejected by the system.
private final Curve mMinimumBrightnessCurve;
private final Spline mMinimumBrightnessSpline;
public DisplayManagerService(Context context) {
this(context, new Injector());
}
@@ -289,8 +298,15 @@ public final class DisplayManagerService extends SystemService {
mUiHandler = UiThread.getHandler();
mDisplayAdapterListener = new DisplayAdapterListener();
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
float[] lux = getFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_minimumBrightnessCurveLux));
float[] nits = getFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_minimumBrightnessCurveNits));
mMinimumBrightnessCurve = new Curve(lux, nits);
mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
@@ -1032,9 +1048,15 @@ public final class DisplayManagerService extends SystemService {
}
}
@VisibleForTesting
Curve getMinimumBrightnessCurveInternal() {
return mMinimumBrightnessCurve;
}
private void setBrightnessConfigurationForUserInternal(
@Nullable BrightnessConfiguration c, @UserIdInt int userId,
@Nullable String packageName) {
validateBrightnessConfiguration(c);
final int userSerial = getUserManager().getUserSerialNumber(userId);
synchronized (mSyncRoot) {
try {
@@ -1049,6 +1071,28 @@ public final class DisplayManagerService extends SystemService {
}
}
@VisibleForTesting
void validateBrightnessConfiguration(BrightnessConfiguration config) {
if (config == null) {
return;
}
if (isBrightnessConfigurationTooDark(config)) {
throw new IllegalArgumentException("brightness curve is too dark");
}
}
private boolean isBrightnessConfigurationTooDark(BrightnessConfiguration config) {
Pair<float[], float[]> curve = config.getCurve();
float[] lux = curve.first;
float[] nits = curve.second;
for (int i = 0; i < lux.length; i++) {
if (nits[i] < mMinimumBrightnessSpline.interpolate(lux[i])) {
return true;
}
}
return false;
}
private void loadBrightnessConfiguration() {
synchronized (mSyncRoot) {
final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId);
@@ -1369,6 +1413,16 @@ public final class DisplayManagerService extends SystemService {
}
}
private static float[] getFloatArray(TypedArray array) {
int length = array.length();
float[] floatArray = new float[length];
for (int i = 0; i < length; i++) {
floatArray[i] = array.getFloat(i, Float.NaN);
}
array.recycle();
return floatArray;
}
/**
* This is the object that everything in the display manager locks on.
* We make it an inner class within the {@link DisplayManagerService} to so that it is
@@ -1999,6 +2053,16 @@ public final class DisplayManagerService extends SystemService {
}
}
@Override // Binder call
public Curve getMinimumBrightnessCurve() {
final long token = Binder.clearCallingIdentity();
try {
return getMinimumBrightnessCurveInternal();
} finally {
Binder.restoreCallingIdentity(token);
}
}
void setBrightness(int brightness) {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);

View File

@@ -17,12 +17,15 @@
package com.android.server.display;
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.SurfaceControl;
@@ -226,6 +229,62 @@ public class DisplayManagerServiceTest extends AndroidTestCase {
+ " virtual display adapter");
}
/**
* Tests that an exception is raised for too dark a brightness configuration.
*/
public void testTooDarkBrightnessConfigurationThrowException() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
Curve minimumBrightnessCurve = displayManager.getMinimumBrightnessCurveInternal();
float[] lux = minimumBrightnessCurve.getX();
float[] minimumNits = minimumBrightnessCurve.getY();
float[] nits = new float[minimumNits.length];
// For every control point, assert that making it slightly lower than the minimum throws an
// exception.
for (int i = 0; i < nits.length; i++) {
for (int j = 0; j < nits.length; j++) {
nits[j] = minimumNits[j];
if (j == i) {
nits[j] -= 0.1f;
}
if (nits[j] < 0) {
nits[j] = 0;
}
}
BrightnessConfiguration config =
new BrightnessConfiguration.Builder(lux, nits).build();
Exception thrown = null;
try {
displayManager.validateBrightnessConfiguration(config);
} catch (IllegalArgumentException e) {
thrown = e;
}
assertNotNull("Building too dark a brightness configuration must throw an exception");
}
}
/**
* Tests that no exception is raised for not too dark a brightness configuration.
*/
public void testBrightEnoughBrightnessConfigurationDoesNotThrowException() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
Curve minimumBrightnessCurve = displayManager.getMinimumBrightnessCurveInternal();
float[] lux = minimumBrightnessCurve.getX();
float[] nits = minimumBrightnessCurve.getY();
BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits).build();
displayManager.validateBrightnessConfiguration(config);
}
/**
* Tests that null brightness configurations are alright.
*/
public void testNullBrightnessConfiguration() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
displayManager.validateBrightnessConfiguration(null);
}
private void registerDefaultDisplays(DisplayManagerService displayManager) {
Handler handler = displayManager.getDisplayHandler();
// Would prefer to call displayManager.onStart() directly here but it performs binderService