Add complementary display concept

Register a display as complementary, if it is a nondefault, enabled
display in the layout.
Expose through logicaldisplay and displaymanager.

Bug: 266695281
Test: com.android.server.display
Change-Id: I766d9f605e23306357f1a0fd9e795568d37157e7
This commit is contained in:
Fiona Campbell
2023-01-25 16:03:45 +00:00
parent 10f357ecdb
commit 7f10d9ecb4
7 changed files with 152 additions and 38 deletions

View File

@@ -765,7 +765,7 @@ public final class DisplayInfo implements Parcelable {
sb.append(name);
sb.append("\", displayId ");
sb.append(displayId);
sb.append("\", displayGroupId ");
sb.append(", displayGroupId ");
sb.append(displayGroupId);
sb.append(flagsToString(flags));
sb.append(", real ");

View File

@@ -22,6 +22,7 @@ import android.os.Environment;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAddress;
import com.android.internal.annotations.VisibleForTesting;
@@ -114,6 +115,7 @@ class DeviceStateToLayoutMap {
Slog.i(TAG, "Display layout config not found: " + configFile);
return;
}
int leadDisplayId = Display.DEFAULT_DISPLAY;
for (com.android.server.display.config.layout.Layout l : layouts.getLayout()) {
final int state = l.getState().intValue();
final Layout layout = createLayout(state);
@@ -124,7 +126,8 @@ class DeviceStateToLayoutMap {
d.isDefaultDisplay(),
d.isEnabled(),
mIdProducer,
d.getBrightnessThrottlingMapId());
d.getBrightnessThrottlingMapId(),
leadDisplayId);
if (FRONT_STRING.equals(d.getPosition())) {
display.setPosition(POSITION_FRONT);

View File

@@ -77,6 +77,12 @@ final class LogicalDisplay {
private final int mDisplayId;
private final int mLayerStack;
// Indicates which display leads this logical display, in terms of brightness or other
// properties.
// {@link Layout.NO_LEAD_DISPLAY} means that this display is not lead by any others, and could
// be a leader itself.
private int mLeadDisplayId = Layout.NO_LEAD_DISPLAY;
private int mDisplayGroupId = Display.INVALID_DISPLAY_GROUP;
/**
@@ -150,7 +156,7 @@ final class LogicalDisplay {
// Indicates the display is part of a transition from one device-state ({@link
// DeviceStateManager}) to another. Being a "part" of a transition means that either
// the {@link mIsEnabled} is changing, or the underlying mPrimiaryDisplayDevice is changing.
// the {@link mIsEnabled} is changing, or the underlying mPrimaryDisplayDevice is changing.
private boolean mIsInTransition;
// Indicates the position of the display, POSITION_UNKNOWN could mean it hasn't been specified,
@@ -826,6 +832,27 @@ final class LogicalDisplay {
brightnessThrottlingDataId;
}
/**
* Sets the display of which this display is a follower, regarding brightness or other
* properties. If set to {@link Layout#NO_LEAD_DISPLAY}, this display does not follow any
* others, and has the potential to be a lead display to others.
*
* A display cannot be a leader or follower of itself, and there cannot be cycles.
* A display cannot be both a leader and a follower, ie, there must not be any chains.
*
* @param displayId logical display id
*/
public void setLeadDisplayLocked(int displayId) {
if (mDisplayId != mLeadDisplayId && mDisplayId != displayId) {
mLeadDisplayId = displayId;
}
}
public int getLeadDisplayLocked() {
return mLeadDisplayId;
}
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mIsEnabled=" + mIsEnabled);
@@ -845,6 +872,7 @@ final class LogicalDisplay {
pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
pw.println("mBrightnessThrottlingDataId=" + mBrightnessThrottlingDataId);
pw.println("mLeadDisplayId=" + mLeadDisplayId);
}
@Override

View File

@@ -18,6 +18,8 @@ package com.android.server.display;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.display.layout.Layout.NO_LEAD_DISPLAY;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -639,7 +641,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
&& !nextDeviceInfo.address.equals(deviceInfo.address)) {
layout.createDisplayLocked(nextDeviceInfo.address,
/* isDefault= */ true, /* isEnabled= */ true, mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null, DEFAULT_DISPLAY);
applyLayoutLocked();
return;
}
@@ -991,6 +993,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
newDisplay.setPositionLocked(displayLayout.getPosition());
newDisplay.setLeadDisplayLocked(displayLayout.getLeadDisplayId());
setLayoutLimitedRefreshRate(newDisplay, device, displayLayout);
setEnabledLocked(newDisplay, displayLayout.isEnabled());
newDisplay.setBrightnessThrottlingDataIdLocked(
@@ -1076,7 +1079,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
layout.createDisplayLocked(info.address, /* isDefault= */ true, /* isEnabled= */ true,
mIdProducer, /* brightnessThrottlingMapId= */ null);
mIdProducer, /* brightnessThrottlingMapId= */ null, NO_LEAD_DISPLAY);
}
private int assignLayerStackLocked(int displayId) {

View File

@@ -39,6 +39,10 @@ public class Layout {
private static final String TAG = "Layout";
private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;
// Lead display Id is set to this if this is not a follower display, and therefore
// has no lead.
public static final int NO_LEAD_DISPLAY = -1;
private final List<Display> mDisplays = new ArrayList<>(2);
/**
@@ -75,13 +79,16 @@ public class Layout {
* @param address Address of the device.
* @param isDefault Indicates if the device is meant to be the default display.
* @param isEnabled Indicates if this display is usable and can be switched on
* @return The new layout.
* @param idProducer Produces the logical display id.
* @param brightnessThrottlingMapId Name of which throttling policy should be used.
* @param leadDisplayId Display that this one follows (-1 if none).
* @return The new Display.
*/
public Display createDisplayLocked(
@NonNull DisplayAddress address, boolean isDefault, boolean isEnabled,
DisplayIdProducer idProducer, String brightnessThrottlingMapId) {
DisplayIdProducer idProducer, String brightnessThrottlingMapId, int leadDisplayId) {
return createDisplayLocked(address, isDefault, isEnabled, idProducer,
brightnessThrottlingMapId, POSITION_UNKNOWN);
brightnessThrottlingMapId, POSITION_UNKNOWN, leadDisplayId);
}
/**
@@ -90,12 +97,16 @@ public class Layout {
* @param address Address of the device.
* @param isDefault Indicates if the device is meant to be the default display.
* @param isEnabled Indicates if this display is usable and can be switched on
* @param idProducer Produces the logical display id.
* @param brightnessThrottlingMapId Name of which throttling policy should be used.
* @param position Indicates the position this display is facing in this layout.
* @return The new layout.
* @param leadDisplayId Display that this one follows (-1 if none).
* @return The new Display.
*/
public Display createDisplayLocked(
@NonNull DisplayAddress address, boolean isDefault, boolean isEnabled,
DisplayIdProducer idProducer, String brightnessThrottlingMapId, int position) {
DisplayIdProducer idProducer, String brightnessThrottlingMapId, int position,
int leadDisplayId) {
if (contains(address)) {
Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
return null;
@@ -113,7 +124,7 @@ public class Layout {
// same logical display ID.
final int logicalDisplayId = idProducer.getId(isDefault);
final Display display = new Display(address, logicalDisplayId, isEnabled,
brightnessThrottlingMapId, position);
brightnessThrottlingMapId, position, leadDisplayId);
mDisplays.add(display);
return display;
@@ -221,17 +232,27 @@ public class Layout {
@Nullable
private final String mBrightnessThrottlingMapId;
// The ID of the lead display that this display will follow in a layout. -1 means no lead.
private int mLeadDisplayId;
// Refresh rate zone id for specific layout
@Nullable
private String mRefreshRateZoneId;
Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled,
String brightnessThrottlingMapId, int position) {
String brightnessThrottlingMapId, int position, int leadDisplayId) {
mAddress = address;
mLogicalDisplayId = logicalDisplayId;
mIsEnabled = isEnabled;
mPosition = position;
mBrightnessThrottlingMapId = brightnessThrottlingMapId;
if (leadDisplayId == mLogicalDisplayId) {
mLeadDisplayId = NO_LEAD_DISPLAY;
} else {
mLeadDisplayId = leadDisplayId;
}
}
@Override
@@ -243,6 +264,7 @@ public class Layout {
+ ((mPosition == POSITION_UNKNOWN) ? "" : ", position: " + mPosition)
+ ", brightnessThrottlingMapId: " + mBrightnessThrottlingMapId
+ ", mRefreshRateZoneId: " + mRefreshRateZoneId
+ ", mLeadDisplayId: " + mLeadDisplayId
+ "}";
}
@@ -260,7 +282,8 @@ public class Layout {
&& this.mAddress.equals(otherDisplay.mAddress)
&& Objects.equals(mBrightnessThrottlingMapId,
otherDisplay.mBrightnessThrottlingMapId)
&& Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId);
&& Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId)
&& this.mLeadDisplayId == otherDisplay.mLeadDisplayId;
}
@Override
@@ -272,6 +295,7 @@ public class Layout {
result = 31 * result + mAddress.hashCode();
result = 31 * result + mBrightnessThrottlingMapId.hashCode();
result = 31 * result + Objects.hashCode(mRefreshRateZoneId);
result = 31 * result + mLeadDisplayId;
return result;
}
@@ -297,6 +321,10 @@ public class Layout {
return mRefreshRateZoneId;
}
/**
* Sets the position that this display is facing.
* @param position the display is facing.
*/
public void setPosition(int position) {
mPosition = position;
}
@@ -308,8 +336,31 @@ public class Layout {
return mBrightnessThrottlingMapId;
}
/**
*
* @return the position that this display is facing.
*/
public int getPosition() {
return mPosition;
}
/**
* Set the display that this display should follow certain properties of, for example,
* brightness
* @param displayId of the lead display.
*/
public void setLeadDisplay(int displayId) {
if (displayId != mLogicalDisplayId) {
mLeadDisplayId = displayId;
}
}
/**
*
* @return logical displayId of the display that this one follows.
*/
public int getLeadDisplayId() {
return mLeadDisplayId;
}
}
}

