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