Loading services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +7 −3 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ 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; Loading @@ -38,6 +37,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import javax.xml.datatype.DatatypeConfigurationException; Loading Loading @@ -115,13 +115,16 @@ 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); for (com.android.server.display.config.layout.Display d: l.getDisplay()) { assert layout != null; int position = getPosition(d.getPosition()); BigInteger leadDisplayPhysicalId = d.getLeadDisplayAddress(); DisplayAddress leadDisplayAddress = leadDisplayPhysicalId == null ? null : DisplayAddress.fromPhysicalDisplayId( leadDisplayPhysicalId.longValue()); layout.createDisplayLocked( DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()), d.isDefaultDisplay(), Loading @@ -129,11 +132,12 @@ class DeviceStateToLayoutMap { d.getDisplayGroup(), mIdProducer, position, leadDisplayId, leadDisplayAddress, d.getBrightnessThrottlingMapId(), d.getRefreshRateZoneId(), d.getRefreshRateThermalThrottlingMapId()); } layout.postProcessLocked(); } } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { Slog.e(TAG, "Encountered an error while reading/parsing display layout config file: " Loading services/core/java/com/android/server/display/layout/Layout.java +97 −16 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; import android.view.DisplayAddress; Loading Loading @@ -77,7 +79,7 @@ public class Layout { DisplayIdProducer idProducer) { createDisplayLocked(address, /* isDefault= */ true, /* isEnabled= */ true, DEFAULT_DISPLAY_GROUP_NAME, idProducer, POSITION_UNKNOWN, NO_LEAD_DISPLAY, /* brightnessThrottlingMapId= */ null, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null); } Loading @@ -90,19 +92,20 @@ public class Layout { * @param displayGroupName Name of the display group to which the display is assigned. * @param idProducer Produces the logical display id. * @param position Indicates the position this display is facing in this layout. * @param leadDisplayId Display that this one follows (-1 if none). * @param leadDisplayAddress Address of a display that this one follows ({@code null} if none). * @param brightnessThrottlingMapId Name of which brightness throttling policy should be used. * @param refreshRateZoneId Layout limited refresh rate zone name. * @param refreshRateThermalThrottlingMapId Name of which refresh rate throttling * policy should be used. * * @exception IllegalArgumentException When a default display owns a display group other than * DEFAULT_DISPLAY_GROUP. */ public void createDisplayLocked( @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled, String displayGroupName, DisplayIdProducer idProducer, int position, int leadDisplayId, String brightnessThrottlingMapId, @Nullable String refreshRateZoneId, String displayGroupName, DisplayIdProducer idProducer, int position, @Nullable DisplayAddress leadDisplayAddress, String brightnessThrottlingMapId, @Nullable String refreshRateZoneId, @Nullable String refreshRateThermalThrottlingMapId) { if (contains(address)) { Slog.w(TAG, "Attempting to add second definition for display-device: " + address); Loading @@ -115,21 +118,27 @@ public class Layout { return; } // Assign a logical display ID and create the new display. // Note that the logical display ID is saved into the layout, so when switching between // different layouts, a logical display can be destroyed and later recreated with the // same logical display ID. if (displayGroupName == null) { displayGroupName = DEFAULT_DISPLAY_GROUP_NAME; } if (isDefault && !displayGroupName.equals(DEFAULT_DISPLAY_GROUP_NAME)) { throw new IllegalArgumentException("Default display should own DEFAULT_DISPLAY_GROUP"); } if (isDefault && leadDisplayAddress != null) { throw new IllegalArgumentException("Default display cannot have a lead display"); } if (address.equals(leadDisplayAddress)) { throw new IllegalArgumentException("Lead display address cannot be the same as display " + " address"); } // Assign a logical display ID and create the new display. // Note that the logical display ID is saved into the layout, so when switching between // different layouts, a logical display can be destroyed and later recreated with the // same logical display ID. final int logicalDisplayId = idProducer.getId(isDefault); leadDisplayId = isDefault ? NO_LEAD_DISPLAY : leadDisplayId; final Display display = new Display(address, logicalDisplayId, isEnabled, displayGroupName, brightnessThrottlingMapId, position, leadDisplayId, refreshRateZoneId, brightnessThrottlingMapId, position, leadDisplayAddress, refreshRateZoneId, refreshRateThermalThrottlingMapId); mDisplays.add(display); Loading @@ -145,6 +154,43 @@ public class Layout { } } /** * Applies post-processing to displays to make sure the information of each display is * up-to-date. * * <p>At creation of a display, lead display is specified by display address. At post * processing, we convert it to logical display ID. */ public void postProcessLocked() { for (int i = 0; i < mDisplays.size(); i++) { Display display = mDisplays.get(i); if (display.getLogicalDisplayId() == DEFAULT_DISPLAY) { display.setLeadDisplayId(NO_LEAD_DISPLAY); continue; } DisplayAddress leadDisplayAddress = display.getLeadDisplayAddress(); if (leadDisplayAddress == null) { display.setLeadDisplayId(NO_LEAD_DISPLAY); continue; } Display leadDisplay = getByAddress(leadDisplayAddress); if (leadDisplay == null) { throw new IllegalArgumentException("Cannot find a lead display whose address is " + leadDisplayAddress); } if (!TextUtils.equals(display.getDisplayGroupName(), leadDisplay.getDisplayGroupName())) { throw new IllegalArgumentException("Lead display(" + leadDisplay + ") should be in " + "the same display group of the display(" + display + ")"); } if (hasCyclicLeadDisplay(display)) { throw new IllegalArgumentException("Display(" + display + ") has a cyclic lead " + "display"); } display.setLeadDisplayId(leadDisplay.getLogicalDisplayId()); } } /** * @param address The address to check. * Loading Loading @@ -208,6 +254,20 @@ public class Layout { return mDisplays.size(); } private boolean hasCyclicLeadDisplay(Display display) { ArraySet<Display> visited = new ArraySet<>(); while (display != null) { if (visited.contains(display)) { return true; } visited.add(display); DisplayAddress leadDisplayAddress = display.getLeadDisplayAddress(); display = leadDisplayAddress == null ? null : getByAddress(leadDisplayAddress); } return false; } /** * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s. */ Loading Loading @@ -240,8 +300,9 @@ public class Layout { @Nullable private final String mThermalBrightnessThrottlingMapId; // The ID of the lead display that this display will follow in a layout. -1 means no lead. private final int mLeadDisplayId; // The address of the lead display that is specified in display-layout-configuration. @Nullable private final DisplayAddress mLeadDisplayAddress; // Refresh rate zone id for specific layout @Nullable Loading @@ -250,9 +311,13 @@ public class Layout { @Nullable private final String mThermalRefreshRateThrottlingMapId; // The ID of the lead display that this display will follow in a layout. -1 means no lead. // This is determined using {@code mLeadDisplayAddress}. private int mLeadDisplayId; private Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled, @NonNull String displayGroupName, String brightnessThrottlingMapId, int position, int leadDisplayId, @Nullable String refreshRateZoneId, @Nullable DisplayAddress leadDisplayAddress, @Nullable String refreshRateZoneId, @Nullable String refreshRateThermalThrottlingMapId) { mAddress = address; mLogicalDisplayId = logicalDisplayId; Loading @@ -260,9 +325,10 @@ public class Layout { mDisplayGroupName = displayGroupName; mPosition = position; mThermalBrightnessThrottlingMapId = brightnessThrottlingMapId; mLeadDisplayAddress = leadDisplayAddress; mRefreshRateZoneId = refreshRateZoneId; mThermalRefreshRateThrottlingMapId = refreshRateThermalThrottlingMapId; mLeadDisplayId = leadDisplayId; mLeadDisplayId = NO_LEAD_DISPLAY; } @Override Loading @@ -276,6 +342,7 @@ public class Layout { + ", mThermalBrightnessThrottlingMapId: " + mThermalBrightnessThrottlingMapId + ", mRefreshRateZoneId: " + mRefreshRateZoneId + ", mLeadDisplayId: " + mLeadDisplayId + ", mLeadDisplayAddress: " + mLeadDisplayAddress + ", mThermalRefreshRateThrottlingMapId: " + mThermalRefreshRateThrottlingMapId + "}"; } Loading @@ -297,6 +364,7 @@ public class Layout { otherDisplay.mThermalBrightnessThrottlingMapId) && Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId) && this.mLeadDisplayId == otherDisplay.mLeadDisplayId && Objects.equals(mLeadDisplayAddress, otherDisplay.mLeadDisplayAddress) && Objects.equals(mThermalRefreshRateThrottlingMapId, otherDisplay.mThermalRefreshRateThrottlingMapId); } Loading @@ -309,9 +377,10 @@ public class Layout { result = 31 * result + mLogicalDisplayId; result = 31 * result + mDisplayGroupName.hashCode(); result = 31 * result + mAddress.hashCode(); result = 31 * result + mThermalBrightnessThrottlingMapId.hashCode(); result = 31 * result + Objects.hashCode(mThermalBrightnessThrottlingMapId); result = 31 * result + Objects.hashCode(mRefreshRateZoneId); result = 31 * result + mLeadDisplayId; result = 31 * result + Objects.hashCode(mLeadDisplayAddress); result = 31 * result + Objects.hashCode(mThermalRefreshRateThrottlingMapId); return result; } Loading Loading @@ -360,8 +429,20 @@ public class Layout { return mLeadDisplayId; } /** * @return Display address of the display that this one follows. */ @Nullable public DisplayAddress getLeadDisplayAddress() { return mLeadDisplayAddress; } public String getRefreshRateThermalThrottlingMapId() { return mThermalRefreshRateThrottlingMapId; } private void setLeadDisplayId(int id) { mLeadDisplayId = id; } } } services/core/xsd/display-layout-config/display-layout-config.xsd +1 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" /> <xs:element name="leadDisplayAddress" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1" /> </xs:sequence> <xs:attribute name="enabled" type="xs:boolean" use="optional" /> <xs:attribute name="defaultDisplay" type="xs:boolean" use="optional" /> Loading services/core/xsd/display-layout-config/schema/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ package com.android.server.display.config.layout { method public java.math.BigInteger getAddress(); method public String getBrightnessThrottlingMapId(); method public String getDisplayGroup(); method public java.math.BigInteger getLeadDisplayAddress(); method public String getPosition(); method public String getRefreshRateThermalThrottlingMapId(); method public String getRefreshRateZoneId(); Loading @@ -16,6 +17,7 @@ package com.android.server.display.config.layout { method public void setDefaultDisplay(boolean); method public void setDisplayGroup(String); method public void setEnabled(boolean); method public void setLeadDisplayAddress(java.math.BigInteger); method public void setPosition(String); method public void setRefreshRateThermalThrottlingMapId(String); method public void setRefreshRateZoneId(String); Loading services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java +136 −11 Original line number Diff line number Diff line Loading @@ -19,8 +19,8 @@ package com.android.server.display; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import android.view.Display; import android.view.DisplayAddress; import androidx.test.filters.SmallTest; Loading Loading @@ -65,9 +65,15 @@ public class DeviceStateToLayoutMapTest { Layout testLayout = new Layout(); createDefaultDisplay(testLayout, 123456L); createNonDefaultDisplay(testLayout, 78910L, /* enabled= */ false, /* group= */ null); createNonDefaultDisplay(testLayout, 98765L, /* enabled= */ true, /* group= */ "group1"); createNonDefaultDisplay(testLayout, 786L, /* enabled= */ false, /* group= */ "group2"); createNonDefaultDisplay(testLayout, 78910L, /* enabled= */ false, /* group= */ null, /* leadDisplayAddress= */ null); createNonDefaultDisplay(testLayout, 98765L, /* enabled= */ true, /* group= */ "group1", /* leadDisplayAddress= */ null); createNonDefaultDisplay(testLayout, 786L, /* enabled= */ false, /* group= */ "group2", /* leadDisplayAddress= */ null); createNonDefaultDisplay(testLayout, 1092L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(78910L)); testLayout.postProcessLocked(); assertEquals(testLayout, configLayout); } Loading @@ -78,7 +84,9 @@ public class DeviceStateToLayoutMapTest { Layout testLayout = new Layout(); createDefaultDisplay(testLayout, 78910L); createNonDefaultDisplay(testLayout, 123456L, /* enabled= */ false, /* group= */ null); createNonDefaultDisplay(testLayout, 123456L, /* enabled= */ false, /* group= */ null, /* leadDisplayAddress= */ null); testLayout.postProcessLocked(); assertEquals(testLayout, configLayout); } Loading Loading @@ -122,20 +130,132 @@ public class DeviceStateToLayoutMapTest { Layout testLayout = new Layout(); testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(345L), /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null, mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, Display.DEFAULT_DISPLAY, /* brightnessThrottlingMapId= */ "brightness1", mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ "brightness1", /* refreshRateZoneId= */ "zone1", /* refreshRateThermalThrottlingMapId= */ "rr1"); testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(678L), /* isDefault= */ false, /* isEnabled= */ false, /* displayGroupName= */ "group1", mDisplayIdProducerMock, Layout.Display.POSITION_REAR, Display.DEFAULT_DISPLAY, /* brightnessThrottlingMapId= */ "brightness2", mDisplayIdProducerMock, Layout.Display.POSITION_REAR, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ "brightness2", /* refreshRateZoneId= */ "zone2", /* refreshRateThermalThrottlingMapId= */ "rr2"); testLayout.postProcessLocked(); assertEquals(testLayout, configLayout); } @Test public void testLeadDisplayAddress() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 111L, /* enabled= */ true, /* group= */ null, /* leadDisplayAddress= */ null); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(111L)); layout.postProcessLocked(); com.android.server.display.layout.Layout.Display display111 = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(111)); com.android.server.display.layout.Layout.Display display222 = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(222)); assertEquals(display111.getLeadDisplayId(), layout.NO_LEAD_DISPLAY); assertEquals(display222.getLeadDisplayId(), display111.getLogicalDisplayId()); } @Test public void testLeadDisplayAddress_defaultDisplay() { Layout layout = new Layout(); createDefaultDisplay(layout, 123456L); layout.postProcessLocked(); com.android.server.display.layout.Layout.Display display = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(123456)); assertEquals(display.getLeadDisplayId(), layout.NO_LEAD_DISPLAY); } @Test public void testLeadDisplayAddress_noLeadDisplay() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, /* leadDisplayAddress= */ null); layout.postProcessLocked(); com.android.server.display.layout.Layout.Display display = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(222)); assertEquals(display.getLeadDisplayId(), layout.NO_LEAD_DISPLAY); } @Test public void testLeadDisplayAddress_selfLeadDisplayForNonDefaultDisplay() { Layout layout = new Layout(); assertThrows("Expected Layout to throw IllegalArgumentException when the display points out" + " itself as a lead display", IllegalArgumentException.class, () -> layout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(123L), /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null, mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, DisplayAddress.fromPhysicalDisplayId(123L), /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null)); } @Test public void testLeadDisplayAddress_wrongLeadDisplayForDefaultDisplay() { Layout layout = new Layout(); assertThrows("Expected Layout to throw IllegalArgumentException when the default display " + "has a lead display", IllegalArgumentException.class, () -> layout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(123L), /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null, mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, DisplayAddress.fromPhysicalDisplayId(987L), /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null)); } @Test public void testLeadDisplayAddress_notExistingLeadDisplayForNonDefaultDisplay() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(111L)); assertThrows("Expected Layout to throw IllegalArgumentException when a lead display doesn't" + " exist", IllegalArgumentException.class, () -> layout.postProcessLocked()); } @Test public void testLeadDisplayAddress_leadDisplayInDifferentDisplayGroup() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 111, /* enabled= */ true, /* group= */ "group1", /* leadDisplayAddress= */ null); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ "group2", DisplayAddress.fromPhysicalDisplayId(111L)); assertThrows("Expected Layout to throw IllegalArgumentException when pointing to a lead " + "display in the different group", IllegalArgumentException.class, () -> layout.postProcessLocked()); } @Test public void testLeadDisplayAddress_cyclicLeadDisplay() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 111, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(222L)); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(333L)); createNonDefaultDisplay(layout, 333L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(222L)); assertThrows("Expected Layout to throw IllegalArgumentException when pointing to a lead " + "display in the different group", IllegalArgumentException.class, () -> layout.postProcessLocked()); } //////////////////// // Helper Methods // //////////////////// Loading @@ -145,10 +265,11 @@ public class DeviceStateToLayoutMapTest { mDisplayIdProducerMock); } private void createNonDefaultDisplay(Layout layout, long id, boolean enabled, String group) { private void createNonDefaultDisplay(Layout layout, long id, boolean enabled, String group, DisplayAddress leadDisplayAddress) { layout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(id), /* isDefault= */ false, enabled, group, mDisplayIdProducerMock, Layout.Display.POSITION_UNKNOWN, Display.DEFAULT_DISPLAY, /* brightnessThrottlingMapId= */ null, leadDisplayAddress, /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null); } Loading Loading @@ -177,6 +298,10 @@ public class DeviceStateToLayoutMapTest { + "<display enabled=\"false\" displayGroup=\"group2\">\n" + "<address>786</address>\n" + "</display>\n" + "<display enabled=\"true\">\n" + "<address>1092</address>\n" + "<leadDisplayAddress>78910</leadDisplayAddress>\n" + "</display>\n" + "</layout>\n" + "<layout>\n" Loading Loading
services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +7 −3 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ 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; Loading @@ -38,6 +37,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import javax.xml.datatype.DatatypeConfigurationException; Loading Loading @@ -115,13 +115,16 @@ 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); for (com.android.server.display.config.layout.Display d: l.getDisplay()) { assert layout != null; int position = getPosition(d.getPosition()); BigInteger leadDisplayPhysicalId = d.getLeadDisplayAddress(); DisplayAddress leadDisplayAddress = leadDisplayPhysicalId == null ? null : DisplayAddress.fromPhysicalDisplayId( leadDisplayPhysicalId.longValue()); layout.createDisplayLocked( DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()), d.isDefaultDisplay(), Loading @@ -129,11 +132,12 @@ class DeviceStateToLayoutMap { d.getDisplayGroup(), mIdProducer, position, leadDisplayId, leadDisplayAddress, d.getBrightnessThrottlingMapId(), d.getRefreshRateZoneId(), d.getRefreshRateThermalThrottlingMapId()); } layout.postProcessLocked(); } } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { Slog.e(TAG, "Encountered an error while reading/parsing display layout config file: " Loading
services/core/java/com/android/server/display/layout/Layout.java +97 −16 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; import android.view.DisplayAddress; Loading Loading @@ -77,7 +79,7 @@ public class Layout { DisplayIdProducer idProducer) { createDisplayLocked(address, /* isDefault= */ true, /* isEnabled= */ true, DEFAULT_DISPLAY_GROUP_NAME, idProducer, POSITION_UNKNOWN, NO_LEAD_DISPLAY, /* brightnessThrottlingMapId= */ null, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null); } Loading @@ -90,19 +92,20 @@ public class Layout { * @param displayGroupName Name of the display group to which the display is assigned. * @param idProducer Produces the logical display id. * @param position Indicates the position this display is facing in this layout. * @param leadDisplayId Display that this one follows (-1 if none). * @param leadDisplayAddress Address of a display that this one follows ({@code null} if none). * @param brightnessThrottlingMapId Name of which brightness throttling policy should be used. * @param refreshRateZoneId Layout limited refresh rate zone name. * @param refreshRateThermalThrottlingMapId Name of which refresh rate throttling * policy should be used. * * @exception IllegalArgumentException When a default display owns a display group other than * DEFAULT_DISPLAY_GROUP. */ public void createDisplayLocked( @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled, String displayGroupName, DisplayIdProducer idProducer, int position, int leadDisplayId, String brightnessThrottlingMapId, @Nullable String refreshRateZoneId, String displayGroupName, DisplayIdProducer idProducer, int position, @Nullable DisplayAddress leadDisplayAddress, String brightnessThrottlingMapId, @Nullable String refreshRateZoneId, @Nullable String refreshRateThermalThrottlingMapId) { if (contains(address)) { Slog.w(TAG, "Attempting to add second definition for display-device: " + address); Loading @@ -115,21 +118,27 @@ public class Layout { return; } // Assign a logical display ID and create the new display. // Note that the logical display ID is saved into the layout, so when switching between // different layouts, a logical display can be destroyed and later recreated with the // same logical display ID. if (displayGroupName == null) { displayGroupName = DEFAULT_DISPLAY_GROUP_NAME; } if (isDefault && !displayGroupName.equals(DEFAULT_DISPLAY_GROUP_NAME)) { throw new IllegalArgumentException("Default display should own DEFAULT_DISPLAY_GROUP"); } if (isDefault && leadDisplayAddress != null) { throw new IllegalArgumentException("Default display cannot have a lead display"); } if (address.equals(leadDisplayAddress)) { throw new IllegalArgumentException("Lead display address cannot be the same as display " + " address"); } // Assign a logical display ID and create the new display. // Note that the logical display ID is saved into the layout, so when switching between // different layouts, a logical display can be destroyed and later recreated with the // same logical display ID. final int logicalDisplayId = idProducer.getId(isDefault); leadDisplayId = isDefault ? NO_LEAD_DISPLAY : leadDisplayId; final Display display = new Display(address, logicalDisplayId, isEnabled, displayGroupName, brightnessThrottlingMapId, position, leadDisplayId, refreshRateZoneId, brightnessThrottlingMapId, position, leadDisplayAddress, refreshRateZoneId, refreshRateThermalThrottlingMapId); mDisplays.add(display); Loading @@ -145,6 +154,43 @@ public class Layout { } } /** * Applies post-processing to displays to make sure the information of each display is * up-to-date. * * <p>At creation of a display, lead display is specified by display address. At post * processing, we convert it to logical display ID. */ public void postProcessLocked() { for (int i = 0; i < mDisplays.size(); i++) { Display display = mDisplays.get(i); if (display.getLogicalDisplayId() == DEFAULT_DISPLAY) { display.setLeadDisplayId(NO_LEAD_DISPLAY); continue; } DisplayAddress leadDisplayAddress = display.getLeadDisplayAddress(); if (leadDisplayAddress == null) { display.setLeadDisplayId(NO_LEAD_DISPLAY); continue; } Display leadDisplay = getByAddress(leadDisplayAddress); if (leadDisplay == null) { throw new IllegalArgumentException("Cannot find a lead display whose address is " + leadDisplayAddress); } if (!TextUtils.equals(display.getDisplayGroupName(), leadDisplay.getDisplayGroupName())) { throw new IllegalArgumentException("Lead display(" + leadDisplay + ") should be in " + "the same display group of the display(" + display + ")"); } if (hasCyclicLeadDisplay(display)) { throw new IllegalArgumentException("Display(" + display + ") has a cyclic lead " + "display"); } display.setLeadDisplayId(leadDisplay.getLogicalDisplayId()); } } /** * @param address The address to check. * Loading Loading @@ -208,6 +254,20 @@ public class Layout { return mDisplays.size(); } private boolean hasCyclicLeadDisplay(Display display) { ArraySet<Display> visited = new ArraySet<>(); while (display != null) { if (visited.contains(display)) { return true; } visited.add(display); DisplayAddress leadDisplayAddress = display.getLeadDisplayAddress(); display = leadDisplayAddress == null ? null : getByAddress(leadDisplayAddress); } return false; } /** * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s. */ Loading Loading @@ -240,8 +300,9 @@ public class Layout { @Nullable private final String mThermalBrightnessThrottlingMapId; // The ID of the lead display that this display will follow in a layout. -1 means no lead. private final int mLeadDisplayId; // The address of the lead display that is specified in display-layout-configuration. @Nullable private final DisplayAddress mLeadDisplayAddress; // Refresh rate zone id for specific layout @Nullable Loading @@ -250,9 +311,13 @@ public class Layout { @Nullable private final String mThermalRefreshRateThrottlingMapId; // The ID of the lead display that this display will follow in a layout. -1 means no lead. // This is determined using {@code mLeadDisplayAddress}. private int mLeadDisplayId; private Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled, @NonNull String displayGroupName, String brightnessThrottlingMapId, int position, int leadDisplayId, @Nullable String refreshRateZoneId, @Nullable DisplayAddress leadDisplayAddress, @Nullable String refreshRateZoneId, @Nullable String refreshRateThermalThrottlingMapId) { mAddress = address; mLogicalDisplayId = logicalDisplayId; Loading @@ -260,9 +325,10 @@ public class Layout { mDisplayGroupName = displayGroupName; mPosition = position; mThermalBrightnessThrottlingMapId = brightnessThrottlingMapId; mLeadDisplayAddress = leadDisplayAddress; mRefreshRateZoneId = refreshRateZoneId; mThermalRefreshRateThrottlingMapId = refreshRateThermalThrottlingMapId; mLeadDisplayId = leadDisplayId; mLeadDisplayId = NO_LEAD_DISPLAY; } @Override Loading @@ -276,6 +342,7 @@ public class Layout { + ", mThermalBrightnessThrottlingMapId: " + mThermalBrightnessThrottlingMapId + ", mRefreshRateZoneId: " + mRefreshRateZoneId + ", mLeadDisplayId: " + mLeadDisplayId + ", mLeadDisplayAddress: " + mLeadDisplayAddress + ", mThermalRefreshRateThrottlingMapId: " + mThermalRefreshRateThrottlingMapId + "}"; } Loading @@ -297,6 +364,7 @@ public class Layout { otherDisplay.mThermalBrightnessThrottlingMapId) && Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId) && this.mLeadDisplayId == otherDisplay.mLeadDisplayId && Objects.equals(mLeadDisplayAddress, otherDisplay.mLeadDisplayAddress) && Objects.equals(mThermalRefreshRateThrottlingMapId, otherDisplay.mThermalRefreshRateThrottlingMapId); } Loading @@ -309,9 +377,10 @@ public class Layout { result = 31 * result + mLogicalDisplayId; result = 31 * result + mDisplayGroupName.hashCode(); result = 31 * result + mAddress.hashCode(); result = 31 * result + mThermalBrightnessThrottlingMapId.hashCode(); result = 31 * result + Objects.hashCode(mThermalBrightnessThrottlingMapId); result = 31 * result + Objects.hashCode(mRefreshRateZoneId); result = 31 * result + mLeadDisplayId; result = 31 * result + Objects.hashCode(mLeadDisplayAddress); result = 31 * result + Objects.hashCode(mThermalRefreshRateThrottlingMapId); return result; } Loading Loading @@ -360,8 +429,20 @@ public class Layout { return mLeadDisplayId; } /** * @return Display address of the display that this one follows. */ @Nullable public DisplayAddress getLeadDisplayAddress() { return mLeadDisplayAddress; } public String getRefreshRateThermalThrottlingMapId() { return mThermalRefreshRateThrottlingMapId; } private void setLeadDisplayId(int id) { mLeadDisplayId = id; } } }
services/core/xsd/display-layout-config/display-layout-config.xsd +1 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" /> <xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" /> <xs:element name="leadDisplayAddress" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1" /> </xs:sequence> <xs:attribute name="enabled" type="xs:boolean" use="optional" /> <xs:attribute name="defaultDisplay" type="xs:boolean" use="optional" /> Loading
services/core/xsd/display-layout-config/schema/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ package com.android.server.display.config.layout { method public java.math.BigInteger getAddress(); method public String getBrightnessThrottlingMapId(); method public String getDisplayGroup(); method public java.math.BigInteger getLeadDisplayAddress(); method public String getPosition(); method public String getRefreshRateThermalThrottlingMapId(); method public String getRefreshRateZoneId(); Loading @@ -16,6 +17,7 @@ package com.android.server.display.config.layout { method public void setDefaultDisplay(boolean); method public void setDisplayGroup(String); method public void setEnabled(boolean); method public void setLeadDisplayAddress(java.math.BigInteger); method public void setPosition(String); method public void setRefreshRateThermalThrottlingMapId(String); method public void setRefreshRateZoneId(String); Loading
services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java +136 −11 Original line number Diff line number Diff line Loading @@ -19,8 +19,8 @@ package com.android.server.display; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import android.view.Display; import android.view.DisplayAddress; import androidx.test.filters.SmallTest; Loading Loading @@ -65,9 +65,15 @@ public class DeviceStateToLayoutMapTest { Layout testLayout = new Layout(); createDefaultDisplay(testLayout, 123456L); createNonDefaultDisplay(testLayout, 78910L, /* enabled= */ false, /* group= */ null); createNonDefaultDisplay(testLayout, 98765L, /* enabled= */ true, /* group= */ "group1"); createNonDefaultDisplay(testLayout, 786L, /* enabled= */ false, /* group= */ "group2"); createNonDefaultDisplay(testLayout, 78910L, /* enabled= */ false, /* group= */ null, /* leadDisplayAddress= */ null); createNonDefaultDisplay(testLayout, 98765L, /* enabled= */ true, /* group= */ "group1", /* leadDisplayAddress= */ null); createNonDefaultDisplay(testLayout, 786L, /* enabled= */ false, /* group= */ "group2", /* leadDisplayAddress= */ null); createNonDefaultDisplay(testLayout, 1092L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(78910L)); testLayout.postProcessLocked(); assertEquals(testLayout, configLayout); } Loading @@ -78,7 +84,9 @@ public class DeviceStateToLayoutMapTest { Layout testLayout = new Layout(); createDefaultDisplay(testLayout, 78910L); createNonDefaultDisplay(testLayout, 123456L, /* enabled= */ false, /* group= */ null); createNonDefaultDisplay(testLayout, 123456L, /* enabled= */ false, /* group= */ null, /* leadDisplayAddress= */ null); testLayout.postProcessLocked(); assertEquals(testLayout, configLayout); } Loading Loading @@ -122,20 +130,132 @@ public class DeviceStateToLayoutMapTest { Layout testLayout = new Layout(); testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(345L), /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null, mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, Display.DEFAULT_DISPLAY, /* brightnessThrottlingMapId= */ "brightness1", mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ "brightness1", /* refreshRateZoneId= */ "zone1", /* refreshRateThermalThrottlingMapId= */ "rr1"); testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(678L), /* isDefault= */ false, /* isEnabled= */ false, /* displayGroupName= */ "group1", mDisplayIdProducerMock, Layout.Display.POSITION_REAR, Display.DEFAULT_DISPLAY, /* brightnessThrottlingMapId= */ "brightness2", mDisplayIdProducerMock, Layout.Display.POSITION_REAR, /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ "brightness2", /* refreshRateZoneId= */ "zone2", /* refreshRateThermalThrottlingMapId= */ "rr2"); testLayout.postProcessLocked(); assertEquals(testLayout, configLayout); } @Test public void testLeadDisplayAddress() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 111L, /* enabled= */ true, /* group= */ null, /* leadDisplayAddress= */ null); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(111L)); layout.postProcessLocked(); com.android.server.display.layout.Layout.Display display111 = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(111)); com.android.server.display.layout.Layout.Display display222 = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(222)); assertEquals(display111.getLeadDisplayId(), layout.NO_LEAD_DISPLAY); assertEquals(display222.getLeadDisplayId(), display111.getLogicalDisplayId()); } @Test public void testLeadDisplayAddress_defaultDisplay() { Layout layout = new Layout(); createDefaultDisplay(layout, 123456L); layout.postProcessLocked(); com.android.server.display.layout.Layout.Display display = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(123456)); assertEquals(display.getLeadDisplayId(), layout.NO_LEAD_DISPLAY); } @Test public void testLeadDisplayAddress_noLeadDisplay() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, /* leadDisplayAddress= */ null); layout.postProcessLocked(); com.android.server.display.layout.Layout.Display display = layout.getByAddress(DisplayAddress.fromPhysicalDisplayId(222)); assertEquals(display.getLeadDisplayId(), layout.NO_LEAD_DISPLAY); } @Test public void testLeadDisplayAddress_selfLeadDisplayForNonDefaultDisplay() { Layout layout = new Layout(); assertThrows("Expected Layout to throw IllegalArgumentException when the display points out" + " itself as a lead display", IllegalArgumentException.class, () -> layout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(123L), /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null, mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, DisplayAddress.fromPhysicalDisplayId(123L), /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null)); } @Test public void testLeadDisplayAddress_wrongLeadDisplayForDefaultDisplay() { Layout layout = new Layout(); assertThrows("Expected Layout to throw IllegalArgumentException when the default display " + "has a lead display", IllegalArgumentException.class, () -> layout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(123L), /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null, mDisplayIdProducerMock, Layout.Display.POSITION_FRONT, DisplayAddress.fromPhysicalDisplayId(987L), /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null)); } @Test public void testLeadDisplayAddress_notExistingLeadDisplayForNonDefaultDisplay() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(111L)); assertThrows("Expected Layout to throw IllegalArgumentException when a lead display doesn't" + " exist", IllegalArgumentException.class, () -> layout.postProcessLocked()); } @Test public void testLeadDisplayAddress_leadDisplayInDifferentDisplayGroup() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 111, /* enabled= */ true, /* group= */ "group1", /* leadDisplayAddress= */ null); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ "group2", DisplayAddress.fromPhysicalDisplayId(111L)); assertThrows("Expected Layout to throw IllegalArgumentException when pointing to a lead " + "display in the different group", IllegalArgumentException.class, () -> layout.postProcessLocked()); } @Test public void testLeadDisplayAddress_cyclicLeadDisplay() { Layout layout = new Layout(); createNonDefaultDisplay(layout, 111, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(222L)); createNonDefaultDisplay(layout, 222L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(333L)); createNonDefaultDisplay(layout, 333L, /* enabled= */ true, /* group= */ null, DisplayAddress.fromPhysicalDisplayId(222L)); assertThrows("Expected Layout to throw IllegalArgumentException when pointing to a lead " + "display in the different group", IllegalArgumentException.class, () -> layout.postProcessLocked()); } //////////////////// // Helper Methods // //////////////////// Loading @@ -145,10 +265,11 @@ public class DeviceStateToLayoutMapTest { mDisplayIdProducerMock); } private void createNonDefaultDisplay(Layout layout, long id, boolean enabled, String group) { private void createNonDefaultDisplay(Layout layout, long id, boolean enabled, String group, DisplayAddress leadDisplayAddress) { layout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(id), /* isDefault= */ false, enabled, group, mDisplayIdProducerMock, Layout.Display.POSITION_UNKNOWN, Display.DEFAULT_DISPLAY, /* brightnessThrottlingMapId= */ null, leadDisplayAddress, /* brightnessThrottlingMapId= */ null, /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null); } Loading Loading @@ -177,6 +298,10 @@ public class DeviceStateToLayoutMapTest { + "<display enabled=\"false\" displayGroup=\"group2\">\n" + "<address>786</address>\n" + "</display>\n" + "<display enabled=\"true\">\n" + "<address>1092</address>\n" + "<leadDisplayAddress>78910</leadDisplayAddress>\n" + "</display>\n" + "</layout>\n" + "<layout>\n" Loading