View File

@@ -19,6 +19,7 @@ package com.android.server.display;
import static org.junit.Assert.assertEquals;
import android.view.Display;
import android.view.DisplayAddress;
import androidx.test.filters.SmallTest;
@@ -65,11 +66,13 @@ public class DeviceStateToLayoutMapTest {
testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(123456L), /* isDefault= */ true,
/* isEnabled= */ true, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(78910L), /* isDefault= */ false,
/* isEnabled= */ false, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
assertEquals(testLayout, configLayout);
}
@@ -81,11 +84,13 @@ public class DeviceStateToLayoutMapTest {
testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(78910L), /* isDefault= */ true,
/* isEnabled= */ true, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(123456L), /* isDefault= */ false,
/* isEnabled= */ false, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
assertEquals(testLayout, configLayout);
}
@@ -99,13 +104,15 @@ public class DeviceStateToLayoutMapTest {
Layout.Display display1 = testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(345L), /* isDefault= */ true,
/* isEnabled= */ true, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ "concurrent");
/* brightnessThrottlingMapId= */ "concurrent",
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
display1.setPosition(Layout.Display.POSITION_FRONT);
Layout.Display display2 = testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(678L), /* isDefault= */ false,
/* isEnabled= */ true, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ "concurrent");
/* brightnessThrottlingMapId= */ "concurrent",
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
display2.setPosition(Layout.Display.POSITION_REAR);
assertEquals(testLayout, configLayout);
@@ -127,12 +134,14 @@ public class DeviceStateToLayoutMapTest {
Layout.Display display1 = testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(345L), /* isDefault= */ true,
/* isEnabled= */ true, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
display1.setRefreshRateZoneId("test1");
testLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(678L), /* isDefault= */ false,
/* isEnabled= */ true, mDisplayIdProducerMock,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
assertEquals(testLayout, configLayout);
}

