Merge "High Brightness Mode (HBM) Refresh Rate Limiting." into sc-dev

This commit is contained in:
Santos Cordon
2021-06-22 11:23:02 +00:00
committed by Android (Google) Code Review
7 changed files with 386 additions and 22 deletions

View File

@@ -16,6 +16,7 @@
package android.hardware.display;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
@@ -29,6 +30,9 @@ import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
/**
@@ -37,6 +41,16 @@ import java.util.Objects;
* @hide Only for use within the system server.
*/
public abstract class DisplayManagerInternal {
@IntDef(prefix = {"REFRESH_RATE_LIMIT_"}, value = {
REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE
})
@Retention(RetentionPolicy.SOURCE)
public @interface RefreshRateLimitType {}
/** Refresh rate should be limited when High Brightness Mode is active. */
public static final int REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE = 1;
/**
* Called by the power manager to initialize power management facilities.
*/
@@ -296,9 +310,10 @@ public abstract class DisplayManagerInternal {
public abstract int getRefreshRateSwitchingType();
/**
* TODO: b/191384041 - Replace this with getRefreshRateLimitations()
* Return the refresh rate restriction for the specified display and sensor pairing. If the
* specified sensor is identified as an associated sensor in the specified display's
* display-device-config file, then return any refresh rate restrictions that it might specify.
* display-device-config file, then return any refresh rate restrictions that it might define.
* If no restriction is specified, or the sensor is not associated with the display, then null
* will be returned.
*
@@ -312,6 +327,15 @@ public abstract class DisplayManagerInternal {
public abstract RefreshRateRange getRefreshRateForDisplayAndSensor(
int displayId, String name, String type);
/**
* Returns a list of various refresh rate limitations for the specified display.
*
* @param displayId The display to get limitations for.
*
* @return a list of {@link RefreshRateLimitation}s describing the various limits.
*/
public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
/**
* Describes the requested power state of the display.
*
@@ -613,4 +637,25 @@ public abstract class DisplayManagerInternal {
return "(" + min + " " + max + ")";
}
}
/**
* Describes a limitation on a display's refresh rate. Includes the allowed refresh rate
* range as well as information about when it applies, such as high-brightness-mode.
*/
public static final class RefreshRateLimitation {
@RefreshRateLimitType public int type;
/** The range the that refresh rate should be limited to. */
public RefreshRateRange range;
public RefreshRateLimitation(@RefreshRateLimitType int type, float min, float max) {
this.type = type;
range = new RefreshRateRange(min, max);
}
@Override
public String toString() {
return "RefreshRateLimitation(" + type + ": " + range + ")";
}
}
}

View File

@@ -19,6 +19,8 @@ package com.android.server.display;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.os.Environment;
import android.os.PowerManager;
import android.text.TextUtils;
@@ -86,6 +88,9 @@ public class DisplayDeviceConfig {
// The details of the proximity sensor associated with this display.
private final SensorData mProximitySensor = new SensorData();
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
// Nits and backlight values that are loaded from either the display device config file, or
// config.xml. These are the raw values and just used for the dumpsys
private float[] mRawNits;
@@ -306,6 +311,10 @@ public class DisplayDeviceConfig {
return hbmData;
}
public List<RefreshRateLimitation> getRefreshRateLimitations() {
return mRefreshRateLimitations;
}
@Override
public String toString() {
String str = "DisplayDeviceConfig{"
@@ -329,6 +338,7 @@ public class DisplayDeviceConfig {
+ ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ "}";
return str;
}
@@ -647,6 +657,13 @@ public class DisplayDeviceConfig {
mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
final RefreshRateRange rr = hbm.getRefreshRate_all();
if (rr != null) {
final float min = rr.getMinimum().floatValue();
final float max = rr.getMaximum().floatValue();
mRefreshRateLimitations.add(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
}
}
}

View File

@@ -63,6 +63,7 @@ import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
@@ -130,6 +131,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
@@ -2111,6 +2113,11 @@ public final class DisplayManagerService extends SystemService {
DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
}
private DisplayDevice getDeviceForDisplayLocked(int displayId) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
return display == null ? null : display.getPrimaryDisplayDeviceLocked();
}
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -3295,6 +3302,19 @@ public final class DisplayManagerService extends SystemService {
}
return null;
}
@Override
public List<RefreshRateLimitation> getRefreshRateLimitations(int displayId) {
final DisplayDeviceConfig config;
synchronized (mSyncRoot) {
final DisplayDevice device = getDeviceForDisplayLocked(displayId);
if (device == null) {
return null;
}
config = device.getDisplayDeviceConfig();
}
return config.getRefreshRateLimitations();
}
}
class DesiredDisplayModeSpecsObserver

