60hz only when ambient or display brightness is low
am: bc841b0293
Change-Id: I83a1d2efcc4ac0731c8cc3167bbfc4e7bcc4ccb2
This commit is contained in:
@@ -4145,8 +4145,27 @@
|
||||
for higher refresh rates to be automatically used out of the box -->
|
||||
<integer name="config_defaultPeakRefreshRate">60</integer>
|
||||
|
||||
<!-- The default brightness threshold that allows to switch to higher refresh rate -->
|
||||
<integer name="config_brightnessThresholdOfPeakRefreshRate">-1</integer>
|
||||
<!-- The display uses different gamma curves for different refresh rates. It's hard for panel
|
||||
vendor to tune the curves to have exact same brightness for different refresh rate. So
|
||||
flicker could be observed at switch time. The issue is worse at the gamma lower end.
|
||||
In addition, human eyes are more sensitive to the flicker at darker environment.
|
||||
To prevent flicker, we only support higher refresh rates if the display brightness is above
|
||||
a threshold. And the darker environment could have higher threshold.
|
||||
For example, no higher refresh rate if
|
||||
display brightness <= disp0 && ambient brightness <= amb0
|
||||
|| display brightness <= disp1 && ambient brightness <= amb1 -->
|
||||
<integer-array translatable="false" name="config_brightnessThresholdsOfPeakRefreshRate">
|
||||
<!--
|
||||
<item>disp0</item>
|
||||
<item>disp1</item>
|
||||
-->
|
||||
</integer-array>
|
||||
<integer-array translatable="false" name="config_ambientThresholdsOfPeakRefreshRate">
|
||||
<!--
|
||||
<item>amb0</item>
|
||||
<item>amb1</item>
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- The type of the light sensor to be used by the display framework for things like
|
||||
auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
|
||||
|
||||
@@ -3793,7 +3793,8 @@
|
||||
|
||||
<!-- For high refresh rate displays -->
|
||||
<java-symbol type="integer" name="config_defaultPeakRefreshRate" />
|
||||
<java-symbol type="integer" name="config_brightnessThresholdOfPeakRefreshRate" />
|
||||
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
|
||||
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
|
||||
|
||||
<!-- For Auto-Brightness -->
|
||||
<java-symbol type="string" name="config_displayLightSensorType" />
|
||||
|
||||
@@ -303,6 +303,8 @@ public final class DisplayManagerService extends SystemService {
|
||||
private final Spline mMinimumBrightnessSpline;
|
||||
private final ColorSpace mWideColorSpace;
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
|
||||
public DisplayManagerService(Context context) {
|
||||
this(context, new Injector());
|
||||
}
|
||||
@@ -430,7 +432,7 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
mDisplayModeDirector.setListener(new AllowedDisplayModeObserver());
|
||||
mDisplayModeDirector.start();
|
||||
mDisplayModeDirector.start(mSensorManager);
|
||||
|
||||
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
|
||||
}
|
||||
@@ -2358,6 +2360,7 @@ public final class DisplayManagerService extends SystemService {
|
||||
};
|
||||
mDisplayPowerController = new DisplayPowerController(
|
||||
mContext, callbacks, handler, sensorManager, blanker);
|
||||
mSensorManager = sensorManager;
|
||||
}
|
||||
|
||||
mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
|
||||
|
||||
@@ -18,26 +18,41 @@ package com.android.server.display;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.UserHandle;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
|
||||
import com.android.server.display.whitebalance.AmbientFilter;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -74,7 +89,7 @@ public class DisplayModeDirector {
|
||||
private final AppRequestObserver mAppRequestObserver;
|
||||
private final SettingsObserver mSettingsObserver;
|
||||
private final DisplayObserver mDisplayObserver;
|
||||
|
||||
private final BrightnessObserver mBrightnessObserver;
|
||||
|
||||
private Listener mListener;
|
||||
|
||||
@@ -87,6 +102,8 @@ public class DisplayModeDirector {
|
||||
mAppRequestObserver = new AppRequestObserver();
|
||||
mSettingsObserver = new SettingsObserver(context, handler);
|
||||
mDisplayObserver = new DisplayObserver(context, handler);
|
||||
mBrightnessObserver = new BrightnessObserver(context, handler);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,15 +113,17 @@ public class DisplayModeDirector {
|
||||
* This has to be deferred because the object may be constructed before the rest of the system
|
||||
* is ready.
|
||||
*/
|
||||
public void start() {
|
||||
public void start(SensorManager sensorManager) {
|
||||
mSettingsObserver.observe();
|
||||
mDisplayObserver.observe();
|
||||
mSettingsObserver.observe();
|
||||
mBrightnessObserver.observe(sensorManager);
|
||||
synchronized (mLock) {
|
||||
// We may have a listener already registered before the call to start, so go ahead and
|
||||
// notify them to pick up our newly initialized state.
|
||||
notifyAllowedModesChangedLocked();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,6 +334,7 @@ public class DisplayModeDirector {
|
||||
}
|
||||
mSettingsObserver.dumpLocked(pw);
|
||||
mAppRequestObserver.dumpLocked(pw);
|
||||
mBrightnessObserver.dumpLocked(pw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,20 +506,15 @@ public class DisplayModeDirector {
|
||||
Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
|
||||
private final Uri mLowPowerModeSetting =
|
||||
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
|
||||
private final Uri mBrightnessSetting =
|
||||
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
|
||||
|
||||
private final Context mContext;
|
||||
private final float mDefaultPeakRefreshRate;
|
||||
private final int mBrightnessThreshold;
|
||||
|
||||
SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
|
||||
super(handler);
|
||||
mContext = context;
|
||||
mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
|
||||
R.integer.config_defaultPeakRefreshRate);
|
||||
mBrightnessThreshold = context.getResources().getInteger(
|
||||
R.integer.config_brightnessThresholdOfPeakRefreshRate);
|
||||
}
|
||||
|
||||
public void observe() {
|
||||
@@ -508,14 +523,9 @@ public class DisplayModeDirector {
|
||||
UserHandle.USER_SYSTEM);
|
||||
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
|
||||
UserHandle.USER_SYSTEM);
|
||||
if (mBrightnessThreshold >= 0) {
|
||||
cr.registerContentObserver(mBrightnessSetting, false /*notifyDescendants*/, this,
|
||||
UserHandle.USER_SYSTEM);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
updateRefreshRateSettingLocked();
|
||||
updateLowPowerModeSettingLocked();
|
||||
updateBrightnessSettingLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,8 +536,6 @@ public class DisplayModeDirector {
|
||||
updateRefreshRateSettingLocked();
|
||||
} else if (mLowPowerModeSetting.equals(uri)) {
|
||||
updateLowPowerModeSettingLocked();
|
||||
} else if (mBrightnessThreshold >=0 && mBrightnessSetting.equals(uri)) {
|
||||
updateBrightnessSettingLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -542,6 +550,7 @@ public class DisplayModeDirector {
|
||||
vote = null;
|
||||
}
|
||||
updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote);
|
||||
mBrightnessObserver.onLowPowerModeEnabled(inLowPowerMode);
|
||||
}
|
||||
|
||||
private void updateRefreshRateSettingLocked() {
|
||||
@@ -549,23 +558,7 @@ public class DisplayModeDirector {
|
||||
Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
|
||||
Vote vote = Vote.forRefreshRates(0f, peakRefreshRate);
|
||||
updateVoteLocked(Vote.PRIORITY_USER_SETTING_REFRESH_RATE, vote);
|
||||
}
|
||||
|
||||
private void updateBrightnessSettingLocked() {
|
||||
int brightness = Settings.System.getInt(mContext.getContentResolver(),
|
||||
Settings.System.SCREEN_BRIGHTNESS, -1);
|
||||
|
||||
if (brightness < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Vote vote;
|
||||
if (brightness <= mBrightnessThreshold) {
|
||||
vote = Vote.forRefreshRates(0f, 60f);
|
||||
} else {
|
||||
vote = null;
|
||||
}
|
||||
updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
|
||||
mBrightnessObserver.onPeakRefreshRateEnabled(peakRefreshRate > 60f);
|
||||
}
|
||||
|
||||
public void dumpLocked(PrintWriter pw) {
|
||||
@@ -715,4 +708,240 @@ public class DisplayModeDirector {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class manages brightness threshold for switching between 60 hz and higher refresh rate.
|
||||
* See more information at the definition of
|
||||
* {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
|
||||
* {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
|
||||
*/
|
||||
private class BrightnessObserver extends ContentObserver {
|
||||
private final Uri mDisplayBrightnessSetting =
|
||||
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
|
||||
|
||||
private final static int LIGHT_SENSOR_RATE_MS = 250;
|
||||
private final int[] mDisplayBrightnessThresholds;
|
||||
private final int[] mAmbientBrightnessThresholds;
|
||||
// valid threshold if any item from the array >= 0
|
||||
private boolean mShouldObserveDisplayChange;
|
||||
private boolean mShouldObserveAmbientChange;
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mLightSensor;
|
||||
// Take it as low brightness before valid sensor data comes
|
||||
private float mAmbientLux = -1.0f;
|
||||
private AmbientFilter mAmbientFilter;
|
||||
|
||||
private final Context mContext;
|
||||
private ScreenStateReceiver mScreenStateReceiver;
|
||||
|
||||
// Enable light sensor only when screen is on, peak refresh rate enabled and low power mode
|
||||
// off. After initialization, these states will be updated from the same handler thread.
|
||||
private boolean mScreenOn = false;
|
||||
private boolean mPeakRefreshRateEnabled = false;
|
||||
private boolean mLowPowerModeEnabled = false;
|
||||
|
||||
BrightnessObserver(Context context, Handler handler) {
|
||||
super(handler);
|
||||
mContext = context;
|
||||
mDisplayBrightnessThresholds = context.getResources().getIntArray(
|
||||
R.array.config_brightnessThresholdsOfPeakRefreshRate);
|
||||
mAmbientBrightnessThresholds = context.getResources().getIntArray(
|
||||
R.array.config_ambientThresholdsOfPeakRefreshRate);
|
||||
if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
|
||||
throw new RuntimeException("display brightness threshold array and ambient "
|
||||
+ "brightness threshold array have different length");
|
||||
}
|
||||
|
||||
mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
|
||||
mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
|
||||
}
|
||||
|
||||
public void observe(SensorManager sensorManager) {
|
||||
if (mShouldObserveDisplayChange) {
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
cr.registerContentObserver(mDisplayBrightnessSetting,
|
||||
false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
|
||||
}
|
||||
|
||||
if (mShouldObserveAmbientChange) {
|
||||
Resources resources = mContext.getResources();
|
||||
String lightSensorType = resources.getString(
|
||||
com.android.internal.R.string.config_displayLightSensorType);
|
||||
|
||||
Sensor lightSensor = null;
|
||||
if (!TextUtils.isEmpty(lightSensorType)) {
|
||||
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
|
||||
for (int i = 0; i < sensors.size(); i++) {
|
||||
Sensor sensor = sensors.get(i);
|
||||
if (lightSensorType.equals(sensor.getStringType())) {
|
||||
lightSensor = sensor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lightSensor == null) {
|
||||
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
|
||||
}
|
||||
|
||||
if (lightSensor != null) {
|
||||
final Resources res = mContext.getResources();
|
||||
|
||||
mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
|
||||
mSensorManager = sensorManager;
|
||||
mLightSensor = lightSensor;
|
||||
|
||||
// Intent.ACTION_SCREEN_ON is not sticky. Check current screen status.
|
||||
if (mContext.getSystemService(PowerManager.class).isInteractive()) {
|
||||
onScreenOn(true);
|
||||
}
|
||||
mScreenStateReceiver = new ScreenStateReceiver(mContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (mShouldObserveDisplayChange || mShouldObserveAmbientChange) {
|
||||
synchronized (mLock) {
|
||||
onBrightnessChangedLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onPeakRefreshRateEnabled(boolean b) {
|
||||
if (mShouldObserveAmbientChange && mPeakRefreshRateEnabled != b) {
|
||||
mPeakRefreshRateEnabled = b;
|
||||
updateSensorStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public void onLowPowerModeEnabled(boolean b) {
|
||||
if (mShouldObserveAmbientChange && mLowPowerModeEnabled != b) {
|
||||
mLowPowerModeEnabled = b;
|
||||
updateSensorStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpLocked(PrintWriter pw) {
|
||||
pw.println(" BrightnessObserver");
|
||||
|
||||
for (int d: mDisplayBrightnessThresholds) {
|
||||
pw.println(" mDisplayBrightnessThreshold: " + d);
|
||||
}
|
||||
|
||||
for (int d: mAmbientBrightnessThresholds) {
|
||||
pw.println(" mAmbientBrightnessThreshold: " + d);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri, int userId) {
|
||||
synchronized (mLock) {
|
||||
onBrightnessChangedLocked();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if at least one value is positive, in which case it is necessary to listen
|
||||
* to value changes.
|
||||
*/
|
||||
private boolean checkShouldObserve(int[] a) {
|
||||
for (int d: a) {
|
||||
if (d >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onBrightnessChangedLocked() {
|
||||
int brightness = Settings.System.getInt(mContext.getContentResolver(),
|
||||
Settings.System.SCREEN_BRIGHTNESS, -1);
|
||||
|
||||
Vote vote = null;
|
||||
for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
|
||||
int disp = mDisplayBrightnessThresholds[i];
|
||||
int ambi = mAmbientBrightnessThresholds[i];
|
||||
|
||||
if (disp >= 0 && ambi >= 0) {
|
||||
if (brightness <= disp && mAmbientLux <= ambi) {
|
||||
vote = Vote.forRefreshRates(0f, 60f);
|
||||
}
|
||||
} else if (disp >= 0) {
|
||||
if (brightness <= disp) {
|
||||
vote = Vote.forRefreshRates(0f, 60f);
|
||||
}
|
||||
} else if (ambi >= 0) {
|
||||
if (mAmbientLux <= ambi) {
|
||||
vote = Vote.forRefreshRates(0f, 60f);
|
||||
}
|
||||
}
|
||||
|
||||
if (vote != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
|
||||
(vote != null ? " 60hz only" : " no refresh rate limit"));
|
||||
}
|
||||
updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
|
||||
}
|
||||
|
||||
private void onScreenOn(boolean on) {
|
||||
// Not check mShouldObserveAmbientChange because Screen status receiver is registered
|
||||
// only when it is true.
|
||||
if (mScreenOn != on) {
|
||||
mScreenOn = on;
|
||||
updateSensorStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSensorStatus() {
|
||||
if (mSensorManager == null || mLightSensorListener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mScreenOn && !mLowPowerModeEnabled && mPeakRefreshRateEnabled) {
|
||||
mSensorManager.registerListener(mLightSensorListener,
|
||||
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(mLightSensorListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final SensorEventListener mLightSensorListener = new SensorEventListener() {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
mAmbientFilter.addValue(now, event.values[0]);
|
||||
mAmbientLux = mAmbientFilter.getEstimate(now);
|
||||
|
||||
synchronized (mLock) {
|
||||
onBrightnessChangedLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// Not used.
|
||||
}
|
||||
};
|
||||
|
||||
private final class ScreenStateReceiver extends BroadcastReceiver {
|
||||
public ScreenStateReceiver(Context context) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
context.registerReceiver(this, filter, null, mHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
onScreenOn(Intent.ACTION_SCREEN_ON.equals(intent.getAction()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.Arrays;
|
||||
* - {@link WeightedMovingAverageAmbientFilter}
|
||||
* A weighted average prioritising recent changes.
|
||||
*/
|
||||
abstract class AmbientFilter {
|
||||
abstract public class AmbientFilter {
|
||||
|
||||
protected static final boolean DEBUG = false; // Enable for verbose logs.
|
||||
|
||||
@@ -156,8 +156,7 @@ abstract class AmbientFilter {
|
||||
/**
|
||||
* A weighted average prioritising recent changes.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class WeightedMovingAverageAmbientFilter extends AmbientFilter {
|
||||
static class WeightedMovingAverageAmbientFilter extends AmbientFilter {
|
||||
|
||||
// How long the latest ambient value change is predicted to last.
|
||||
private static final int PREDICTION_TIME = 100; // Milliseconds
|
||||
|
||||
@@ -115,8 +115,7 @@ public class DisplayWhiteBalanceFactory {
|
||||
* Creates a BrightnessFilter which functions as a weighted moving average buffer for recent
|
||||
* brightness values.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static AmbientFilter createBrightnessFilter(Resources resources) {
|
||||
public static AmbientFilter createBrightnessFilter(Resources resources) {
|
||||
final int horizon = resources.getInteger(
|
||||
com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon);
|
||||
final float intercept = getFloat(resources,
|
||||
@@ -129,7 +128,6 @@ public class DisplayWhiteBalanceFactory {
|
||||
+ "expected config_displayWhiteBalanceBrightnessFilterIntercept");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an ambient color sensor instance to redirect sensor data to callbacks.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user