Merge "Add Display.Mode.getAlternativeRefreshRates()"
This commit is contained in:
committed by
Android (Google) Code Review
commit
e5b273412d
@@ -52693,6 +52693,7 @@ package android.view {
|
||||
|
||||
public static final class Display.Mode implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @NonNull public float[] getAlternativeRefreshRates();
|
||||
method public int getModeId();
|
||||
method public int getPhysicalHeight();
|
||||
method public int getPhysicalWidth();
|
||||
|
||||
@@ -50798,6 +50798,7 @@ package android.view {
|
||||
|
||||
public static final class Display.Mode implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @NonNull public float[] getAlternativeRefreshRates();
|
||||
method public int getModeId();
|
||||
method public int getPhysicalHeight();
|
||||
method public int getPhysicalWidth();
|
||||
|
||||
@@ -1405,16 +1405,29 @@ public final class Display {
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final float mRefreshRate;
|
||||
@NonNull
|
||||
private final float[] mAlternativeRefreshRates;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
public Mode(int modeId, int width, int height, float refreshRate) {
|
||||
this(modeId, width, height, refreshRate, new float[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public Mode(int modeId, int width, int height, float refreshRate,
|
||||
float[] alternativeRefreshRates) {
|
||||
mModeId = modeId;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mRefreshRate = refreshRate;
|
||||
mAlternativeRefreshRates =
|
||||
Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
|
||||
Arrays.sort(mAlternativeRefreshRates);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1463,6 +1476,28 @@ public final class Display {
|
||||
return mRefreshRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of refresh rates which can be switched to seamlessly.
|
||||
* <p>
|
||||
* A seamless switch is one without visual interruptions, such as a black screen for
|
||||
* a second or two.
|
||||
* <p>
|
||||
* Presence in this list does not guarantee a switch will occur to the desired
|
||||
* refresh rate, but rather, if a switch does occur to a refresh rate in this list,
|
||||
* it is guaranteed to be seamless.
|
||||
* <p>
|
||||
* The binary relation "refresh rate X is alternative to Y" is non-reflexive,
|
||||
* symmetric and transitive. For example the mode 1920x1080 60Hz, will never have an
|
||||
* alternative refresh rate of 60Hz. If 1920x1080 60Hz has an alternative of 50Hz
|
||||
* then 1920x1080 50Hz will have alternative refresh rate of 60Hz. If 1920x1080 60Hz
|
||||
* has an alternative of 50Hz and 1920x1080 50Hz has an alternative of 24Hz, then 1920x1080
|
||||
* 60Hz will also have an alternative of 24Hz.
|
||||
*/
|
||||
@NonNull
|
||||
public float[] getAlternativeRefreshRates() {
|
||||
return mAlternativeRefreshRates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this mode matches the given parameters.
|
||||
*
|
||||
@@ -1483,7 +1518,8 @@ public final class Display {
|
||||
return false;
|
||||
}
|
||||
Mode that = (Mode) other;
|
||||
return mModeId == that.mModeId && matches(that.mWidth, that.mHeight, that.mRefreshRate);
|
||||
return mModeId == that.mModeId && matches(that.mWidth, that.mHeight, that.mRefreshRate)
|
||||
&& Arrays.equals(mAlternativeRefreshRates, that.mAlternativeRefreshRates);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1493,6 +1529,7 @@ public final class Display {
|
||||
hash = hash * 17 + mWidth;
|
||||
hash = hash * 17 + mHeight;
|
||||
hash = hash * 17 + Float.floatToIntBits(mRefreshRate);
|
||||
hash = hash * 17 + Arrays.hashCode(mAlternativeRefreshRates);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1503,6 +1540,8 @@ public final class Display {
|
||||
.append(", width=").append(mWidth)
|
||||
.append(", height=").append(mHeight)
|
||||
.append(", fps=").append(mRefreshRate)
|
||||
.append(", alternativeRefreshRates=")
|
||||
.append(Arrays.toString(mAlternativeRefreshRates))
|
||||
.append("}")
|
||||
.toString();
|
||||
}
|
||||
@@ -1513,7 +1552,7 @@ public final class Display {
|
||||
}
|
||||
|
||||
private Mode(Parcel in) {
|
||||
this(in.readInt(), in.readInt(), in.readInt(), in.readFloat());
|
||||
this(in.readInt(), in.readInt(), in.readInt(), in.readFloat(), in.createFloatArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1522,6 +1561,7 @@ public final class Display {
|
||||
out.writeInt(mWidth);
|
||||
out.writeInt(mHeight);
|
||||
out.writeFloat(mRefreshRate);
|
||||
out.writeFloatArray(mAlternativeRefreshRates);
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
|
||||
@@ -120,8 +120,13 @@ abstract class DisplayAdapter {
|
||||
}
|
||||
|
||||
public static Display.Mode createMode(int width, int height, float refreshRate) {
|
||||
return new Display.Mode(
|
||||
NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate);
|
||||
return createMode(width, height, refreshRate, new float[0]);
|
||||
}
|
||||
|
||||
public static Display.Mode createMode(int width, int height, float refreshRate,
|
||||
float[] alternativeRefreshRates) {
|
||||
return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate,
|
||||
alternativeRefreshRates);
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
|
||||
@@ -266,12 +266,27 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
boolean modesAdded = false;
|
||||
for (int i = 0; i < configs.length; i++) {
|
||||
SurfaceControl.DisplayConfig config = configs[i];
|
||||
List<Float> alternativeRefreshRates = new ArrayList<>();
|
||||
for (int j = 0; j < configs.length; j++) {
|
||||
SurfaceControl.DisplayConfig other = configs[j];
|
||||
boolean isAlternative = j != i && other.width == config.width
|
||||
&& other.height == config.height
|
||||
&& other.refreshRate != config.refreshRate
|
||||
&& other.configGroup == config.configGroup;
|
||||
if (isAlternative) {
|
||||
alternativeRefreshRates.add(configs[j].refreshRate);
|
||||
}
|
||||
}
|
||||
Collections.sort(alternativeRefreshRates);
|
||||
|
||||
// First, check to see if we've already added a matching mode. Since not all
|
||||
// configuration options are exposed via Display.Mode, it's possible that we have
|
||||
// multiple DisplayConfigs that would generate the same Display.Mode.
|
||||
boolean existingMode = false;
|
||||
for (int j = 0; j < records.size(); j++) {
|
||||
if (records.get(j).hasMatchingMode(config)) {
|
||||
for (DisplayModeRecord record : records) {
|
||||
if (record.hasMatchingMode(config)
|
||||
&& refreshRatesEquals(alternativeRefreshRates,
|
||||
record.mMode.getAlternativeRefreshRates())) {
|
||||
existingMode = true;
|
||||
break;
|
||||
}
|
||||
@@ -282,9 +297,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
// If we haven't already added a mode for this configuration to the new set of
|
||||
// supported modes then check to see if we have one in the prior set of supported
|
||||
// modes to reuse.
|
||||
DisplayModeRecord record = findDisplayModeRecord(config);
|
||||
DisplayModeRecord record = findDisplayModeRecord(config, alternativeRefreshRates);
|
||||
if (record == null) {
|
||||
record = new DisplayModeRecord(config);
|
||||
float[] alternativeRates = new float[alternativeRefreshRates.size()];
|
||||
for (int j = 0; j < alternativeRates.length; j++) {
|
||||
alternativeRates[j] = alternativeRefreshRates.get(j);
|
||||
}
|
||||
record = new DisplayModeRecord(config, alternativeRates);
|
||||
modesAdded = true;
|
||||
}
|
||||
records.add(record);
|
||||
@@ -495,16 +514,31 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
return true;
|
||||
}
|
||||
|
||||
private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config) {
|
||||
private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config,
|
||||
List<Float> alternativeRefreshRates) {
|
||||
for (int i = 0; i < mSupportedModes.size(); i++) {
|
||||
DisplayModeRecord record = mSupportedModes.valueAt(i);
|
||||
if (record.hasMatchingMode(config)) {
|
||||
if (record.hasMatchingMode(config)
|
||||
&& refreshRatesEquals(alternativeRefreshRates,
|
||||
record.mMode.getAlternativeRefreshRates())) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean refreshRatesEquals(List<Float> list, float[] array) {
|
||||
if (list.size() != array.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (Float.floatToIntBits(list.get(i)) != Float.floatToIntBits(array[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyPendingDisplayDeviceInfoChangesLocked() {
|
||||
if (mHavePendingChanges) {
|
||||
@@ -1032,8 +1066,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
private static final class DisplayModeRecord {
|
||||
public final Display.Mode mMode;
|
||||
|
||||
DisplayModeRecord(SurfaceControl.DisplayConfig config) {
|
||||
mMode = createMode(config.width, config.height, config.refreshRate);
|
||||
DisplayModeRecord(SurfaceControl.DisplayConfig config,
|
||||
float[] alternativeRefreshRates) {
|
||||
mMode = createMode(config.width, config.height, config.refreshRate,
|
||||
alternativeRefreshRates);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,6 +42,8 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.lights.LightsManager;
|
||||
|
||||
import com.google.common.truth.Truth;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -190,6 +192,102 @@ public class LocalDisplayAdapterTest {
|
||||
16000);
|
||||
}
|
||||
|
||||
private static class DisplayModeWrapper {
|
||||
public SurfaceControl.DisplayConfig config;
|
||||
public float[] expectedAlternativeRefreshRates;
|
||||
|
||||
DisplayModeWrapper(SurfaceControl.DisplayConfig config,
|
||||
float[] expectedAlternativeRefreshRates) {
|
||||
this.config = config;
|
||||
this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the <code>display</code> using the given <code>modes</code> and then checks if the
|
||||
* <code>expectedAlternativeRefreshRates</code> are present for each of the
|
||||
* <code>modes</code>.
|
||||
*/
|
||||
private void testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] modes)
|
||||
throws InterruptedException {
|
||||
// Update the display.
|
||||
SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[modes.length];
|
||||
for (int i = 0; i < modes.length; i++) {
|
||||
configs[i] = modes[i].config;
|
||||
}
|
||||
display.configs = configs;
|
||||
setUpDisplay(display);
|
||||
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
|
||||
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
|
||||
assertThat(mListener.changedDisplays.size()).isGreaterThan(0);
|
||||
|
||||
// Verify the supported modes are updated accordingly.
|
||||
DisplayDevice displayDevice =
|
||||
mListener.changedDisplays.get(mListener.changedDisplays.size() - 1);
|
||||
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
|
||||
Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes;
|
||||
assertThat(supportedModes.length).isEqualTo(configs.length);
|
||||
|
||||
for (int i = 0; i < modes.length; i++) {
|
||||
assertModeIsSupported(supportedModes, configs[i],
|
||||
modes[i].expectedAlternativeRefreshRates);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterDisplayChange_AlternativeRefreshRatesAreUpdated() throws Exception {
|
||||
FakeDisplay display = new FakeDisplay(PORT_A);
|
||||
setUpDisplay(display);
|
||||
updateAvailableDisplays();
|
||||
mAdapter.registerLocked();
|
||||
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
|
||||
|
||||
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{24f, 50f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{24f, 60f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 24f, 0), new float[]{50f, 60f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 60f, 0), new float[]{24f, 50f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 50f, 0), new float[]{24f, 60f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 24f, 0), new float[]{50f, 60f}),
|
||||
});
|
||||
|
||||
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{50f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{60f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 24f, 1), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 60f, 2), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 50f, 3), new float[]{24f}),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 24f, 3), new float[]{50f}),
|
||||
});
|
||||
|
||||
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 60f, 0), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 50f, 1), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(1920, 1080, 24f, 2), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 60f, 3), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 50f, 4), new float[0]),
|
||||
new DisplayModeWrapper(
|
||||
createFakeDisplayConfig(3840, 2160, 24f, 5), new float[0]),
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
|
||||
SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
|
||||
@@ -419,6 +517,23 @@ public class LocalDisplayAdapterTest {
|
||||
x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue();
|
||||
}
|
||||
|
||||
private void assertModeIsSupported(Display.Mode[] supportedModes,
|
||||
SurfaceControl.DisplayConfig mode, float[] alternativeRefreshRates) {
|
||||
float[] sortedAlternativeRates =
|
||||
Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
|
||||
Arrays.sort(sortedAlternativeRates);
|
||||
|
||||
String message = "Expected " + mode + " with alternativeRefreshRates = "
|
||||
+ Arrays.toString(alternativeRefreshRates) + " to be in list of supported modes = "
|
||||
+ Arrays.toString(supportedModes);
|
||||
Truth.assertWithMessage(message)
|
||||
.that(Arrays.stream(supportedModes)
|
||||
.anyMatch(x -> x.matches(mode.width, mode.height, mode.refreshRate)
|
||||
&& Arrays.equals(x.getAlternativeRefreshRates(), sortedAlternativeRates)))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
|
||||
private static class FakeDisplay {
|
||||
public final DisplayAddress.Physical address;
|
||||
public final IBinder token = new Binder();
|
||||
@@ -492,12 +607,18 @@ public class LocalDisplayAdapterTest {
|
||||
|
||||
private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
|
||||
float refreshRate) {
|
||||
return createFakeDisplayConfig(width, height, refreshRate, 0);
|
||||
}
|
||||
|
||||
private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
|
||||
float refreshRate, int configGroup) {
|
||||
final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
|
||||
config.width = width;
|
||||
config.height = height;
|
||||
config.refreshRate = refreshRate;
|
||||
config.xDpi = 100;
|
||||
config.yDpi = 100;
|
||||
config.configGroup = configGroup;
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user