View File

@@ -16,6 +16,8 @@
package com.android.server.display;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -26,8 +28,10 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
@@ -102,6 +106,7 @@ public class DisplayModeDirector {
private final DisplayObserver mDisplayObserver;
private final UdfpsObserver mUdfpsObserver;
private final SensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@@ -127,7 +132,7 @@ public class DisplayModeDirector {
private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
this(context, handler, new RealInjector());
this(context, handler, new RealInjector(context));
}
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@@ -143,11 +148,13 @@ public class DisplayModeDirector {
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mUdfpsObserver = new UdfpsObserver();
mSensorObserver = new SensorObserver(context, (displayId, priority, vote) -> {
final BallotBox ballotBox = (displayId, priority, vote) -> {
synchronized (mLock) {
updateVoteLocked(displayId, priority, vote);
}
});
};
mSensorObserver = new SensorObserver(context, ballotBox);
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
@@ -165,6 +172,7 @@ public class DisplayModeDirector {
mDisplayObserver.observe();
mBrightnessObserver.observe(sensorManager);
mSensorObserver.observe();
mHbmObserver.observe();
synchronized (mLock) {
// We may have a listener already registered before the call to start, so go ahead and
// notify them to pick up our newly initialized state.
@@ -596,6 +604,7 @@ public class DisplayModeDirector {
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
}
}
@@ -938,13 +947,16 @@ public class DisplayModeDirector {
// user seeing the display flickering when the switches occur.
public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
// High-brightness-mode may need a specific range of refresh-rates to function properly.
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
public static final int PRIORITY_PROXIMITY = 9;
public static final int PRIORITY_PROXIMITY = 10;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
public static final int PRIORITY_UDFPS = 10;
public static final int PRIORITY_UDFPS = 11;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -1021,29 +1033,30 @@ public class DisplayModeDirector {
public static String priorityToString(int priority) {
switch (priority) {
case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
case PRIORITY_APP_REQUEST_SIZE:
return "PRIORITY_APP_REQUEST_SIZE";
case PRIORITY_DEFAULT_REFRESH_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
case PRIORITY_FLICKER_REFRESH_RATE:
return "PRIORITY_FLICKER_REFRESH_RATE";
case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
case PRIORITY_APP_REQUEST_SIZE:
return "PRIORITY_APP_REQUEST_SIZE";
case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
case PRIORITY_HIGH_BRIGHTNESS_MODE:
return "PRIORITY_HIGH_BRIGHTNESS_MODE";
case PRIORITY_PROXIMITY:
return "PRIORITY_PROXIMITY";
case PRIORITY_LOW_POWER_MODE:
return "PRIORITY_LOW_POWER_MODE";
case PRIORITY_UDFPS:
return "PRIORITY_UDFPS";
case PRIORITY_PROXIMITY:
return "PRIORITY_PROXIMITY";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
default:
return Integer.toString(priority);
}
@@ -2155,6 +2168,75 @@ public class DisplayModeDirector {
}
}
/**
* Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
* HBM that are associated with that display. Restrictions are retrieved from
* DisplayManagerInternal but originate in the display-device-config file.
*/
private static class HbmObserver implements DisplayManager.DisplayListener {
private final BallotBox mBallotBox;
private final Handler mHandler;
private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
private final Injector mInjector;
private DisplayManagerInternal mDisplayManagerInternal;
HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
mInjector = injector;
mBallotBox = ballotBox;
mHandler = handler;
}
public void observe() {
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInjector.registerDisplayListener(this, mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
}
@Override
public void onDisplayAdded(int displayId) {}
@Override
public void onDisplayRemoved(int displayId) {
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
}
@Override
public void onDisplayChanged(int displayId) {
final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
if (info == null) {
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
return;
}
final boolean isHbmEnabled =
info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
if (isHbmEnabled == mHbmEnabled.get(displayId)) {
// no change, ignore.
return;
}
Vote vote = null;
mHbmEnabled.put(displayId, isHbmEnabled);
if (isHbmEnabled) {
final List<RefreshRateLimitation> limits =
mDisplayManagerInternal.getRefreshRateLimitations(displayId);
for (int i = 0; limits != null && i < limits.size(); i++) {
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;
}
}
}
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
}
void dumpLocked(PrintWriter pw) {
pw.println(" HbmObserver");
pw.println(" mHbmEnabled: " + mHbmEnabled);
}
}
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public DeviceConfigDisplaySettings() {
}
@@ -2309,10 +2391,21 @@ public class DisplayModeDirector {
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler, long flags);
BrightnessInfo getBrightnessInfo(int displayId);
}
@VisibleForTesting
static class RealInjector implements Injector {
private final Context mContext;
private DisplayManager mDisplayManager;
RealInjector(Context context) {
mContext = context;
}
@Override
@NonNull
@@ -2339,6 +2432,28 @@ public class DisplayModeDirector {
cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener,
Handler handler, long flags) {
getDisplayManager().registerDisplayListener(listener, handler, flags);
}
@Override
public BrightnessInfo getBrightnessInfo(int displayId) {
final Display display = getDisplayManager().getDisplay(displayId);
if (display != null) {
return display.getBrightnessInfo();
}
return null;
}
private DisplayManager getDisplayManager() {
if (mDisplayManager == null) {
mDisplayManager = mContext.getSystemService(DisplayManager.class);
}
return mDisplayManager;
}
}
interface BallotBox {

View File

@@ -77,6 +77,10 @@
<xs:annotation name="final"/>
</xs:element>
<xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/>
<xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1">
<xs:annotation name="nullable"/>
<xs:annotation name="final"/>
</xs:element>
</xs:all>
<xs:attribute name="enabled" type="xs:boolean" use="optional"/>
</xs:complexType>

View File

@@ -44,10 +44,12 @@ package com.android.server.display.config {
ctor public HighBrightnessMode();
method public boolean getEnabled();
method @NonNull public final java.math.BigDecimal getMinimumLux_all();
method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all();
method public com.android.server.display.config.HbmTiming getTiming_all();
method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
method public void setEnabled(boolean);
method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange);
method public void setTiming_all(com.android.server.display.config.HbmTiming);
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}

View File

@@ -48,8 +48,11 @@ import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Handler;
@@ -120,7 +123,7 @@ public class DisplayModeDirectorTest {
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
mInjector = new FakesInjector();
mInjector = spy(new FakesInjector());
mHandler = new Handler(Looper.getMainLooper());
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -1256,13 +1259,163 @@ public class DisplayModeDirectorTest {
assertNull(vote);
}
@Test
public void testHbmVoting_forHdr() {
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.f, 60.f)));
// 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(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
}
@Test
public void testHbmVoting_forSunlight() {
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.f, 60.f)));
// 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(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
}
@Test
public void testHbmVoting_forSunlight_NoLimitation() {
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 for different display
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID + 1)).thenReturn(
List.of(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
60.f, 60.f)));
// 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(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
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.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
}
@Test
public void testHbmVoting_RemovedDisplay() {
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 for different display
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
List.of(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
60.f, 60.f)));
// 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(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
// Turn off HBM
listener.onDisplayRemoved(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
}
private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
}
private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
@Override
public String getProperty(String namespace, String name) {
Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
@@ -1403,7 +1556,7 @@ public class DisplayModeDirectorTest {
mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
}
static class FakesInjector implements DisplayModeDirector.Injector {
public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private ContentObserver mBrightnessObserver;
private ContentObserver mPeakRefreshRateObserver;
@@ -1444,6 +1597,14 @@ public class DisplayModeDirectorTest {
mPeakRefreshRateObserver = observer;
}
@Override
public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
@Override
public BrightnessInfo getBrightnessInfo(int displayId) {
return null;
}
void notifyPeakRefreshRateChanged() {
if (mPeakRefreshRateObserver != null) {
mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,