Merge changes from topic "r3_flicker_high_brightness" into rvc-qpr-dev

* changes:
  Allow DeviceConfig to change display settings for high zone
  Set fixed fps when ambient or display brightness is high
  Add FakeSettingsProviderRule class
This commit is contained in:
TreeHugger Robot
2020-12-02 01:05:39 +00:00
committed by Android (Google) Code Review
16 changed files with 1129 additions and 187 deletions

View File

@@ -875,12 +875,52 @@ public final class DisplayManager {
public interface DeviceConfig {
/**
* Key for refresh rate in the zone defined by thresholds.
* Key for refresh rate in the low zone defined by thresholds.
*
* Note that the name and value don't match because they were added before we had a high
* zone to consider.
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_defaultZoneBehavior
*/
String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
/**
* Key for accessing the low display brightness thresholds for the configured refresh
* rate zone.
* The value will be a pair of comma separated integers representing the minimum and maximum
* thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
*
* Note that the name and value don't match because they were added before we had a high
* zone to consider.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
* @hide
*/
String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_brightness_thresholds";
/**
* Key for accessing the low ambient brightness thresholds for the configured refresh
* rate zone. The value will be a pair of comma separated integers representing the minimum
* and maximum thresholds of the zone, respectively, in lux.
*
* Note that the name and value don't match because they were added before we had a high
* zone to consider.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
* @hide
*/
String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_ambient_thresholds";
/**
* Key for refresh rate in the high zone defined by thresholds.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_fixedRefreshRateInHighZone
*/
String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
/**
* Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -888,11 +928,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
* @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
* @hide
*/
String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_brightness_thresholds";
String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_display_brightness_thresholds";
/**
* Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -900,12 +940,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in lux.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
* @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
* @hide
*/
String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
"peak_refresh_rate_ambient_thresholds";
String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_ambient_brightness_thresholds";
/**
* Key for default peak refresh rate
*

View File

@@ -4138,6 +4138,35 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_defaultRefreshRateInZone">0</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 can be observed on the screen with
even full white content at the high brightness. To prevent flickering, we support fixed
refresh rates if the display and ambient brightness are equal to or above the provided
thresholds. You can define multiple threshold levels as higher brightness environments
may have lower display brightness requirements for the flickering is visible. And the
high brightness environment could have higher threshold.
For example, fixed refresh rate if
display brightness >= disp0 && ambient brightness >= amb0
|| display brightness >= disp1 && ambient brightness >= amb1 -->
<integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
<!--
<item>disp0</item>
<item>disp1</item>
-->
</integer-array>
<integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
<!--
<item>amb0</item>
<item>amb1</item>
-->
</integer-array>
<!-- Default refresh rate in the high zone defined by brightness and ambient thresholds.
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_fixedRefreshRateInHighZone">0</integer>
<!-- 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. -->
<string name="config_displayLightSensorType" translatable="false" />

View File

@@ -3783,6 +3783,11 @@
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
<!-- For fixed refresh rate displays in high brightness-->
<java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
<java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" />
<java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" />
<!-- For Auto-Brightness -->
<java-symbol type="string" name="config_displayLightSensorType" />

View File

@@ -1,3 +1,10 @@
filegroup {
name: "services.core-sources-deviceconfig-interface",
srcs: [
"java/com/android/server/utils/DeviceConfigInterface.java"
],
}
filegroup {
name: "services.core-sources",
srcs: ["java/**/*.java"],

View File

@@ -46,8 +46,10 @@ import android.view.DisplayInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,9 +66,11 @@ public class DisplayModeDirector {
private static final boolean DEBUG = false;
private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -79,6 +83,13 @@ public class DisplayModeDirector {
private final Context mContext;
private final DisplayModeDirectorHandler mHandler;
private final Injector mInjector;
private final AppRequestObserver mAppRequestObserver;
private final SettingsObserver mSettingsObserver;
private final DisplayObserver mDisplayObserver;
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
@@ -89,17 +100,19 @@ public class DisplayModeDirector {
// A map from the display ID to the default mode of that display.
private SparseArray<Display.Mode> mDefaultModeByDisplay;
private final AppRequestObserver mAppRequestObserver;
private final SettingsObserver mSettingsObserver;
private final DisplayObserver mDisplayObserver;
private BrightnessObserver mBrightnessObserver;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
this(context, handler, new RealInjector());
}
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@NonNull Injector injector) {
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
mVotesByDisplay = new SparseArray<>();
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
@@ -108,6 +121,7 @@ public class DisplayModeDirector {
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mDeviceConfig = injector.getDeviceConfig();
}
/**
@@ -348,6 +362,23 @@ public class DisplayModeDirector {
}
}
/**
* Retrieve the Vote for the given display and priority. Intended only for testing purposes.
*
* @param displayId the display to query for
* @param priority the priority of the vote to return
* @return the vote corresponding to the given {@code displayId} and {@code priority},
* or {@code null} if there isn't one
*/
@VisibleForTesting
@Nullable
Vote getVote(int displayId, int priority) {
synchronized (mLock) {
SparseArray<Vote> votes = getVotesLocked(displayId);
return votes.get(priority);
}
}
/**
* Print the object's state and debug information into the given stream.
*
@@ -465,6 +496,17 @@ public class DisplayModeDirector {
mBrightnessObserver = brightnessObserver;
}
@VisibleForTesting
BrightnessObserver getBrightnessObserver() {
return mBrightnessObserver;
}
@VisibleForTesting
SettingsObserver getSettingsObserver() {
return mSettingsObserver;
}
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
@@ -493,16 +535,35 @@ public class DisplayModeDirector {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
thresholds.first, thresholds.second);
break;
}
case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
int refreshRateInZone = msg.arg1;
mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
refreshRateInZone);
break;
}
case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
if (thresholds != null) {
mBrightnessObserver.onDeviceConfigThresholdsChanged(
thresholds.first, thresholds.second);
} else {
mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
}
mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
thresholds.first, thresholds.second);
break;
}
case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
int refreshRateInZone = msg.arg1;
mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
refreshRateInZone);
break;
}
case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
Float defaultPeakRefreshRate = (Float) msg.obj;
@@ -510,12 +571,6 @@ public class DisplayModeDirector {
defaultPeakRefreshRate);
break;
case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
int refreshRateInZone = msg.arg1;
mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
refreshRateInZone);
break;
case MSG_REFRESH_RATE_RANGE_CHANGED:
DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
(DesiredDisplayModeSpecsListener) msg.obj;
@@ -685,10 +740,11 @@ public class DisplayModeDirector {
// by all other considerations. It acts to set a default frame rate for a device.
public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
// LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
// FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
// If the higher voters result is a range, it will fix the rate to a single choice.
// It's used to avoid rate switch in certain conditions.
public static final int PRIORITY_LOW_BRIGHTNESS = 1;
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
public static final int PRIORITY_FLICKER = 1;
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
@@ -761,8 +817,8 @@ public class DisplayModeDirector {
switch (priority) {
case PRIORITY_DEFAULT_REFRESH_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
case PRIORITY_LOW_BRIGHTNESS:
return "PRIORITY_LOW_BRIGHTNESS";
case PRIORITY_FLICKER:
return "PRIORITY_FLICKER";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
case PRIORITY_APP_REQUEST_REFRESH_RATE:
@@ -787,7 +843,8 @@ public class DisplayModeDirector {
}
}
private final class SettingsObserver extends ContentObserver {
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
private final Uri mPeakRefreshRateSetting =
Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
private final Uri mMinRefreshRateSetting =
@@ -810,8 +867,7 @@ public class DisplayModeDirector {
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
mInjector.registerPeakRefreshRateObserver(cr, this);
cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
@@ -829,6 +885,13 @@ public class DisplayModeDirector {
}
}
public void setDefaultRefreshRate(float refreshRate) {
synchronized (mLock) {
mDefaultRefreshRate = refreshRate;
updateRefreshRateSettingLocked();
}
}
public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
if (defaultPeakRefreshRate == null) {
defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
@@ -1033,6 +1096,7 @@ public class DisplayModeDirector {
@Override
public void onDisplayChanged(int displayId) {
updateDisplayModes(displayId);
// TODO: Break the coupling between DisplayObserver and BrightnessObserver.
mBrightnessObserver.onDisplayChanged(displayId);
}
@@ -1071,15 +1135,16 @@ public class DisplayModeDirector {
*/
@VisibleForTesting
public class BrightnessObserver extends ContentObserver {
// TODO: brightnessfloat: change this to the float setting
private final Uri mDisplayBrightnessSetting =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
private final static int LIGHT_SENSOR_RATE_MS = 250;
private int[] mDisplayBrightnessThresholds;
private int[] mAmbientBrightnessThresholds;
private int[] mLowDisplayBrightnessThresholds;
private int[] mLowAmbientBrightnessThresholds;
private int[] mHighDisplayBrightnessThresholds;
private int[] mHighAmbientBrightnessThresholds;
// valid threshold if any item from the array >= 0
private boolean mShouldObserveDisplayChange;
private boolean mShouldObserveAmbientChange;
private boolean mShouldObserveDisplayLowChange;
private boolean mShouldObserveAmbientLowChange;
private boolean mShouldObserveDisplayHighChange;
private boolean mShouldObserveAmbientHighChange;
private SensorManager mSensorManager;
private Sensor mLightSensor;
@@ -1087,46 +1152,122 @@ public class DisplayModeDirector {
// Take it as low brightness before valid sensor data comes
private float mAmbientLux = -1.0f;
private AmbientFilter mAmbientFilter;
private int mBrightness = -1;
private final Context mContext;
// Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
// refresh rate changeable and low power mode off. After initialization, these states will
// Enable light sensor only when mShouldObserveAmbientLowChange is true or
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
private boolean mScreenOn = false;
private boolean mDefaultDisplayOn = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
private int mRefreshRateInZone;
private int mRefreshRateInLowZone;
private int mRefreshRateInHighZone;
BrightnessObserver(Context context, Handler handler) {
super(handler);
mContext = context;
mDisplayBrightnessThresholds = context.getResources().getIntArray(
mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
mAmbientBrightnessThresholds = context.getResources().getIntArray(
mLowAmbientBrightnessThresholds = 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");
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
+ "displayBrightnessThresholds="
+ Arrays.toString(mLowDisplayBrightnessThresholds)
+ ", ambientBrightnessThresholds="
+ Arrays.toString(mLowAmbientBrightnessThresholds));
}
mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
+ "brightness threshold array have different length: "
+ "displayBrightnessThresholds="
+ Arrays.toString(mHighDisplayBrightnessThresholds)
+ ", ambientBrightnessThresholds="
+ Arrays.toString(mHighAmbientBrightnessThresholds));
}
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
}
/**
* @return the refresh to lock to when in a low brightness zone
*/
@VisibleForTesting
int getRefreshRateInLowZone() {
return mRefreshRateInLowZone;
}
/**
* @return the display brightness thresholds for the low brightness zones
*/
@VisibleForTesting
int[] getLowDisplayBrightnessThresholds() {
return mLowDisplayBrightnessThresholds;
}
/**
* @return the ambient brightness thresholds for the low brightness zones
*/
@VisibleForTesting
int[] getLowAmbientBrightnessThresholds() {
return mLowAmbientBrightnessThresholds;
}
public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
mSensorManager = sensorManager;
mLightSensor = lightSensor;
mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
}
public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
final ContentResolver cr = mContext.getContentResolver();
mBrightness = Settings.System.getIntForUser(cr,
Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
// DeviceConfig is accessible after system ready.
int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
int[] lowDisplayBrightnessThresholds =
mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
int[] lowAmbientBrightnessThresholds =
mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
if (brightnessThresholds != null && ambientThresholds != null
&& brightnessThresholds.length == ambientThresholds.length) {
mDisplayBrightnessThresholds = brightnessThresholds;
mAmbientBrightnessThresholds = ambientThresholds;
if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
&& lowDisplayBrightnessThresholds.length
== lowAmbientBrightnessThresholds.length) {
mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
}
mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
int[] highDisplayBrightnessThresholds =
mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
int[] highAmbientBrightnessThresholds =
mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
&& highDisplayBrightnessThresholds.length
== highAmbientBrightnessThresholds.length) {
mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
}
mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
restartObserver();
mDeviceConfigDisplaySettings.startListening();
}
@@ -1138,7 +1279,7 @@ public class DisplayModeDirector {
updateSensorStatus();
if (!changeable) {
// Revoke previous vote from BrightnessObserver
updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
updateVoteLocked(Vote.PRIORITY_FLICKER, null);
}
}
}
@@ -1150,25 +1291,48 @@ public class DisplayModeDirector {
}
}
public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
int[] ambientThresholds) {
if (brightnessThresholds != null && ambientThresholds != null
&& brightnessThresholds.length == ambientThresholds.length) {
mDisplayBrightnessThresholds = brightnessThresholds;
mAmbientBrightnessThresholds = ambientThresholds;
if (displayThresholds != null && ambientThresholds != null
&& displayThresholds.length == ambientThresholds.length) {
mLowDisplayBrightnessThresholds = displayThresholds;
mLowAmbientBrightnessThresholds = ambientThresholds;
} else {
// Invalid or empty. Use device default.
mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_ambientThresholdsOfPeakRefreshRate);
}
restartObserver();
}
public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
if (refreshRate != mRefreshRateInZone) {
mRefreshRateInZone = refreshRate;
public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
if (refreshRate != mRefreshRateInLowZone) {
mRefreshRateInLowZone = refreshRate;
restartObserver();
}
}
public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
int[] ambientThresholds) {
if (displayThresholds != null && ambientThresholds != null
&& displayThresholds.length == ambientThresholds.length) {
mHighDisplayBrightnessThresholds = displayThresholds;
mHighAmbientBrightnessThresholds = ambientThresholds;
} else {
// Invalid or empty. Use device default.
mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
}
restartObserver();
}
public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
if (refreshRate != mRefreshRateInHighZone) {
mRefreshRateInHighZone = refreshRate;
restartObserver();
}
}
@@ -1176,48 +1340,95 @@ public class DisplayModeDirector {
public void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mRefreshRateInZone: " + mRefreshRateInZone);
pw.println(" mBrightness: " + mBrightness);
pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone);
for (int d: mDisplayBrightnessThresholds) {
pw.println(" mDisplayBrightnessThreshold: " + d);
for (int d : mLowDisplayBrightnessThresholds) {
pw.println(" mDisplayLowBrightnessThreshold: " + d);
}
for (int d: mAmbientBrightnessThresholds) {
pw.println(" mAmbientBrightnessThreshold: " + d);
for (int d : mLowAmbientBrightnessThresholds) {
pw.println(" mAmbientLowBrightnessThreshold: " + d);
}
pw.println(" mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone);
for (int d : mHighDisplayBrightnessThresholds) {
pw.println(" mDisplayHighBrightnessThresholds: " + d);
}
for (int d : mHighAmbientBrightnessThresholds) {
pw.println(" mAmbientHighBrightnessThresholds: " + d);
}
mLightSensorListener.dumpLocked(pw);
if (mAmbientFilter != null) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.setIndent(" ");
mAmbientFilter.dump(ipw);
}
}
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
onScreenOn(isDefaultDisplayOn());
updateDefaultDisplayState();
}
}
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
synchronized (mLock) {
onBrightnessChangedLocked();
final ContentResolver cr = mContext.getContentResolver();
int brightness = Settings.System.getIntForUser(cr,
Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
if (brightness != mBrightness) {
mBrightness = brightness;
onBrightnessChangedLocked();
}
}
}
private void restartObserver() {
mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
final ContentResolver cr = mContext.getContentResolver();
if (mShouldObserveDisplayChange) {
// Content Service does not check if an listener has already been registered.
// To ensure only one listener is registered, force an unregistration first.
cr.unregisterContentObserver(this);
cr.registerContentObserver(mDisplayBrightnessSetting,
false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
if (mRefreshRateInLowZone > 0) {
mShouldObserveDisplayLowChange = hasValidThreshold(
mLowDisplayBrightnessThresholds);
mShouldObserveAmbientLowChange = hasValidThreshold(
mLowAmbientBrightnessThresholds);
} else {
cr.unregisterContentObserver(this);
mShouldObserveDisplayLowChange = false;
mShouldObserveAmbientLowChange = false;
}
if (mShouldObserveAmbientChange) {
if (mRefreshRateInHighZone > 0) {
mShouldObserveDisplayHighChange = hasValidThreshold(
mHighDisplayBrightnessThresholds);
mShouldObserveAmbientHighChange = hasValidThreshold(
mHighAmbientBrightnessThresholds);
} else {
mShouldObserveDisplayHighChange = false;
mShouldObserveAmbientHighChange = false;
}
if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
// Content Service does not check if an listener has already been registered.
// To ensure only one listener is registered, force an unregistration first.
mInjector.unregisterBrightnessObserver(cr, this);
mInjector.registerBrightnessObserver(cr, this);
} else {
mInjector.unregisterBrightnessObserver(cr, this);
}
if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
Resources resources = mContext.getResources();
String lightSensorType = resources.getString(
com.android.internal.R.string.config_displayLightSensorType);
@@ -1243,8 +1454,6 @@ public class DisplayModeDirector {
mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
mLightSensor = lightSensor;
onScreenOn(isDefaultDisplayOn());
}
} else {
mAmbientFilter = null;
@@ -1263,11 +1472,7 @@ public class DisplayModeDirector {
* 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) {
if (mRefreshRateInZone <= 0) {
return false;
}
private boolean hasValidThreshold(int[] a) {
for (int d: a) {
if (d >= 0) {
return true;
@@ -1277,13 +1482,13 @@ public class DisplayModeDirector {
return false;
}
private boolean isInsideZone(int brightness, float lux) {
for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
int disp = mDisplayBrightnessThresholds[i];
int ambi = mAmbientBrightnessThresholds[i];
private boolean isInsideLowZone(int brightness, float lux) {
for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
int disp = mLowDisplayBrightnessThresholds[i];
int ambi = mLowAmbientBrightnessThresholds[i];
if (disp >= 0 && ambi >= 0) {
if (brightness <= disp && mAmbientLux <= ambi) {
if (brightness <= disp && lux <= ambi) {
return true;
}
} else if (disp >= 0) {
@@ -1291,7 +1496,7 @@ public class DisplayModeDirector {
return true;
}
} else if (ambi >= 0) {
if (mAmbientLux <= ambi) {
if (lux <= ambi) {
return true;
}
}
@@ -1299,27 +1504,77 @@ public class DisplayModeDirector {
return false;
}
// TODO: brightnessfloat: make it use float not int
private void onBrightnessChangedLocked() {
int brightness = Settings.System.getInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, -1);
private boolean isInsideHighZone(int brightness, float lux) {
for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
int disp = mHighDisplayBrightnessThresholds[i];
int ambi = mHighAmbientBrightnessThresholds[i];
if (disp >= 0 && ambi >= 0) {
if (brightness >= disp && lux >= ambi) {
return true;
}
} else if (disp >= 0) {
if (brightness >= disp) {
return true;
}
} else if (ambi >= 0) {
if (lux >= ambi) {
return true;
}
}
}
return false;
}
private void onBrightnessChangedLocked() {
Vote vote = null;
boolean insideZone = isInsideZone(brightness, mAmbientLux);
if (insideZone) {
vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
if (mBrightness < 0) {
// Either the setting isn't available or we shouldn't be observing yet anyways.
// Either way, just bail out since there's nothing we can do here.
return;
}
boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
if (insideLowZone) {
vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
}
boolean insideHighZone = hasValidHighZone()
&& isInsideHighZone(mBrightness, mAmbientLux);
if (insideHighZone) {
vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
}
if (DEBUG) {
Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
", Vote " + vote);
Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
+ ", Vote " + vote);
}
updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
}
private void onScreenOn(boolean on) {
if (mScreenOn != on) {
mScreenOn = on;
private boolean hasValidLowZone() {
return mRefreshRateInLowZone > 0
&& (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
}
private boolean hasValidHighZone() {
return mRefreshRateInHighZone > 0
&& (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
}
private void updateDefaultDisplayState() {
Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
setDefaultDisplayState(defaultDisplayOn);
}
@VisibleForTesting
public void setDefaultDisplayState(boolean on) {
if (mDefaultDisplayOn != on) {
mDefaultDisplayOn = on;
updateSensorStatus();
}
}
@@ -1329,8 +1584,8 @@ public class DisplayModeDirector {
return;
}
if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
&& mRefreshRateChangeable) {
if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
&& isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
} else {
@@ -1339,11 +1594,8 @@ public class DisplayModeDirector {
}
}
private boolean isDefaultDisplayOn() {
final Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
return display.getState() != Display.STATE_OFF
&& mContext.getSystemService(PowerManager.class).isInteractive();
private boolean isDeviceActive() {
return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
}
private final class LightSensorEventListener implements SensorEventListener {
@@ -1361,23 +1613,33 @@ public class DisplayModeDirector {
Slog.d(TAG, "On sensor changed: " + mLastSensorData);
}
boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
if (zoneChanged && mLastSensorData < mAmbientLux) {
// Easier to see flicker at lower brightness environment. Forget the history to
// get immediate response.
mAmbientFilter.clear();
boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
mLowAmbientBrightnessThresholds);
boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
mHighAmbientBrightnessThresholds);
if ((lowZoneChanged && mLastSensorData < mAmbientLux)
|| (highZoneChanged && mLastSensorData > mAmbientLux)) {
// Easier to see flicker at lower brightness environment or high brightness
// environment. Forget the history to get immediate response.
if (mAmbientFilter != null) {
mAmbientFilter.clear();
}
}
long now = SystemClock.uptimeMillis();
mAmbientFilter.addValue(now, mLastSensorData);
if (mAmbientFilter != null) {
mAmbientFilter.addValue(now, mLastSensorData);
}
mHandler.removeCallbacks(mInjectSensorEventRunnable);
processSensorData(now);
if (zoneChanged && mLastSensorData > mAmbientLux) {
if ((lowZoneChanged && mLastSensorData > mAmbientLux)
|| (highZoneChanged && mLastSensorData < mAmbientLux)) {
// Sensor may not report new event if there is no brightness change.
// Need to keep querying the temporal filter for the latest estimation,
// until enter in higher lux zone or is interrupted by a new sensor event.
// until sensor readout and filter estimation are in the same zone or
// is interrupted by a new sensor event.
mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
}
}
@@ -1392,17 +1654,19 @@ public class DisplayModeDirector {
}
private void processSensorData(long now) {
mAmbientLux = mAmbientFilter.getEstimate(now);
if (mAmbientFilter != null) {
mAmbientLux = mAmbientFilter.getEstimate(now);
} else {
mAmbientLux = mLastSensorData;
}
synchronized (mLock) {
onBrightnessChangedLocked();
}
}
private boolean isDifferentZone(float lux1, float lux2) {
for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
final float boundary = mAmbientBrightnessThresholds[z];
private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
for (final float boundary : luxThresholds) {
// Test each boundary. See if the current value and the new value are at
// different sides.
if ((lux1 <= boundary && lux2 > boundary)
@@ -1422,7 +1686,10 @@ public class DisplayModeDirector {
processSensorData(now);
// Inject next event if there is a possible zone change.
if (isDifferentZone(mLastSensorData, mAmbientLux)) {
if (isDifferentZone(mLastSensorData, mAmbientLux,
mLowAmbientBrightnessThresholds)
|| isDifferentZone(mLastSensorData, mAmbientLux,
mHighAmbientBrightnessThresholds)) {
mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
}
}
@@ -1435,33 +1702,75 @@ public class DisplayModeDirector {
}
public void startListening() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(), this);
}
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
public int[] getBrightnessThresholds() {
public int[] getLowDisplayBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
}
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
public int[] getAmbientThresholds() {
public int[] getLowAmbientBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
public int getRefreshRateInLowZone() {
int defaultRefreshRateInZone = mContext.getResources().getInteger(
R.integer.config_defaultRefreshRateInZone);
int refreshRate = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
defaultRefreshRateInZone);
return refreshRate;
}
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
public int[] getHighDisplayBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig
.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
}
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
public int[] getHighAmbientBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig
.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
public int getRefreshRateInHighZone() {
int defaultRefreshRateInZone = mContext.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
int refreshRate = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
defaultRefreshRateInZone);
return refreshRate;
}
/*
* Return null if no such property
*/
public Float getDefaultPeakRefreshRate() {
float defaultPeakRefreshRate = DeviceConfig.getFloat(
float defaultPeakRefreshRate = mDeviceConfig.getFloat(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
@@ -1471,36 +1780,35 @@ public class DisplayModeDirector {
return defaultPeakRefreshRate;
}
public int getRefreshRateInZone() {
int defaultRefreshRateInZone = mContext.getResources().getInteger(
R.integer.config_defaultRefreshRateInZone);
int refreshRate = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
defaultRefreshRateInZone);
return refreshRate;
}
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
int[] brightnessThresholds = getBrightnessThresholds();
int[] ambientThresholds = getAmbientThresholds();
Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
int refreshRateInZone = getRefreshRateInZone();
mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
.sendToTarget();
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
defaultPeakRefreshRate).sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
0).sendToTarget();
int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
int refreshRateInLowZone = getRefreshRateInLowZone();
mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
.sendToTarget();
int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
int refreshRateInHighZone = getRefreshRateInHighZone();
mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
.sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
null);
if (strArray != null) {
@@ -1527,4 +1835,59 @@ public class DisplayModeDirector {
}
}
interface Injector {
// TODO: brightnessfloat: change this to the float setting
Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
void registerBrightnessObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
void unregisterBrightnessObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
boolean isDeviceInteractive(@NonNull Context context);
}
@VisibleForTesting
static class RealInjector implements Injector {
@Override
@NonNull
public DeviceConfigInterface getDeviceConfig() {
return DeviceConfigInterface.REAL;
}
@Override
public void registerBrightnessObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
@Override
public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
cr.unregisterContentObserver(observer);
}
@Override
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
@Override
public boolean isDeviceInteractive(@NonNull Context ctx) {
return ctx.getSystemService(PowerManager.class).isInteractive();
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.wm.utils;
package com.android.server.utils;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -53,6 +53,11 @@ public interface DeviceConfigInterface {
*/
boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
/**
* @see DeviceConfig#getFloat
*/
float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
/**
* @see DeviceConfig#addOnPropertiesChangedListener
*/
@@ -95,6 +100,12 @@ public interface DeviceConfigInterface {
return DeviceConfig.getBoolean(namespace, name, defaultValue);
}
@Override
public float getFloat(@NonNull String namespace, @NonNull String name,
float defaultValue) {
return DeviceConfig.getFloat(namespace, name, defaultValue);
}
@Override
public void addOnPropertiesChangedListener(String namespace, Executor executor,
DeviceConfig.OnPropertiesChangedListener listener) {

View File

@@ -27,7 +27,7 @@ import android.util.ArraySet;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.wm.utils.DeviceConfigInterface;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;

View File

@@ -23,7 +23,7 @@ import android.provider.AndroidDeviceConfig;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.utils.DeviceConfigInterface;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.Objects;

View File

@@ -282,8 +282,8 @@ import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.protolog.ProtoLogImpl;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.utils.DeviceConfigInterface;
import com.android.server.utils.PriorityDump;
import com.android.server.wm.utils.DeviceConfigInterface;
import java.io.BufferedWriter;
import java.io.DataInputStream;

View File

@@ -48,7 +48,6 @@ android_test {
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
],
aidl: {
@@ -110,6 +109,7 @@ java_library {
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
@@ -126,6 +126,7 @@ java_library {
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",

View File

@@ -16,49 +16,99 @@
package com.android.server.display;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
import com.android.server.testutils.FakeDeviceConfigInterface;
import com.google.common.truth.Truth;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DisplayModeDirectorTest {
// The tolerance within which we consider something approximately equals.
private static final String TAG = "DisplayModeDirectorTest";
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
private Context mContext;
private FakesInjector mInjector;
private Handler mHandler;
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
mInjector = new FakesInjector();
mHandler = new Handler(Looper.getMainLooper());
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
float[] refreshRates, int baseModeId) {
DisplayModeDirector director =
new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
new DisplayModeDirector(mContext, mHandler, mInjector);
int displayId = 0;
Display.Mode[] modes = new Display.Mode[refreshRates.length];
for (int i = 0; i < refreshRates.length; i++) {
@@ -159,9 +209,9 @@ public class DisplayModeDirectorTest {
}
@Test
public void testBrightnessHasLowerPriorityThanUser() {
assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
public void testFlickerHasLowerPriorityThanUser() {
assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
@@ -169,7 +219,7 @@ public class DisplayModeDirectorTest {
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -177,7 +227,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -185,7 +235,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -193,7 +243,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -202,10 +252,10 @@ public class DisplayModeDirectorTest {
@Test
public void testAppRequestRefreshRateRange() {
// Confirm that the app request range doesn't include low brightness or min refresh rate
// settings, but does include everything else.
// Confirm that the app request range doesn't include flicker or min refresh rate settings,
// but does include everything else.
assertTrue(
Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
< Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
@@ -216,7 +266,7 @@ public class DisplayModeDirectorTest {
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -302,4 +352,343 @@ public class DisplayModeDirectorTest {
verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
}
@Test
public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
SensorManager sensorManager = createMockSensorManager(createLightSensor());
final int initialRefreshRate = 60;
mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
director.start(sensorManager);
assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
.isEqualTo(initialRefreshRate);
final int updatedRefreshRate = 90;
mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
.isEqualTo(updatedRefreshRate);
}
@Test
public void testBrightnessObserverThresholdsInZone() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
SensorManager sensorManager = createMockSensorManager(createLightSensor());
final int[] initialDisplayThresholds = { 10 };
final int[] initialAmbientThresholds = { 20 };
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
director.start(sensorManager);
assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
.isEqualTo(initialDisplayThresholds);
assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
.isEqualTo(initialAmbientThresholds);
final int[] updatedDisplayThresholds = { 9, 14 };
final int[] updatedAmbientThresholds = { -1, 19 };
config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
// Need to wait for the property change to propagate to the main thread.
waitForIdleSync();
assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
.isEqualTo(updatedDisplayThresholds);
assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
.isEqualTo(updatedAmbientThresholds);
}
@Test
public void testLockFpsForLowZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(true);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInLowZone(90);
config.setLowDisplayBrightnessThresholds(new int[] { 10 });
config.setLowAmbientBrightnessThresholds(new int[] { 20 });
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
director.start(sensorManager);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
.registerListener(
listenerCaptor.capture(),
eq(lightSensor),
anyInt(),
any(Handler.class));
SensorEventListener listener = listenerCaptor.getValue();
setBrightness(10);
// Sensor reads 20 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
setBrightness(125);
// Sensor reads 1000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
assertThat(vote).isNull();
}
@Test
public void testLockFpsForHighZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(true);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
director.start(sensorManager);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
.registerListener(
listenerCaptor.capture(),
eq(lightSensor),
anyInt(),
any(Handler.class));
SensorEventListener listener = listenerCaptor.getValue();
setBrightness(100);
// Sensor reads 2000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
assertThat(vote).isNull();
setBrightness(255);
// Sensor reads 9000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
}
private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
}
private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
@Override
public String getProperty(String namespace, String name) {
Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
return super.getProperty(namespace, name);
}
@Override
public void addOnPropertiesChangedListener(
String namespace,
Executor executor,
DeviceConfig.OnPropertiesChangedListener listener) {
Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
super.addOnPropertiesChangedListener(namespace, executor, listener);
}
void setRefreshRateInLowZone(int fps) {
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
String.valueOf(fps));
}
void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
if (DEBUG) {
Slog.e(TAG, "Brightness Thresholds = " + thresholds);
}
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
thresholds);
}
void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
String thresholds = toPropertyValue(ambientThresholds);
if (DEBUG) {
Slog.e(TAG, "Ambient Thresholds = " + thresholds);
}
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
thresholds);
}
void setRefreshRateInHighZone(int fps) {
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
String.valueOf(fps));
}
void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
if (DEBUG) {
Slog.e(TAG, "Brightness Thresholds = " + thresholds);
}
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
thresholds);
}
void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
String thresholds = toPropertyValue(ambientThresholds);
if (DEBUG) {
Slog.e(TAG, "Ambient Thresholds = " + thresholds);
}
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
thresholds);
}
@NonNull
private static String toPropertyValue(@NonNull int[] intArray) {
return Arrays.stream(intArray)
.mapToObj(Integer::toString)
.collect(Collectors.joining(","));
}
}
private void setBrightness(int brightness) {
Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
brightness);
mInjector.notifyBrightnessChanged();
waitForIdleSync();
}
private void setPeakRefreshRate(float fps) {
Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
fps);
mInjector.notifyPeakRefreshRateChanged();
waitForIdleSync();
}
private static SensorManager createMockSensorManager(Sensor... sensors) {
SensorManager sensorManager = Mockito.mock(SensorManager.class);
when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
List<Sensor> requestedSensors = new ArrayList<>();
int type = invocation.getArgument(0);
for (Sensor sensor : sensors) {
if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
requestedSensors.add(sensor);
}
}
return requestedSensors;
});
when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
int type = invocation.getArgument(0);
for (Sensor sensor : sensors) {
if (sensor.getType() == type) {
return sensor;
}
}
return null;
});
return sensorManager;
}
private static Sensor createLightSensor() {
try {
return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
} catch (Exception e) {
// There's nothing we can do if this fails, just throw a RuntimeException so that we
// don't have to mark every function that might call this as throwing Exception
throw new RuntimeException("Failed to create a light sensor", e);
}
}
private void waitForIdleSync() {
mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
}
static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private ContentObserver mBrightnessObserver;
private ContentObserver mPeakRefreshRateObserver;
FakesInjector() {
mDeviceConfig = new FakeDeviceConfig();
}
@NonNull
public FakeDeviceConfig getDeviceConfig() {
return mDeviceConfig;
}
@Override
public void registerBrightnessObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
if (mBrightnessObserver != null) {
throw new IllegalStateException("Tried to register a second brightness observer");
}
mBrightnessObserver = observer;
}
@Override
public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
mBrightnessObserver = null;
}
void notifyBrightnessChanged() {
if (mBrightnessObserver != null) {
mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
}
}
@Override
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
mPeakRefreshRateObserver = observer;
}
void notifyPeakRefreshRateChanged() {
if (mPeakRefreshRateObserver != null) {
mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
PEAK_REFRESH_RATE_URI);
}
}
@Override
public boolean isDeviceInteractive(@NonNull Context context) {
return true;
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.server.wm.utils;
package com.android.server.testutils;
import android.annotation.NonNull;
import android.provider.DeviceConfig;
@@ -22,6 +22,7 @@ import android.util.ArrayMap;
import android.util.Pair;
import com.android.internal.util.Preconditions;
import com.android.server.utils.DeviceConfigInterface;
import java.lang.reflect.Constructor;
import java.util.HashMap;
@@ -121,6 +122,19 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface {
}
}
@Override
public float getFloat(String namespace, String name, float defaultValue) {
String value = getProperty(namespace, name);
if (value == null) {
return defaultValue;
}
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
@Override
public boolean getBoolean(String namespace, String name, boolean defaultValue) {
String value = getProperty(namespace, name);

View File

@@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
import com.android.server.wm.utils.FakeDeviceConfigInterface;
import com.android.server.testutils.FakeDeviceConfigInterface;
import org.junit.After;
import org.junit.Test;

View File

@@ -32,7 +32,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.wm.utils.FakeDeviceConfigInterface;
import com.android.server.testutils.FakeDeviceConfigInterface;
import org.junit.After;
import org.junit.Before;

View File

@@ -91,6 +91,14 @@ public class FakeSettingsProvider extends MockContentProvider {
}
}
/**
* Creates a {@link org.junit.rules.TestRule} that makes sure {@link #clearSettingsProvider()}
* is triggered before and after each test.
*/
public static FakeSettingsProviderRule rule() {
return new FakeSettingsProviderRule();
}
/**
* This needs to be called before and after using the FakeSettingsProvider class.
*/

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2020 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 com.android.internal.util.test;
import android.content.Context;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* JUnit Rule helps keeping test {@link FakeSettingsProvider} clean.
*
* <p>It clears {@link FakeSettingsProvider} before and after each test. Example use:
* <pre class="code"><code class="java">
* public class ExampleTest {
*
* &#064;Rule public FakeSettingsProviderRule rule = FakeSettingsProvider.rule();
*
* &#064;Test
* public void shouldDoSomething() {
* ContextResolver cr = rule.mockContentResolver(mContext);
* Settings.Global.putInt(cr, "my_setting_name", 1);
* // Test code relying on my_setting_name value using cr
* }
* }
* </code></pre>
*
* @see FakeSettingsProvider
*/
public final class FakeSettingsProviderRule implements TestRule {
/** Prevent initialization outside {@link FakeSettingsProvider}. */
FakeSettingsProviderRule() {
}
/**
* Creates a {@link MockContentResolver} that uses the {@link FakeSettingsProvider} as the
* {@link Settings#AUTHORITY} provider.
*/
public MockContentResolver mockContentResolver(Context context) {
MockContentResolver contentResolver = new MockContentResolver(context);
contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
return contentResolver;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
public void evaluate() throws Throwable {
FakeSettingsProvider.clearSettingsProvider();
try {
base.evaluate();
} finally {
FakeSettingsProvider.clearSettingsProvider();
}
}
};
}
}