Merge changes from topic "b200851199" into sc-qpr1-dev am: 250448c0bc
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15909960 Change-Id: Icaa01be38637089e703e322e083a52a97dddd771
This commit is contained in:
@@ -60,12 +60,18 @@ public final class BrightnessInfo implements Parcelable {
|
|||||||
/** Brightness */
|
/** Brightness */
|
||||||
public final float brightness;
|
public final float brightness;
|
||||||
|
|
||||||
|
/** Brightness after {@link DisplayPowerController} adjustments */
|
||||||
|
public final float adjustedBrightness;
|
||||||
|
|
||||||
/** Current minimum supported brightness. */
|
/** Current minimum supported brightness. */
|
||||||
public final float brightnessMinimum;
|
public final float brightnessMinimum;
|
||||||
|
|
||||||
/** Current maximum supported brightness. */
|
/** Current maximum supported brightness. */
|
||||||
public final float brightnessMaximum;
|
public final float brightnessMaximum;
|
||||||
|
|
||||||
|
/** Brightness values greater than this point are only used in High Brightness Mode. */
|
||||||
|
public final float highBrightnessTransitionPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current state of high brightness mode.
|
* Current state of high brightness mode.
|
||||||
* Can be any of HIGH_BRIGHTNESS_MODE_* values.
|
* Can be any of HIGH_BRIGHTNESS_MODE_* values.
|
||||||
@@ -73,11 +79,20 @@ public final class BrightnessInfo implements Parcelable {
|
|||||||
public final int highBrightnessMode;
|
public final int highBrightnessMode;
|
||||||
|
|
||||||
public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
|
public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
|
||||||
@HighBrightnessMode int highBrightnessMode) {
|
@HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint) {
|
||||||
|
this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode,
|
||||||
|
highBrightnessTransitionPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum,
|
||||||
|
float brightnessMaximum, @HighBrightnessMode int highBrightnessMode,
|
||||||
|
float highBrightnessTransitionPoint) {
|
||||||
this.brightness = brightness;
|
this.brightness = brightness;
|
||||||
|
this.adjustedBrightness = adjustedBrightness;
|
||||||
this.brightnessMinimum = brightnessMinimum;
|
this.brightnessMinimum = brightnessMinimum;
|
||||||
this.brightnessMaximum = brightnessMaximum;
|
this.brightnessMaximum = brightnessMaximum;
|
||||||
this.highBrightnessMode = highBrightnessMode;
|
this.highBrightnessMode = highBrightnessMode;
|
||||||
|
this.highBrightnessTransitionPoint = highBrightnessTransitionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,9 +118,11 @@ public final class BrightnessInfo implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeFloat(brightness);
|
dest.writeFloat(brightness);
|
||||||
|
dest.writeFloat(adjustedBrightness);
|
||||||
dest.writeFloat(brightnessMinimum);
|
dest.writeFloat(brightnessMinimum);
|
||||||
dest.writeFloat(brightnessMaximum);
|
dest.writeFloat(brightnessMaximum);
|
||||||
dest.writeInt(highBrightnessMode);
|
dest.writeInt(highBrightnessMode);
|
||||||
|
dest.writeFloat(highBrightnessTransitionPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR =
|
public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR =
|
||||||
@@ -123,9 +140,11 @@ public final class BrightnessInfo implements Parcelable {
|
|||||||
|
|
||||||
private BrightnessInfo(Parcel source) {
|
private BrightnessInfo(Parcel source) {
|
||||||
brightness = source.readFloat();
|
brightness = source.readFloat();
|
||||||
|
adjustedBrightness = source.readFloat();
|
||||||
brightnessMinimum = source.readFloat();
|
brightnessMinimum = source.readFloat();
|
||||||
brightnessMaximum = source.readFloat();
|
brightnessMaximum = source.readFloat();
|
||||||
highBrightnessMode = source.readInt();
|
highBrightnessMode = source.readInt();
|
||||||
|
highBrightnessTransitionPoint = source.readFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.display;
|
package com.android.server.display;
|
||||||
|
|
||||||
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
|
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
|
||||||
|
import static android.os.PowerManager.BRIGHTNESS_INVALID;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -40,6 +41,7 @@ import android.os.IThermalEventListener;
|
|||||||
import android.os.IThermalService;
|
import android.os.IThermalService;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.PowerManager;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -60,6 +62,7 @@ import android.view.DisplayInfo;
|
|||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.display.BrightnessSynchronizer;
|
||||||
import com.android.internal.os.BackgroundThread;
|
import com.android.internal.os.BackgroundThread;
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.display.utils.AmbientFilter;
|
import com.android.server.display.utils.AmbientFilter;
|
||||||
@@ -155,7 +158,7 @@ public class DisplayModeDirector {
|
|||||||
mAppRequestObserver = new AppRequestObserver();
|
mAppRequestObserver = new AppRequestObserver();
|
||||||
mSettingsObserver = new SettingsObserver(context, handler);
|
mSettingsObserver = new SettingsObserver(context, handler);
|
||||||
mDisplayObserver = new DisplayObserver(context, handler);
|
mDisplayObserver = new DisplayObserver(context, handler);
|
||||||
mBrightnessObserver = new BrightnessObserver(context, handler);
|
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
|
||||||
mUdfpsObserver = new UdfpsObserver();
|
mUdfpsObserver = new UdfpsObserver();
|
||||||
final BallotBox ballotBox = (displayId, priority, vote) -> {
|
final BallotBox ballotBox = (displayId, priority, vote) -> {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -1427,8 +1430,6 @@ public class DisplayModeDirector {
|
|||||||
@Override
|
@Override
|
||||||
public void onDisplayChanged(int displayId) {
|
public void onDisplayChanged(int displayId) {
|
||||||
updateDisplayModes(displayId);
|
updateDisplayModes(displayId);
|
||||||
// TODO: Break the coupling between DisplayObserver and BrightnessObserver.
|
|
||||||
mBrightnessObserver.onDisplayChanged(displayId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisplayModes(int displayId) {
|
private void updateDisplayModes(int displayId) {
|
||||||
@@ -1465,7 +1466,7 @@ public class DisplayModeDirector {
|
|||||||
* {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
|
* {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public class BrightnessObserver extends ContentObserver {
|
public class BrightnessObserver implements DisplayManager.DisplayListener {
|
||||||
private final static int LIGHT_SENSOR_RATE_MS = 250;
|
private final static int LIGHT_SENSOR_RATE_MS = 250;
|
||||||
private int[] mLowDisplayBrightnessThresholds;
|
private int[] mLowDisplayBrightnessThresholds;
|
||||||
private int[] mLowAmbientBrightnessThresholds;
|
private int[] mLowAmbientBrightnessThresholds;
|
||||||
@@ -1488,6 +1489,8 @@ public class DisplayModeDirector {
|
|||||||
private int mBrightness = -1;
|
private int mBrightness = -1;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
private final Injector mInjector;
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
// Enable light sensor only when mShouldObserveAmbientLowChange is true or
|
// Enable light sensor only when mShouldObserveAmbientLowChange is true or
|
||||||
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
|
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
|
||||||
@@ -1500,9 +1503,11 @@ public class DisplayModeDirector {
|
|||||||
private int mRefreshRateInLowZone;
|
private int mRefreshRateInLowZone;
|
||||||
private int mRefreshRateInHighZone;
|
private int mRefreshRateInHighZone;
|
||||||
|
|
||||||
BrightnessObserver(Context context, Handler handler) {
|
BrightnessObserver(Context context, Handler handler, Injector injector) {
|
||||||
super(handler);
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mHandler = handler;
|
||||||
|
mInjector = injector;
|
||||||
|
|
||||||
mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
|
mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
|
||||||
R.array.config_brightnessThresholdsOfPeakRefreshRate);
|
R.array.config_brightnessThresholdsOfPeakRefreshRate);
|
||||||
mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
|
mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
|
||||||
@@ -1569,8 +1574,7 @@ public class DisplayModeDirector {
|
|||||||
public void observe(SensorManager sensorManager) {
|
public void observe(SensorManager sensorManager) {
|
||||||
mSensorManager = sensorManager;
|
mSensorManager = sensorManager;
|
||||||
final ContentResolver cr = mContext.getContentResolver();
|
final ContentResolver cr = mContext.getContentResolver();
|
||||||
mBrightness = Settings.System.getIntForUser(cr,
|
mBrightness = getBrightness(Display.DEFAULT_DISPLAY);
|
||||||
Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
|
|
||||||
|
|
||||||
// DeviceConfig is accessible after system ready.
|
// DeviceConfig is accessible after system ready.
|
||||||
int[] lowDisplayBrightnessThresholds =
|
int[] lowDisplayBrightnessThresholds =
|
||||||
@@ -1603,6 +1607,10 @@ public class DisplayModeDirector {
|
|||||||
|
|
||||||
restartObserver();
|
restartObserver();
|
||||||
mDeviceConfigDisplaySettings.startListening();
|
mDeviceConfigDisplaySettings.startListening();
|
||||||
|
|
||||||
|
mInjector.registerDisplayListener(this, mHandler,
|
||||||
|
DisplayManager.EVENT_FLAG_DISPLAY_CHANGED |
|
||||||
|
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoggingEnabled(boolean loggingEnabled) {
|
public void setLoggingEnabled(boolean loggingEnabled) {
|
||||||
@@ -1718,28 +1726,30 @@ public class DisplayModeDirector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplayAdded(int displayId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplayRemoved(int displayId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onDisplayChanged(int displayId) {
|
public void onDisplayChanged(int displayId) {
|
||||||
if (displayId == Display.DEFAULT_DISPLAY) {
|
if (displayId == Display.DEFAULT_DISPLAY) {
|
||||||
updateDefaultDisplayState();
|
updateDefaultDisplayState();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// We don't support multiple display blocking zones yet, so only handle
|
||||||
public void onChange(boolean selfChange, Uri uri, int userId) {
|
// brightness changes for the default display for now.
|
||||||
synchronized (mLock) {
|
int brightness = getBrightness(displayId);
|
||||||
final ContentResolver cr = mContext.getContentResolver();
|
synchronized (mLock) {
|
||||||
int brightness = Settings.System.getIntForUser(cr,
|
if (brightness != mBrightness) {
|
||||||
Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
|
mBrightness = brightness;
|
||||||
if (brightness != mBrightness) {
|
onBrightnessChangedLocked();
|
||||||
mBrightness = brightness;
|
}
|
||||||
onBrightnessChangedLocked();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restartObserver() {
|
private void restartObserver() {
|
||||||
final ContentResolver cr = mContext.getContentResolver();
|
|
||||||
|
|
||||||
if (mRefreshRateInLowZone > 0) {
|
if (mRefreshRateInLowZone > 0) {
|
||||||
mShouldObserveDisplayLowChange = hasValidThreshold(
|
mShouldObserveDisplayLowChange = hasValidThreshold(
|
||||||
mLowDisplayBrightnessThresholds);
|
mLowDisplayBrightnessThresholds);
|
||||||
@@ -1760,15 +1770,6 @@ public class DisplayModeDirector {
|
|||||||
mShouldObserveAmbientHighChange = 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) {
|
if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
|
||||||
Resources resources = mContext.getResources();
|
Resources resources = mContext.getResources();
|
||||||
String lightSensorType = resources.getString(
|
String lightSensorType = resources.getString(
|
||||||
@@ -1968,6 +1969,15 @@ public class DisplayModeDirector {
|
|||||||
return mDefaultDisplayState == Display.STATE_ON;
|
return mDefaultDisplayState == Display.STATE_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getBrightness(int displayId) {
|
||||||
|
final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
|
||||||
|
if (info != null) {
|
||||||
|
return BrightnessSynchronizer.brightnessFloatToInt(info.adjustedBrightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BRIGHTNESS_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
private final class LightSensorEventListener implements SensorEventListener {
|
private final class LightSensorEventListener implements SensorEventListener {
|
||||||
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
|
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
|
||||||
private float mLastSensorData;
|
private float mLastSensorData;
|
||||||
@@ -2283,6 +2293,7 @@ public class DisplayModeDirector {
|
|||||||
private final BallotBox mBallotBox;
|
private final BallotBox mBallotBox;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final SparseIntArray mHbmMode = new SparseIntArray();
|
private final SparseIntArray mHbmMode = new SparseIntArray();
|
||||||
|
private final SparseBooleanArray mHbmActive = new SparseBooleanArray();
|
||||||
private final Injector mInjector;
|
private final Injector mInjector;
|
||||||
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
|
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
|
||||||
private int mRefreshRateInHbmSunlight;
|
private int mRefreshRateInHbmSunlight;
|
||||||
@@ -2351,6 +2362,7 @@ public class DisplayModeDirector {
|
|||||||
public void onDisplayRemoved(int displayId) {
|
public void onDisplayRemoved(int displayId) {
|
||||||
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
|
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
|
||||||
mHbmMode.delete(displayId);
|
mHbmMode.delete(displayId);
|
||||||
|
mHbmActive.delete(displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2360,12 +2372,17 @@ public class DisplayModeDirector {
|
|||||||
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
|
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int hbmMode = info.highBrightnessMode;
|
final int hbmMode = info.highBrightnessMode;
|
||||||
if (hbmMode == mHbmMode.get(displayId)) {
|
final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF &&
|
||||||
|
info.adjustedBrightness > info.highBrightnessTransitionPoint;
|
||||||
|
if (hbmMode == mHbmMode.get(displayId) &&
|
||||||
|
isHbmActive == mHbmActive.get(displayId)) {
|
||||||
// no change, ignore.
|
// no change, ignore.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mHbmMode.put(displayId, hbmMode);
|
mHbmMode.put(displayId, hbmMode);
|
||||||
|
mHbmActive.put(displayId, isHbmActive);
|
||||||
recalculateVotesForDisplay(displayId);
|
recalculateVotesForDisplay(displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2379,28 +2396,36 @@ public class DisplayModeDirector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void recalculateVotesForDisplay(int displayId) {
|
private void recalculateVotesForDisplay(int displayId) {
|
||||||
final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
|
|
||||||
Vote vote = null;
|
Vote vote = null;
|
||||||
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
|
if (mHbmActive.get(displayId, false)) {
|
||||||
// Device resource properties take priority over DisplayDeviceConfig
|
final int hbmMode =
|
||||||
if (mRefreshRateInHbmSunlight > 0) {
|
mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
|
||||||
vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
|
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
|
||||||
mRefreshRateInHbmSunlight);
|
// Device resource properties take priority over DisplayDeviceConfig
|
||||||
} else {
|
if (mRefreshRateInHbmSunlight > 0) {
|
||||||
final List<RefreshRateLimitation> limits =
|
vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
|
||||||
mDisplayManagerInternal.getRefreshRateLimitations(displayId);
|
mRefreshRateInHbmSunlight);
|
||||||
for (int i = 0; limits != null && i < limits.size(); i++) {
|
} else {
|
||||||
final RefreshRateLimitation limitation = limits.get(i);
|
final List<RefreshRateLimitation> limits =
|
||||||
if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
|
mDisplayManagerInternal.getRefreshRateLimitations(displayId);
|
||||||
vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
|
for (int i = 0; limits != null && i < limits.size(); i++) {
|
||||||
break;
|
final RefreshRateLimitation limitation = limits.get(i);
|
||||||
|
if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
|
||||||
|
vote = Vote.forRefreshRates(limitation.range.min,
|
||||||
|
limitation.range.max);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR &&
|
||||||
|
mRefreshRateInHbmHdr > 0) {
|
||||||
|
// HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for
|
||||||
|
// a vote from Device properties
|
||||||
|
vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
|
||||||
|
} else {
|
||||||
|
Slog.w(TAG, "Unexpected HBM mode " + hbmMode + " for display ID " + displayId);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
|
|
||||||
&& mRefreshRateInHbmHdr > 0) {
|
|
||||||
vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
|
|
||||||
}
|
}
|
||||||
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
|
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
|
||||||
}
|
}
|
||||||
@@ -2408,6 +2433,7 @@ public class DisplayModeDirector {
|
|||||||
void dumpLocked(PrintWriter pw) {
|
void dumpLocked(PrintWriter pw) {
|
||||||
pw.println(" HbmObserver");
|
pw.println(" HbmObserver");
|
||||||
pw.println(" mHbmMode: " + mHbmMode);
|
pw.println(" mHbmMode: " + mHbmMode);
|
||||||
|
pw.println(" mHbmActive: " + mHbmActive);
|
||||||
pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
|
pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
|
||||||
pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
|
pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
|
||||||
}
|
}
|
||||||
@@ -2630,19 +2656,11 @@ public class DisplayModeDirector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Injector {
|
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);
|
Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
DeviceConfigInterface getDeviceConfig();
|
DeviceConfigInterface getDeviceConfig();
|
||||||
|
|
||||||
void registerBrightnessObserver(@NonNull ContentResolver cr,
|
|
||||||
@NonNull ContentObserver observer);
|
|
||||||
|
|
||||||
void unregisterBrightnessObserver(@NonNull ContentResolver cr,
|
|
||||||
@NonNull ContentObserver observer);
|
|
||||||
|
|
||||||
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
|
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
|
||||||
@NonNull ContentObserver observer);
|
@NonNull ContentObserver observer);
|
||||||
|
|
||||||
@@ -2671,19 +2689,6 @@ public class DisplayModeDirector {
|
|||||||
return DeviceConfigInterface.REAL;
|
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
|
@Override
|
||||||
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
|
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
|
||||||
@NonNull ContentObserver observer) {
|
@NonNull ContentObserver observer) {
|
||||||
|
|||||||
@@ -1259,10 +1259,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
|||||||
putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
|
putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We save the brightness info *after* the brightness setting has been changed so that
|
|
||||||
// the brightness info reflects the latest value.
|
|
||||||
saveBrightnessInfo(getScreenBrightnessSetting());
|
|
||||||
|
|
||||||
// Apply dimming by at least some minimum amount when user activity
|
// Apply dimming by at least some minimum amount when user activity
|
||||||
// timeout is about to expire.
|
// timeout is about to expire.
|
||||||
if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
|
if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
|
||||||
@@ -1393,6 +1389,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
|||||||
hadUserBrightnessPoint);
|
hadUserBrightnessPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We save the brightness info *after* the brightness setting has been changed and
|
||||||
|
// adjustments made so that the brightness info reflects the latest value.
|
||||||
|
saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
|
||||||
|
} else {
|
||||||
|
saveBrightnessInfo(getScreenBrightnessSetting());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log any changes to what is currently driving the brightness setting.
|
// Log any changes to what is currently driving the brightness setting.
|
||||||
@@ -1509,18 +1510,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
|||||||
synchronized (mCachedBrightnessInfo) {
|
synchronized (mCachedBrightnessInfo) {
|
||||||
return new BrightnessInfo(
|
return new BrightnessInfo(
|
||||||
mCachedBrightnessInfo.brightness,
|
mCachedBrightnessInfo.brightness,
|
||||||
|
mCachedBrightnessInfo.adjustedBrightness,
|
||||||
mCachedBrightnessInfo.brightnessMin,
|
mCachedBrightnessInfo.brightnessMin,
|
||||||
mCachedBrightnessInfo.brightnessMax,
|
mCachedBrightnessInfo.brightnessMax,
|
||||||
mCachedBrightnessInfo.hbmMode);
|
mCachedBrightnessInfo.hbmMode,
|
||||||
|
mCachedBrightnessInfo.highBrightnessTransitionPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveBrightnessInfo(float brightness) {
|
private void saveBrightnessInfo(float brightness) {
|
||||||
|
saveBrightnessInfo(brightness, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveBrightnessInfo(float brightness, float adjustedBrightness) {
|
||||||
synchronized (mCachedBrightnessInfo) {
|
synchronized (mCachedBrightnessInfo) {
|
||||||
mCachedBrightnessInfo.brightness = brightness;
|
mCachedBrightnessInfo.brightness = brightness;
|
||||||
|
mCachedBrightnessInfo.adjustedBrightness = adjustedBrightness;
|
||||||
mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin();
|
mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin();
|
||||||
mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax();
|
mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax();
|
||||||
mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode();
|
mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode();
|
||||||
|
mCachedBrightnessInfo.highBrightnessTransitionPoint =
|
||||||
|
mHbmController.getTransitionPoint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2195,6 +2205,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
|||||||
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
|
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
|
||||||
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
|
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
|
||||||
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
|
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
|
||||||
|
synchronized (mCachedBrightnessInfo) {
|
||||||
|
pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness);
|
||||||
|
pw.println(" mCachedBrightnessInfo.adjustedBrightness=" +
|
||||||
|
mCachedBrightnessInfo.adjustedBrightness);
|
||||||
|
pw.println(" mCachedBrightnessInfo.brightnessMin=" +
|
||||||
|
mCachedBrightnessInfo.brightnessMin);
|
||||||
|
pw.println(" mCachedBrightnessInfo.brightnessMax=" +
|
||||||
|
mCachedBrightnessInfo.brightnessMax);
|
||||||
|
pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode);
|
||||||
|
pw.println(" mCachedBrightnessInfo.highBrightnessTransitionPoint=" +
|
||||||
|
mCachedBrightnessInfo.highBrightnessTransitionPoint);
|
||||||
|
}
|
||||||
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
|
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
|
||||||
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
|
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
|
||||||
|
|
||||||
@@ -2606,8 +2628,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
|||||||
|
|
||||||
static class CachedBrightnessInfo {
|
static class CachedBrightnessInfo {
|
||||||
public float brightness;
|
public float brightness;
|
||||||
|
public float adjustedBrightness;
|
||||||
public float brightnessMin;
|
public float brightnessMin;
|
||||||
public float brightnessMax;
|
public float brightnessMax;
|
||||||
public int hbmMode;
|
public int hbmMode;
|
||||||
|
public float highBrightnessTransitionPoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ class HighBrightnessModeController {
|
|||||||
|
|
||||||
private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;
|
private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
|
||||||
|
|
||||||
private final float mBrightnessMin;
|
private final float mBrightnessMin;
|
||||||
private final float mBrightnessMax;
|
private final float mBrightnessMax;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
@@ -214,6 +217,14 @@ class HighBrightnessModeController {
|
|||||||
return mHbmMode;
|
return mHbmMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getTransitionPoint() {
|
||||||
|
if (deviceSupportsHbm()) {
|
||||||
|
return mHbmData.transitionPoint;
|
||||||
|
} else {
|
||||||
|
return HBM_TRANSITION_POINT_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
registerHdrListener(null /*displayToken*/);
|
registerHdrListener(null /*displayToken*/);
|
||||||
mSkinThermalStatusObserver.stopObserving();
|
mSkinThermalStatusObserver.stopObserving();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R
|
|||||||
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
|
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
|
||||||
|
|
||||||
import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE;
|
import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE;
|
||||||
|
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ import androidx.test.core.app.ApplicationProvider;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.internal.display.BrightnessSynchronizer;
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
import com.android.internal.util.test.FakeSettingsProvider;
|
import com.android.internal.util.test.FakeSettingsProvider;
|
||||||
import com.android.internal.util.test.FakeSettingsProviderRule;
|
import com.android.internal.util.test.FakeSettingsProviderRule;
|
||||||
@@ -110,6 +112,7 @@ public class DisplayModeDirectorTest {
|
|||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
private static final float FLOAT_TOLERANCE = 0.01f;
|
private static final float FLOAT_TOLERANCE = 0.01f;
|
||||||
private static final int DISPLAY_ID = 0;
|
private static final int DISPLAY_ID = 0;
|
||||||
|
private static final float TRANSITION_POINT = 0.763f;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private FakesInjector mInjector;
|
private FakesInjector mInjector;
|
||||||
@@ -751,19 +754,27 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
director.start(sensorManager);
|
director.start(sensorManager);
|
||||||
|
|
||||||
ArgumentCaptor<SensorEventListener> listenerCaptor =
|
ArgumentCaptor<DisplayListener> displayListenerCaptor =
|
||||||
|
ArgumentCaptor.forClass(DisplayListener.class);
|
||||||
|
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
|
||||||
|
any(Handler.class),
|
||||||
|
eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
|
||||||
|
| DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
|
||||||
|
DisplayListener displayListener = displayListenerCaptor.getValue();
|
||||||
|
|
||||||
|
ArgumentCaptor<SensorEventListener> sensorListenerCaptor =
|
||||||
ArgumentCaptor.forClass(SensorEventListener.class);
|
ArgumentCaptor.forClass(SensorEventListener.class);
|
||||||
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
|
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
|
||||||
.registerListener(
|
.registerListener(
|
||||||
listenerCaptor.capture(),
|
sensorListenerCaptor.capture(),
|
||||||
eq(lightSensor),
|
eq(lightSensor),
|
||||||
anyInt(),
|
anyInt(),
|
||||||
any(Handler.class));
|
any(Handler.class));
|
||||||
SensorEventListener listener = listenerCaptor.getValue();
|
SensorEventListener sensorListener = sensorListenerCaptor.getValue();
|
||||||
|
|
||||||
setBrightness(10);
|
setBrightness(10, 10, displayListener);
|
||||||
// Sensor reads 20 lux,
|
// Sensor reads 20 lux,
|
||||||
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
|
sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
|
||||||
|
|
||||||
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
||||||
assertVoteForRefreshRate(vote, 90 /*fps*/);
|
assertVoteForRefreshRate(vote, 90 /*fps*/);
|
||||||
@@ -771,9 +782,11 @@ public class DisplayModeDirectorTest {
|
|||||||
assertThat(vote).isNotNull();
|
assertThat(vote).isNotNull();
|
||||||
assertThat(vote.disableRefreshRateSwitching).isTrue();
|
assertThat(vote.disableRefreshRateSwitching).isTrue();
|
||||||
|
|
||||||
setBrightness(125);
|
// We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
|
||||||
|
// parameter to the necessary threshold
|
||||||
|
setBrightness(10, 125, displayListener);
|
||||||
// Sensor reads 1000 lux,
|
// Sensor reads 1000 lux,
|
||||||
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
|
sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
|
||||||
|
|
||||||
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
||||||
assertThat(vote).isNull();
|
assertThat(vote).isNull();
|
||||||
@@ -799,6 +812,14 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
director.start(sensorManager);
|
director.start(sensorManager);
|
||||||
|
|
||||||
|
ArgumentCaptor<DisplayListener> displayListenerCaptor =
|
||||||
|
ArgumentCaptor.forClass(DisplayListener.class);
|
||||||
|
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
|
||||||
|
any(Handler.class),
|
||||||
|
eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
|
||||||
|
| DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
|
||||||
|
DisplayListener displayListener = displayListenerCaptor.getValue();
|
||||||
|
|
||||||
ArgumentCaptor<SensorEventListener> listenerCaptor =
|
ArgumentCaptor<SensorEventListener> listenerCaptor =
|
||||||
ArgumentCaptor.forClass(SensorEventListener.class);
|
ArgumentCaptor.forClass(SensorEventListener.class);
|
||||||
verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
|
verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
|
||||||
@@ -807,20 +828,22 @@ public class DisplayModeDirectorTest {
|
|||||||
eq(lightSensor),
|
eq(lightSensor),
|
||||||
anyInt(),
|
anyInt(),
|
||||||
any(Handler.class));
|
any(Handler.class));
|
||||||
SensorEventListener listener = listenerCaptor.getValue();
|
SensorEventListener sensorListener = listenerCaptor.getValue();
|
||||||
|
|
||||||
setBrightness(100);
|
setBrightness(100, 100, displayListener);
|
||||||
// Sensor reads 2000 lux,
|
// Sensor reads 2000 lux,
|
||||||
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
|
sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
|
||||||
|
|
||||||
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
||||||
assertThat(vote).isNull();
|
assertThat(vote).isNull();
|
||||||
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
|
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
|
||||||
assertThat(vote).isNull();
|
assertThat(vote).isNull();
|
||||||
|
|
||||||
setBrightness(255);
|
// We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
|
||||||
|
// parameter to the necessary threshold
|
||||||
|
setBrightness(100, 255, displayListener);
|
||||||
// Sensor reads 9000 lux,
|
// Sensor reads 9000 lux,
|
||||||
listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
|
sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
|
||||||
|
|
||||||
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
|
||||||
assertVoteForRefreshRate(vote, 60 /*fps*/);
|
assertVoteForRefreshRate(vote, 60 /*fps*/);
|
||||||
@@ -1435,16 +1458,58 @@ public class DisplayModeDirectorTest {
|
|||||||
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertNull(vote);
|
assertNull(vote);
|
||||||
|
|
||||||
// Turn on HBM
|
// Turn on HBM, with brightness in the HBM range
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
|
new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
|
||||||
|
BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertVoteForRefreshRate(vote, hbmRefreshRate);
|
||||||
|
|
||||||
|
// Turn on HBM, with brightness below the HBM range
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
|
||||||
|
BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn off HBM
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn on HBM, with brightness in the HBM range
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
|
||||||
|
BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertVoteForRefreshRate(vote, hbmRefreshRate);
|
assertVoteForRefreshRate(vote, hbmRefreshRate);
|
||||||
|
|
||||||
// Turn off HBM
|
// Turn off HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
|
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn on HBM, with brightness below the HBM range
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
|
||||||
|
BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn off HBM
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertNull(vote);
|
assertNull(vote);
|
||||||
@@ -1514,7 +1579,8 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
// Turn on HBM
|
// Turn on HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertVoteForRefreshRate(vote, initialRefreshRate);
|
assertVoteForRefreshRate(vote, initialRefreshRate);
|
||||||
@@ -1531,14 +1597,16 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
// Turn off HBM
|
// Turn off HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
|
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertNull(vote);
|
assertNull(vote);
|
||||||
|
|
||||||
// Turn HBM on again and ensure the updated vote value stuck
|
// Turn HBM on again and ensure the updated vote value stuck
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertVoteForRefreshRate(vote, updatedRefreshRate);
|
assertVoteForRefreshRate(vote, updatedRefreshRate);
|
||||||
@@ -1553,7 +1621,8 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
// Turn off HBM
|
// Turn off HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
|
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertNull(vote);
|
assertNull(vote);
|
||||||
@@ -1584,14 +1653,82 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
// Turn on HBM
|
// Turn on HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertNull(vote);
|
assertNull(vote);
|
||||||
|
|
||||||
// Turn off HBM
|
// Turn off HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
|
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHbmVoting_HbmUnsupported() {
|
||||||
|
DisplayModeDirector director =
|
||||||
|
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
|
||||||
|
director.start(createMockSensorManager());
|
||||||
|
|
||||||
|
ArgumentCaptor<DisplayListener> captor =
|
||||||
|
ArgumentCaptor.forClass(DisplayListener.class);
|
||||||
|
verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
|
||||||
|
eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
|
||||||
|
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
|
||||||
|
DisplayListener listener = captor.getValue();
|
||||||
|
|
||||||
|
// Specify Limitation
|
||||||
|
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
|
||||||
|
List.of(new RefreshRateLimitation(
|
||||||
|
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
|
||||||
|
60.0f, 60.0f)));
|
||||||
|
|
||||||
|
// Verify that there is no HBM vote initially
|
||||||
|
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn on HBM when HBM is supported; expect a valid transition point and a vote.
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
|
||||||
|
TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertVoteForRefreshRate(vote, 60.0f);
|
||||||
|
|
||||||
|
// Turn off HBM
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn on Sunlight HBM when HBM is unsupported; expect an invalid transition point and
|
||||||
|
// no vote.
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
|
||||||
|
HBM_TRANSITION_POINT_INVALID));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn on HDR HBM when HBM is unsupported; expect an invalid transition point and
|
||||||
|
// no vote.
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
|
||||||
|
HBM_TRANSITION_POINT_INVALID));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
|
assertNull(vote);
|
||||||
|
|
||||||
|
// Turn off HBM
|
||||||
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertNull(vote);
|
assertNull(vote);
|
||||||
@@ -1600,7 +1737,7 @@ public class DisplayModeDirectorTest {
|
|||||||
private void setHbmAndAssertRefreshRate(
|
private void setHbmAndAssertRefreshRate(
|
||||||
DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
|
DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID))
|
when(mInjector.getBrightnessInfo(DISPLAY_ID))
|
||||||
.thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode));
|
.thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
|
|
||||||
final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
@@ -1679,7 +1816,8 @@ public class DisplayModeDirectorTest {
|
|||||||
|
|
||||||
// Turn on HBM
|
// Turn on HBM
|
||||||
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
|
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
|
||||||
|
TRANSITION_POINT));
|
||||||
listener.onDisplayChanged(DISPLAY_ID);
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
|
||||||
assertVoteForRefreshRate(vote, 60.f);
|
assertVoteForRefreshRate(vote, 60.f);
|
||||||
@@ -1834,11 +1972,14 @@ public class DisplayModeDirectorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBrightness(int brightness) {
|
private void setBrightness(int brightness, int adjustedBrightness, DisplayListener listener) {
|
||||||
Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
|
float floatBri = BrightnessSynchronizer.brightnessIntToFloat(brightness);
|
||||||
brightness);
|
float floatAdjBri = BrightnessSynchronizer.brightnessIntToFloat(adjustedBrightness);
|
||||||
mInjector.notifyBrightnessChanged();
|
|
||||||
waitForIdleSync();
|
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
|
||||||
|
new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
|
||||||
|
BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT));
|
||||||
|
listener.onDisplayChanged(DISPLAY_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPeakRefreshRate(float fps) {
|
private void setPeakRefreshRate(float fps) {
|
||||||
@@ -1901,27 +2042,6 @@ public class DisplayModeDirectorTest {
|
|||||||
return mDeviceConfig;
|
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
|
@Override
|
||||||
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
|
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
|
||||||
@NonNull ContentObserver observer) {
|
@NonNull ContentObserver observer) {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
|
|||||||
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
|
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
|
||||||
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
|
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
|
||||||
|
|
||||||
|
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
@@ -124,6 +126,7 @@ public class HighBrightnessModeControllerTest {
|
|||||||
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
|
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
|
||||||
DEFAULT_MAX, null, () -> {}, mContextSpy);
|
DEFAULT_MAX, null, () -> {}, mContextSpy);
|
||||||
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
|
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
|
||||||
|
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -135,6 +138,7 @@ public class HighBrightnessModeControllerTest {
|
|||||||
hbmc.setAutoBrightnessEnabled(true);
|
hbmc.setAutoBrightnessEnabled(true);
|
||||||
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
|
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
|
||||||
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
|
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
|
||||||
|
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user