diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index d13a97dc6881d..d49cc4438dd44 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -114,6 +114,23 @@ public final class DisplayManager { public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION"; + /** + * Display category: Rear displays. + *

+ * This category can be used to identify complementary internal displays that are facing away + * from the user. + * Certain applications may present to this display. + * Similar to presentation displays. + *

+ * + * @see android.app.Presentation + * @see Display#FLAG_PRESENTATION + * @see #getDisplays(String) + * @hide + */ + public static final String DISPLAY_CATEGORY_REAR = + "android.hardware.display.category.REAR"; + /** * Display category: All displays, including disabled displays. *

@@ -619,11 +636,19 @@ public final class DisplayManager { synchronized (mLock) { try { if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) { - addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI); - addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL); - addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY); - addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL); - addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL); + addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI, + Display.FLAG_PRESENTATION); + addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL, + Display.FLAG_PRESENTATION); + addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY, + Display.FLAG_PRESENTATION); + addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL, + Display.FLAG_PRESENTATION); + addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL, + Display.FLAG_PRESENTATION); + } else if (DISPLAY_CATEGORY_REAR.equals(category)) { + addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL, + Display.FLAG_REAR); } else if (category == null || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) { addAllDisplaysLocked(mTempDisplays, displayIds); @@ -644,15 +669,16 @@ public final class DisplayManager { } } - private void addPresentationDisplaysLocked( - ArrayList displays, int[] displayIds, int matchType) { - for (int i = 0; i < displayIds.length; i++) { - if (displayIds[i] == DEFAULT_DISPLAY) { + private void addDisplaysLocked( + ArrayList displays, int[] displayIds, int matchType, int flagMask) { + for (int displayId : displayIds) { + if (displayId == DEFAULT_DISPLAY) { continue; } - Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/); + + Display display = getOrCreateDisplayLocked(displayId, /* assumeValid= */ true); if (display != null - && (display.getFlags() & Display.FLAG_PRESENTATION) != 0 + && (display.getFlags() & flagMask) == flagMask && display.getType() == matchType) { displays.add(display); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 25863a6babe13..39ea2fdfec9c2 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -359,6 +359,17 @@ public final class Display { */ public static final int FLAG_STEAL_TOP_FOCUS_DISABLED = 1 << 12; + /** + * Display flag: Indicates that the display is a rear display. + *

+ * This flag identifies complementary displays that are facing away from the user. + *

+ * + * @hide + * @see #getFlags() + */ + public static final int FLAG_REAR = 1 << 13; + /** * Display flag: Indicates that the contents of the display should not be scaled * to fit the physical screen dimensions. Used for development only to emulate diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index c2b6bc5a27089..e26c7be134168 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -906,6 +906,9 @@ public final class DisplayInfo implements Parcelable { if ((flags & Display.FLAG_TOUCH_FEEDBACK_DISABLED) != 0) { result.append(", FLAG_TOUCH_FEEDBACK_DISABLED"); } + if ((flags & Display.FLAG_REAR) != 0) { + result.append(", FLAG_REAR_DISPLAY"); + } return result.toString(); } } diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java index 0c164b44c2f1e..a921a546af5f2 100644 --- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java +++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java @@ -118,6 +118,7 @@ class DeviceStateToLayoutMap { 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; Layout.Display display = layout.createDisplayLocked( DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()), d.isDefaultDisplay(), diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index bad4b3c5bc7a7..4bb1f0ebb242d 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -31,6 +31,7 @@ import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; +import com.android.server.display.layout.Layout; import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; @@ -152,6 +153,10 @@ final class LogicalDisplay { // the {@link mIsEnabled} is changing, or the underlying mPrimiaryDisplayDevice is changing. private boolean mIsInTransition; + // Indicates the position of the display, POSITION_UNKNOWN could mean it hasn't been specified, + // or this is a virtual display etc. + private int mPosition = Layout.Display.POSITION_UNKNOWN; + /** * The ID of the brightness throttling data that should be used. This can change e.g. in * concurrent displays mode in which a stricter brightness throttling policy might need to be @@ -170,6 +175,13 @@ final class LogicalDisplay { mBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID; } + public void setPositionLocked(int position) { + mPosition = position; + } + public int getPositionLocked() { + return mPosition; + } + /** * Gets the logical display id of this logical display. * @@ -424,6 +436,11 @@ final class LogicalDisplay { mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners; mBaseDisplayInfo.installOrientation = deviceInfo.installOrientation; mBaseDisplayInfo.displayShape = deviceInfo.displayShape; + + if (mPosition == Layout.Display.POSITION_REAR) { + mBaseDisplayInfo.flags |= Display.FLAG_REAR; + } + mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); } @@ -814,6 +831,7 @@ final class LogicalDisplay { pw.println("mIsEnabled=" + mIsEnabled); pw.println("mIsInTransition=" + mIsInTransition); pw.println("mLayerStack=" + mLayerStack); + pw.println("mPosition=" + mPosition); pw.println("mHasContent=" + mHasContent); pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}"); pw.println("mRequestedColorMode=" + mRequestedColorMode); diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index a2ca1c0f4b30c..a67644bf8a349 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -977,6 +977,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the // right one, if it doesn't exist, create a new one. final int logicalDisplayId = displayLayout.getLogicalDisplayId(); + LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId); if (newDisplay == null) { newDisplay = createNewLogicalDisplayLocked( @@ -989,6 +990,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { newDisplay.swapDisplaysLocked(oldDisplay); } + newDisplay.setPositionLocked(displayLayout.getPosition()); setEnabledLocked(newDisplay, displayLayout.isEnabled()); newDisplay.setBrightnessThrottlingDataIdLocked( displayLayout.getBrightnessThrottlingMapId() == null diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java index 5de9f0b10fab4..01aa97a35c6fd 100644 --- a/services/core/java/com/android/server/display/layout/Layout.java +++ b/services/core/java/com/android/server/display/layout/Layout.java @@ -18,6 +18,8 @@ package com.android.server.display.layout; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN; + import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Slog; @@ -78,6 +80,22 @@ public class Layout { public Display createDisplayLocked( @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled, DisplayIdProducer idProducer, String brightnessThrottlingMapId) { + return createDisplayLocked(address, isDefault, isEnabled, idProducer, + brightnessThrottlingMapId, POSITION_UNKNOWN); + } + + /** + * Creates a simple 1:1 LogicalDisplay mapping for the specified DisplayDevice. + * + * @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 position Indicates the position this display is facing in this layout. + * @return The new layout. + */ + public Display createDisplayLocked( + @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled, + DisplayIdProducer idProducer, String brightnessThrottlingMapId, int position) { if (contains(address)) { Slog.w(TAG, "Attempting to add second definition for display-device: " + address); return null; @@ -95,7 +113,7 @@ public class Layout { // same logical display ID. final int logicalDisplayId = idProducer.getId(isDefault); final Display display = new Display(address, logicalDisplayId, isEnabled, - brightnessThrottlingMapId); + brightnessThrottlingMapId, position); mDisplays.add(display); return display; @@ -204,11 +222,11 @@ public class Layout { private final String mBrightnessThrottlingMapId; Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled, - String brightnessThrottlingMapId) { + String brightnessThrottlingMapId, int position) { mAddress = address; mLogicalDisplayId = logicalDisplayId; mIsEnabled = isEnabled; - mPosition = POSITION_UNKNOWN; + mPosition = position; mBrightnessThrottlingMapId = brightnessThrottlingMapId; } @@ -272,5 +290,9 @@ public class Layout { public String getBrightnessThrottlingMapId() { return mBrightnessThrottlingMapId; } + + public int getPosition() { + return mPosition; + } } } diff --git a/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java b/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java index 7645a3aaf6644..a192913512509 100644 --- a/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java @@ -111,6 +111,14 @@ public class DeviceStateToLayoutMapTest { assertEquals(testLayout, configLayout); } + @Test + public void testRearDisplayLayout() { + Layout configLayout = mDeviceStateToLayoutMap.get(2); + + assertEquals(Layout.Display.POSITION_FRONT, configLayout.getAt(0).getPosition()); + assertEquals(Layout.Display.POSITION_REAR, configLayout.getAt(1).getPosition()); + } + //////////////////// // Helper Methods // //////////////////// diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java index f8cc7b2fbd64d..8a37ed93e4b48 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -19,6 +19,7 @@ package com.android.server.display; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY_GROUP; +import static android.view.Display.FLAG_REAR; import static android.view.Display.TYPE_EXTERNAL; import static android.view.Display.TYPE_INTERNAL; import static android.view.Display.TYPE_VIRTUAL; @@ -30,6 +31,8 @@ import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REM import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY; import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED; import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED; +import static com.android.server.display.layout.Layout.Display.POSITION_REAR; +import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN; import static com.google.common.truth.Truth.assertThat; @@ -778,6 +781,45 @@ public class LogicalDisplayMapperTest { assertFalse(display2.isEnabledLocked()); } + @Test + public void testDisplayFlagRear() { + DisplayDevice device1 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device2 = createDisplayDevice(TYPE_INTERNAL, 600, 800, + FLAG_REAR); + + Layout layout = new Layout(); + layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, + true, true, mIdProducer, /* brightnessThrottlingMapId= */ null); + layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, + false, true, mIdProducer, /* brightnessThrottlingMapId= */ null, + POSITION_REAR); + when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout); + + when(mDeviceStateToLayoutMapSpy.size()).thenReturn(1); + + LogicalDisplay display1 = add(device1); + assertEquals(info(display1).address, info(device1).address); + assertEquals(DEFAULT_DISPLAY, id(display1)); + + LogicalDisplay display2 = add(device2); + assertEquals(info(display2).address, info(device2).address); + // We can only have one default display + assertEquals(DEFAULT_DISPLAY, id(display1)); + + mLogicalDisplayMapper.setDeviceStateLocked(0, false); + advanceTime(1000); + mLogicalDisplayMapper.onBootCompleted(); + advanceTime(1000); + + assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked()); + assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked()); + + assertEquals(POSITION_UNKNOWN, + mLogicalDisplayMapper.getDisplayLocked(device1).getPositionLocked()); + assertEquals(POSITION_REAR, + mLogicalDisplayMapper.getDisplayLocked(device2).getPositionLocked()); + } ///////////////// // Helper Methods