View File

@@ -299,9 +299,11 @@ public class LogicalDisplayMapperTest {
Layout layout1 = new Layout();
layout1.createDisplayLocked(info(device1).address, /* isDefault= */ true,
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null);
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
layout1.createDisplayLocked(info(device2).address, /* isDefault= */ false,
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null);
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(STATE_DEFAULT)).thenReturn(layout1);
assertThat(layout1.size()).isEqualTo(2);
final int logicalId2 = layout1.getByAddress(info(device2).address).getLogicalDisplayId();
@@ -335,16 +337,19 @@ public class LogicalDisplayMapperTest {
Layout layout1 = new Layout();
layout1.createDisplayLocked(info(device1).address, /* isDefault= */ true,
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null);
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(STATE_DEFAULT)).thenReturn(layout1);
final int layoutState2 = 2;
Layout layout2 = new Layout();
layout2.createDisplayLocked(info(device2).address, /* isDefault= */ false,
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null);
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
// Device3 is the default display.
layout2.createDisplayLocked(info(device3).address, /* isDefault= */ true,
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null);
/* isEnabled= */ true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(layoutState2)).thenReturn(layout2);
assertThat(layout2.size()).isEqualTo(2);
final int logicalId2 = layout2.getByAddress(info(device2).address).getLogicalDisplayId();
@@ -567,17 +572,21 @@ public class LogicalDisplayMapperTest {
Layout layout = new Layout();
layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address,
true, true, mIdProducer,
/* brightnessThrottlingMapId= */ "concurrent");
/* brightnessThrottlingMapId= */ "concurrent",
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address,
false, true, mIdProducer,
/* brightnessThrottlingMapId= */ "concurrent");
/* brightnessThrottlingMapId= */ "concurrent",
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
layout = new Layout();
layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address,
false, false, mIdProducer, /* brightnessThrottlingMapId= */ null);
false, false, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address,
true, true, mIdProducer, /* brightnessThrottlingMapId= */ null);
true, true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(layout);
when(mDeviceStateToLayoutMapSpy.get(2)).thenReturn(layout);
@@ -604,6 +613,10 @@ public class LogicalDisplayMapperTest {
assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
assertEquals(-1, mLogicalDisplayMapper.getDisplayLocked(device1)
.getLeadDisplayLocked());
assertEquals(0, mLogicalDisplayMapper.getDisplayLocked(device2)
.getLeadDisplayLocked());
assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device1)
.getBrightnessThrottlingDataIdLocked());
assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device2)
@@ -655,19 +668,22 @@ public class LogicalDisplayMapperTest {
/* isDefault= */ true,
/* isEnabled= */ true,
mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
threeDevicesEnabledLayout.createDisplayLocked(
displayAddressTwo,
/* isDefault= */ false,
/* isEnabled= */ true,
mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
threeDevicesEnabledLayout.createDisplayLocked(
displayAddressThree,
/* isDefault= */ false,
/* isEnabled= */ true,
mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(STATE_DEFAULT))
.thenReturn(threeDevicesEnabledLayout);
@@ -703,19 +719,22 @@ public class LogicalDisplayMapperTest {
/* isDefault= */ true,
/* isEnabled= */ true,
mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
oneDeviceEnabledLayout.createDisplayLocked(
displayAddressTwo,
/* isDefault= */ false,
/* isEnabled= */ false,
mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
oneDeviceEnabledLayout.createDisplayLocked(
displayAddressThree,
/* isDefault= */ false,
/* isEnabled= */ false,
mIdProducer,
/* brightnessThrottlingMapId= */ null);
/* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout);
when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout);
@@ -790,10 +809,11 @@ public class LogicalDisplayMapperTest {
Layout layout = new Layout();
layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address,
true, true, mIdProducer, /* brightnessThrottlingMapId= */ null);
true, true, mIdProducer, /* brightnessThrottlingMapId= */ null,
/* leadDisplayId= */ Display.DEFAULT_DISPLAY);
layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address,
false, true, mIdProducer, /* brightnessThrottlingMapId= */ null,
POSITION_REAR);
POSITION_REAR, Display.DEFAULT_DISPLAY);
when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
when(mDeviceStateToLayoutMapSpy.size()).thenReturn(1);