summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml23
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java291
-rw-r--r--services/core/java/com/android/server/display/whitebalance/AmbientFilter.java5
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java4
6 files changed, 290 insertions, 41 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 77a0f89fcad1..fd5d7575e7e6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -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. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a1dce0390a67..5f092af0cbf0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -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" />
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 99341d1c96ae..4f33ebb035da 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -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);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 14bd2d8150da..78a48dac6fb5 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -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()));
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
index 123cd73a3ff3..35808974b9e4 100644
--- a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
+++ b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
@@ -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
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 4df7d6b14f25..bf0a1d16219d 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -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.
*/