Revert "Clean up LogicalDisplayMapper"

Revert submission 13575048-clean_ldm

Reason for revert: http://b/181537022
Reverted Changes:
I69c3478b4:Adjust jumbojack config to fit latest display layo...
I72a4eb667:Clean up LogicalDisplayMapper

Change-Id: I392816dd3870bd1ab969c06e796977a9d39bafda
This commit is contained in:
Victor Chang
2021-03-01 13:21:54 +00:00
parent c7f1d37c19
commit 8085fe75d1
9 changed files with 255 additions and 776 deletions

View File

@@ -810,9 +810,6 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_TRUSTED) != 0) {
result.append(", FLAG_TRUSTED");
}
if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
result.append(", FLAG_OWN_DISPLAY_GROUP");
}
return result.toString();
}
}

View File

@@ -673,15 +673,6 @@
-->
</integer-array>
<!-- The device states (supplied by DeviceStateManager) that should be treated as unfolded by
the display fold controller. Default is empty. -->
<integer-array name="config_unfoldedDeviceStates">
<!-- Example:
<item>3</item>
<item>4</item>
-->
</integer-array>
<!-- Indicate the display area rect for foldable devices in folded state. -->
<string name="config_foldedArea"></string>

View File

@@ -3762,7 +3762,6 @@
<!-- For Foldables -->
<java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="array" name="config_unfoldedDeviceStates" />
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />

View File

@@ -39,6 +39,10 @@ class DeviceStateToLayoutMap {
public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;
// TODO - b/168208162 - Remove these when we check in static definitions for layouts
public static final int STATE_FOLDED = 100;
public static final int STATE_UNFOLDED = 101;
private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
DeviceStateToLayoutMap(Context context) {
@@ -64,7 +68,7 @@ class DeviceStateToLayoutMap {
return layout;
}
private Layout createLayout(int state) {
private Layout create(int state) {
if (mLayoutMap.contains(state)) {
Slog.e(TAG, "Attempted to create a second layout for state " + state);
return null;
@@ -75,12 +79,10 @@ class DeviceStateToLayoutMap {
return layout;
}
/**
* Loads config.xml-specified folded configurations for foldable devices.
*/
private void loadFoldedDisplayConfig(Context context) {
final String[] strDisplayIds = context.getResources().getStringArray(
com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
if (strDisplayIds.length != 2 || TextUtils.isEmpty(strDisplayIds[0])
|| TextUtils.isEmpty(strDisplayIds[1])) {
Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(strDisplayIds)
@@ -101,23 +103,19 @@ class DeviceStateToLayoutMap {
final int[] foldedDeviceStates = context.getResources().getIntArray(
com.android.internal.R.array.config_foldedDeviceStates);
final int[] unfoldedDeviceStates = context.getResources().getIntArray(
com.android.internal.R.array.config_unfoldedDeviceStates);
// Only add folded states if folded state config is not empty
if (foldedDeviceStates.length == 0 || unfoldedDeviceStates.length == 0) {
if (foldedDeviceStates.length == 0) {
return;
}
for (int state : foldedDeviceStates) {
// Create the folded state layout
createLayout(state).createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/);
}
// Create the folded state layout
final Layout foldedLayout = create(STATE_FOLDED);
foldedLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/);
for (int state : unfoldedDeviceStates) {
// Create the unfolded state layout
createLayout(state).createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/);
}
// Create the unfolded state layout
final Layout unfoldedLayout = create(STATE_UNFOLDED);
unfoldedLayout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/);
}
}

View File

@@ -30,8 +30,6 @@ public class DisplayGroup {
private final List<LogicalDisplay> mDisplays = new ArrayList<>();
private final int mGroupId;
private int mChangeCount;
DisplayGroup(int groupId) {
mGroupId = groupId;
}
@@ -47,16 +45,11 @@ public class DisplayGroup {
* @param display the {@link LogicalDisplay} to add to the Group
*/
void addDisplayLocked(LogicalDisplay display) {
if (!containsLocked(display)) {
mChangeCount++;
if (!mDisplays.contains(display)) {
mDisplays.add(display);
}
}
boolean containsLocked(LogicalDisplay display) {
return mDisplays.contains(display);
}
/**
* Removes the provided {@code display} from the Group.
*
@@ -64,7 +57,6 @@ public class DisplayGroup {
* @return {@code true} if the {@code display} was removed; otherwise {@code false}
*/
boolean removeDisplayLocked(LogicalDisplay display) {
mChangeCount++;
return mDisplays.remove(display);
}
@@ -73,11 +65,6 @@ public class DisplayGroup {
return mDisplays.isEmpty();
}
/** Returns a count of the changes made to this display group. */
int getChangeCountLocked() {
return mChangeCount;
}
/** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */
int getSizeLocked() {
return mDisplays.size();

View File

@@ -485,13 +485,13 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
long timeout = SystemClock.uptimeMillis()
+ mInjector.getDefaultDisplayDelayTimeout();
while (mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY) == null
while (mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY) == null
|| mVirtualDisplayAdapter == null) {
long delay = timeout - SystemClock.uptimeMillis();
if (delay <= 0) {
throw new RuntimeException("Timeout waiting for default display "
+ "to be initialized. DefaultDisplay="
+ mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)
+ mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY)
+ ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
}
if (DEBUG) {
@@ -549,7 +549,7 @@ public final class DisplayManagerService extends SystemService {
mSystemReady = true;
// Just in case the top inset changed before the system was ready. At this point, any
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
recordTopInsetLocked(mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY));
updateSettingsLocked();
}
@@ -617,7 +617,7 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
void setDisplayInfoOverrideFromWindowManagerInternal(int displayId, DisplayInfo info) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
@@ -632,7 +632,7 @@ public final class DisplayManagerService extends SystemService {
*/
private void getNonOverrideDisplayInfoInternal(int displayId, DisplayInfo outInfo) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
display.getNonOverrideDisplayInfoLocked(outInfo);
}
@@ -691,8 +691,8 @@ public final class DisplayManagerService extends SystemService {
mDisplayStates.setValueAt(index, state);
mDisplayBrightnesses.setValueAt(index, brightnessState);
runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId)
.getPrimaryDisplayDeviceLocked());
runnable = updateDisplayStateLocked(
mLogicalDisplayMapper.getLocked(displayId).getPrimaryDisplayDeviceLocked());
}
// Setting the display power state can take hundreds of milliseconds
@@ -803,9 +803,9 @@ public final class DisplayManagerService extends SystemService {
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
final DisplayInfo info =
DisplayInfo info =
getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
display.getDisplayInfoLocked(), callingUid);
if (info.hasAccess(callingUid)
@@ -952,7 +952,7 @@ public final class DisplayManagerService extends SystemService {
private void requestColorModeInternal(int displayId, int colorMode) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null &&
display.getRequestedColorModeLocked() != colorMode) {
display.setRequestedColorModeLocked(colorMode);
@@ -989,7 +989,7 @@ public final class DisplayManagerService extends SystemService {
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
if (display != null) {
return display.getDisplayIdLocked();
}
@@ -1203,7 +1203,7 @@ public final class DisplayManagerService extends SystemService {
// TODO - b/170498827 The rules regarding what display state to apply to each
// display will depend on the configuration/mapping of logical displays.
// Clean up LogicalDisplay.isEnabled() mechanism once this is fixed.
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
final int state;
final int displayId = display.getDisplayIdLocked();
@@ -1364,7 +1364,7 @@ public final class DisplayManagerService extends SystemService {
float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
boolean inTraversal) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display == null) {
return;
}
@@ -1406,7 +1406,7 @@ public final class DisplayManagerService extends SystemService {
private void setDisplayOffsetsInternal(int displayId, int x, int y) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display == null) {
return;
}
@@ -1424,7 +1424,7 @@ public final class DisplayManagerService extends SystemService {
private void setDisplayScalingDisabledInternal(int displayId, boolean disable) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display == null) {
return;
}
@@ -1460,7 +1460,7 @@ public final class DisplayManagerService extends SystemService {
@Nullable
private IBinder getDisplayToken(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
if (device != null) {
@@ -1478,7 +1478,7 @@ public final class DisplayManagerService extends SystemService {
if (token == null) {
return null;
}
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getLocked(displayId);
if (logicalDisplay == null) {
return null;
}
@@ -1611,16 +1611,15 @@ public final class DisplayManagerService extends SystemService {
// Find the logical display that the display device is showing.
// Certain displays only ever show their own content.
LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
if (!ownContent) {
if (display != null && !display.hasContentLocked()) {
// If the display does not have any content of its own, then
// automatically mirror the requested logical display contents if possible.
display = mLogicalDisplayMapper.getDisplayLocked(
device.getDisplayIdToMirrorLocked());
display = mLogicalDisplayMapper.getLocked(device.getDisplayIdToMirrorLocked());
}
if (display == null) {
display = mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY);
display = mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY);
}
}
@@ -1897,9 +1896,9 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
DisplayDeviceInfo getDisplayDeviceInfoInternal(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
return displayDevice.getDisplayDeviceInfoLocked();
}
return null;
@@ -1909,9 +1908,9 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
int getDisplayIdToMirrorInternal(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
return displayDevice.getDisplayIdToMirrorLocked();
}
return Display.INVALID_DISPLAY;
@@ -1993,8 +1992,7 @@ public final class DisplayManagerService extends SystemService {
ArraySet<Integer> uids;
synchronized (mSyncRoot) {
int displayId = msg.arg1;
final LogicalDisplay display =
mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
uids = display.getPendingFrameRateOverrideUids();
display.clearPendingFrameRateOverrideUids();
}
@@ -2588,7 +2586,7 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public boolean isMinimalPostProcessingRequested(int displayId) {
synchronized (mSyncRoot) {
return mLogicalDisplayMapper.getDisplayLocked(displayId)
return mLogicalDisplayMapper.getLocked(displayId)
.getRequestedMinimalPostProcessingLocked();
}
}
@@ -2833,7 +2831,7 @@ public final class DisplayManagerService extends SystemService {
@Override
public Point getDisplayPosition(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
if (display != null) {
return display.getDisplayPosition();
}

View File

@@ -17,11 +17,11 @@
package com.android.server.display;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayEventReceiver;
@@ -64,14 +64,12 @@ import java.util.Objects;
*/
final class LogicalDisplay {
private static final String TAG = "LogicalDisplay";
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
// of its content from appearing.
private static final int BLANK_LAYER_STACK = -1;
private static final DisplayInfo EMPTY_DISPLAY_INFO = new DisplayInfo();
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
private final int mDisplayId;
private final int mLayerStack;
@@ -299,7 +297,7 @@ final class LogicalDisplay {
// Check whether logical display has become invalid.
if (!deviceRepo.containsLocked(mPrimaryDisplayDevice)) {
setPrimaryDisplayDeviceLocked(null);
mPrimaryDisplayDevice = null;
return;
}
@@ -686,28 +684,18 @@ final class LogicalDisplay {
* @param targetDisplay The display with which to swap display-devices.
* @return {@code true} if the displays were swapped, {@code false} otherwise.
*/
public void swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) {
final DisplayDevice oldTargetDevice =
targetDisplay.setPrimaryDisplayDeviceLocked(mPrimaryDisplayDevice);
setPrimaryDisplayDeviceLocked(oldTargetDevice);
}
public boolean swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) {
final DisplayDevice targetDevice = targetDisplay.getPrimaryDisplayDeviceLocked();
if (mPrimaryDisplayDevice == null || targetDevice == null) {
Slog.e(TAG, "Missing display device during swap: " + mPrimaryDisplayDevice + " , "
+ targetDevice);
return false;
}
/**
* Sets the primary display device to the specified device.
*
* @param device The new device to set.
* @return The previously set display device.
*/
public DisplayDevice setPrimaryDisplayDeviceLocked(@Nullable DisplayDevice device) {
final DisplayDevice old = mPrimaryDisplayDevice;
mPrimaryDisplayDevice = device;
// Reset all our display info data
mPrimaryDisplayDeviceInfo = null;
mBaseDisplayInfo.copyFrom(EMPTY_DISPLAY_INFO);
mInfo.set(null);
return old;
final DisplayDevice tmpDevice = mPrimaryDisplayDevice;
mPrimaryDisplayDevice = targetDisplay.mPrimaryDisplayDevice;
targetDisplay.mPrimaryDisplayDevice = tmpDevice;
return true;
}
/**
@@ -730,8 +718,8 @@ final class LogicalDisplay {
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mIsEnabled=" + mIsEnabled);
pw.println("mLayerStack=" + mLayerStack);
pw.println("mIsEnabled=" + mIsEnabled);
pw.println("mHasContent=" + mHasContent);
pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}");
pw.println("mRequestedColorMode=" + mRequestedColorMode);

View File

@@ -17,16 +17,13 @@
package com.android.server.display;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import com.android.server.display.layout.Layout;
@@ -77,50 +74,24 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private final boolean mSingleDisplayDemoMode;
/**
* Map of all logical displays indexed by logical display id.
* List of all logical displays indexed by logical display id.
* Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
* TODO: multi-display - Move the aforementioned comment?
*/
private final SparseArray<LogicalDisplay> mLogicalDisplays =
new SparseArray<LogicalDisplay>();
/** Map of all display groups indexed by display group id. */
private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
/** A mapping from logical display id to display group. */
private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
private final DisplayDeviceRepository mDisplayDeviceRepo;
private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
private final Listener mListener;
private final int[] mFoldedDeviceStates;
/**
* Has an entry for every logical display that the rest of the system has been notified about.
* Any entry in here requires us to send a {@link LOGICAL_DISPLAY_EVENT_REMOVED} event when it
* is deleted or {@link LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed.
*/
private final SparseBooleanArray mUpdatedLogicalDisplays = new SparseBooleanArray();
/**
* Keeps track of all the display groups that we already told other people about. IOW, if a
* display group is in this array, then we *must* send change and remove notifications for it
* because other components know about them. Also, what this array stores is a change counter
* for each group, so we know if the group itself has changes since we last sent out a
* notification. See {@link DisplayGroup#getChangeCountLocked}.
*/
private final SparseIntArray mUpdatedDisplayGroups = new SparseIntArray();
/**
* Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
*/
private final SparseIntArray mLogicalDisplaysToUpdate = new SparseIntArray();
/**
* Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
*/
private final SparseIntArray mDisplayGroupsToUpdate = new SparseIntArray();
private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
private Layout mCurrentLayout = null;
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
private boolean mIsFolded = false;
LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
mDisplayDeviceRepo = repo;
@@ -138,23 +109,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
switch (event) {
case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
if (DEBUG) {
Slog.d(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
}
handleDisplayDeviceAddedLocked(device);
break;
case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
if (DEBUG) {
Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
}
updateLogicalDisplaysLocked();
break;
case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
if (DEBUG) {
Slog.d(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
}
updateLogicalDisplaysLocked();
break;
}
@@ -165,11 +127,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mListener.onTraversalRequested();
}
public LogicalDisplay getDisplayLocked(int displayId) {
public LogicalDisplay getLocked(int displayId) {
return mLogicalDisplays.get(displayId);
}
public LogicalDisplay getDisplayLocked(DisplayDevice device) {
public LogicalDisplay getLocked(DisplayDevice device) {
final int count = mLogicalDisplays.size();
for (int i = 0; i < count; i++) {
LogicalDisplay display = mLogicalDisplays.valueAt(i);
@@ -204,25 +166,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
}
public int getDisplayGroupIdFromDisplayIdLocked(int displayId) {
final LogicalDisplay display = getDisplayLocked(displayId);
if (display == null) {
return Display.INVALID_DISPLAY_GROUP;
}
final int size = mDisplayGroups.size();
public DisplayGroup getDisplayGroupLocked(int groupId) {
final int size = mDisplayIdToGroupMap.size();
for (int i = 0; i < size; i++) {
final DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
if (displayGroup.containsLocked(display)) {
return mDisplayGroups.keyAt(i);
final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i);
if (displayGroup.getGroupId() == groupId) {
return displayGroup;
}
}
return Display.INVALID_DISPLAY_GROUP;
}
public DisplayGroup getDisplayGroupLocked(int groupId) {
return mDisplayGroups.get(groupId);
return null;
}
public void dumpLocked(PrintWriter pw) {
@@ -250,333 +203,229 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
void setDeviceStateLocked(int state) {
if (state != mDeviceState) {
resetLayoutLocked();
mDeviceState = state;
applyLayoutLocked();
updateLogicalDisplaysLocked();
boolean folded = false;
for (int i = 0; i < mFoldedDeviceStates.length; i++) {
if (state == mFoldedDeviceStates[i]) {
folded = true;
break;
}
}
setDeviceFoldedLocked(folded);
}
void setDeviceFoldedLocked(boolean isFolded) {
mIsFolded = isFolded;
// Until we have fully functioning state mapping, use hardcoded states based on isFolded
final int state = mIsFolded ? DeviceStateToLayoutMap.STATE_FOLDED
: DeviceStateToLayoutMap.STATE_UNFOLDED;
if (DEBUG) {
Slog.d(TAG, "New device state: " + state);
}
final Layout layout = mDeviceStateToLayoutMap.get(state);
if (layout == null) {
return;
}
final Layout.Display displayLayout = layout.getById(Display.DEFAULT_DISPLAY);
if (displayLayout == null) {
return;
}
final DisplayDevice newDefaultDevice =
mDisplayDeviceRepo.getByAddressLocked(displayLayout.getAddress());
if (newDefaultDevice == null) {
return;
}
final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
mCurrentLayout = layout;
// If we're already set up accurately, return early
if (defaultDisplay.getPrimaryDisplayDeviceLocked() == newDefaultDevice) {
return;
}
// We need to swap the default display's display-device with the one that is supposed
// to be the default in the new layout.
final LogicalDisplay displayToSwap = getLocked(newDefaultDevice);
if (displayToSwap == null) {
Slog.w(TAG, "Canceling display swap - unexpected empty second display for: "
+ newDefaultDevice);
return;
}
defaultDisplay.swapDisplaysLocked(displayToSwap);
// We ensure that the non-default Display is always forced to be off. This was likely
// already done in a previous iteration, but we do it with each swap in case something in
// the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example.
defaultDisplay.setEnabled(true);
displayToSwap.setEnabled(false);
// Update the world
updateLogicalDisplaysLocked();
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
// Internal Displays need to have additional initialization.
// TODO: b/168208162 - This initializes a default dynamic display layout for INTERNAL
// devices, which will eventually just be a fallback in case no static layout definitions
// exist or cannot be loaded.
if (deviceInfo.type == Display.TYPE_INTERNAL) {
initializeInternalDisplayDeviceLocked(device);
boolean isDefault = (deviceInfo.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
isDefault = false;
}
// Create a logical display for the new display device
LogicalDisplay display = createNewLogicalDisplayLocked(
device, Layout.assignDisplayIdLocked(false /*isDefault*/));
if (!isDefault && mSingleDisplayDemoMode) {
Slog.i(TAG, "Not creating a logical display for a secondary display "
+ " because single display demo mode is enabled: " + deviceInfo);
return;
}
applyLayoutLocked();
updateLogicalDisplaysLocked();
final int displayId = Layout.assignDisplayIdLocked(isDefault);
final int layerStack = assignLayerStackLocked(displayId);
final DisplayGroup displayGroup;
final boolean addNewDisplayGroup =
isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0;
if (addNewDisplayGroup) {
final int groupId = assignDisplayGroupIdLocked(isDefault);
displayGroup = new DisplayGroup(groupId);
} else {
displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY);
}
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
display.updateDisplayGroupIdLocked(displayGroup.getGroupId());
display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
// This should never happen currently.
Slog.w(TAG, "Ignoring display device because the logical display "
+ "created from it was not considered valid: " + deviceInfo);
return;
}
// For foldable devices, we start the internal non-default displays as disabled.
// TODO - b/168208162 - this will be removed when we recalculate the layout with each
// display-device addition.
if (mFoldedDeviceStates.length > 0 && deviceInfo.type == Display.TYPE_INTERNAL
&& !isDefault) {
display.setEnabled(false);
}
mLogicalDisplays.put(displayId, display);
displayGroup.addDisplayLocked(display);
mDisplayIdToGroupMap.append(displayId, displayGroup);
if (addNewDisplayGroup) {
// Group added events happen before Logical Display added events.
mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
}
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
if (!addNewDisplayGroup) {
// Group changed events happen after Logical Display added events.
mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
}
if (DEBUG) {
Slog.d(TAG, "New Display added: " + display);
}
}
/**
* Updates the rest of the display system once all the changes are applied for display
* devices and logical displays. The includes releasing invalid/empty LogicalDisplays,
* creating/adjusting/removing DisplayGroups, and notifying the rest of the system of the
* relevant changes.
* Updates all existing logical displays given the current set of display devices.
* Removes invalid logical displays. Sends notifications if needed.
*/
private void updateLogicalDisplaysLocked() {
// Go through all the displays and figure out if they need to be updated.
// Loops in reverse so that displays can be removed during the loop without affecting the
// rest of the loop.
for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
final int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides =
display.getFrameRateOverrides();
display.updateLocked(mDisplayDeviceRepo);
final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
final boolean wasPreviouslyUpdated = mUpdatedLogicalDisplays.get(displayId);
// The display is no longer valid and needs to be removed.
final DisplayGroup changedDisplayGroup;
if (!display.isValidLocked()) {
mUpdatedLogicalDisplays.delete(displayId);
mLogicalDisplays.removeAt(i);
final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId);
displayGroup.removeDisplayLocked(display);
// Remove from group
final DisplayGroup displayGroup = getDisplayGroupLocked(
getDisplayGroupIdFromDisplayIdLocked(displayId));
if (displayGroup != null) {
displayGroup.removeDisplayLocked(display);
}
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
if (wasPreviouslyUpdated) {
// The display isn't actually removed from our internal data structures until
// after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
changedDisplayGroup = displayGroup;
} else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
final int flags = display.getDisplayInfoLocked().flags;
final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get(
Display.DEFAULT_DISPLAY);
if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
// The display should have its own DisplayGroup.
if (defaultDisplayGroup.removeDisplayLocked(display)) {
final int groupId = assignDisplayGroupIdLocked(false);
final DisplayGroup displayGroup = new DisplayGroup(groupId);
displayGroup.addDisplayLocked(display);
display.updateDisplayGroupIdLocked(groupId);
mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup);
mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
changedDisplayGroup = defaultDisplayGroup;
} else {
changedDisplayGroup = null;
}
} else {
// This display never left this class, safe to remove without notification
mLogicalDisplays.removeAt(i);
// The display should be a part of the default DisplayGroup.
final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
if (displayGroup != defaultDisplayGroup) {
displayGroup.removeDisplayLocked(display);
defaultDisplayGroup.addDisplayLocked(display);
display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId());
mListener.onDisplayGroupEventLocked(defaultDisplayGroup.getGroupId(),
LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup);
changedDisplayGroup = displayGroup;
} else {
changedDisplayGroup = null;
}
}
continue;
// The display is new.
} else if (!wasPreviouslyUpdated) {
assignDisplayGroupLocked(display);
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
// Underlying displays device has changed to a different one.
} else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
// FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
assignDisplayGroupLocked(display);
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
// Something about the display device has changed.
} else if (!mTempDisplayInfo.equals(newDisplayInfo)) {
// FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
assignDisplayGroupLocked(display);
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
// Display frame rate overrides changed.
final String oldUniqueId = mTempDisplayInfo.uniqueId;
final String newUniqueId = display.getDisplayInfoLocked().uniqueId;
final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId)
? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED;
mListener.onLogicalDisplayEventLocked(display, eventMsg);
} else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
mLogicalDisplaysToUpdate.put(
displayId, LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
// Non-override display values changed.
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
changedDisplayGroup = null;
} else {
// While application shouldn't know nor care about the non-overridden info, we
// While applications shouldn't know nor care about the non-overridden info, we
// still need to let WindowManager know so it can update its own internal state for
// things like display cutouts.
display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
}
changedDisplayGroup = null;
}
mUpdatedLogicalDisplays.put(displayId, true);
}
// Go through the groups and do the same thing. We do this after displays since group
// information can change in the previous loop.
// Loops in reverse so that groups can be removed during the loop without affecting the
// rest of the loop.
for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
final int groupId = mDisplayGroups.keyAt(i);
final DisplayGroup group = mDisplayGroups.valueAt(i);
final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) < 0;
final int changeCount = group.getChangeCountLocked();
if (group.isEmptyLocked()) {
mUpdatedDisplayGroups.delete(groupId);
if (wasPreviouslyUpdated) {
mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_REMOVED);
}
continue;
} else if (!wasPreviouslyUpdated) {
mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_ADDED);
} else if (mUpdatedDisplayGroups.get(groupId) != changeCount) {
mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_CHANGED);
}
mUpdatedDisplayGroups.put(groupId, changeCount);
}
// Send the display and display group updates in order by message type. This is important
// to ensure that addition and removal notifications happen in the right order.
sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED);
mLogicalDisplaysToUpdate.clear();
mDisplayGroupsToUpdate.clear();
}
/**
* Send the specified message for all relevant displays in the specified display-to-message map.
*/
private void sendUpdatesForDisplaysLocked(int msg) {
for (int i = mLogicalDisplaysToUpdate.size() - 1; i >= 0; --i) {
final int currMsg = mLogicalDisplaysToUpdate.valueAt(i);
if (currMsg != msg) {
continue;
}
final int id = mLogicalDisplaysToUpdate.keyAt(i);
mListener.onLogicalDisplayEventLocked(getDisplayLocked(id), msg);
if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
// We wait until we sent the EVENT_REMOVED event before actually removing the
// display.
mLogicalDisplays.delete(id);
// CHANGED and REMOVED DisplayGroup events should always fire after Display events.
if (changedDisplayGroup != null) {
final int event = changedDisplayGroup.isEmptyLocked()
? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED
: LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED;
mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event);
}
}
}
/**
* Send the specified message for all relevant display groups in the specified message map.
*/
private void sendUpdatesForGroupsLocked(int msg) {
for (int i = mDisplayGroupsToUpdate.size() - 1; i >= 0; --i) {
final int currMsg = mDisplayGroupsToUpdate.valueAt(i);
if (currMsg != msg) {
continue;
}
final int id = mDisplayGroupsToUpdate.keyAt(i);
mListener.onDisplayGroupEventLocked(id, msg);
if (msg == DISPLAY_GROUP_EVENT_REMOVED) {
// We wait until we sent the EVENT_REMOVED event before actually removing the
// group.
mDisplayGroups.delete(id);
}
}
}
private void assignDisplayGroupLocked(LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
// Get current display group data
int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
// Get the new display group if a change is needed
final DisplayInfo info = display.getDisplayInfoLocked();
final boolean needsOwnDisplayGroup = (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0;
final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
if (groupId == Display.INVALID_DISPLAY_GROUP
|| hasOwnDisplayGroup != needsOwnDisplayGroup) {
groupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
}
// Create a new group if needed
DisplayGroup newGroup = getDisplayGroupLocked(groupId);
if (newGroup == null) {
newGroup = new DisplayGroup(groupId);
mDisplayGroups.append(groupId, newGroup);
}
// Add/remove display from the groups.
newGroup.addDisplayLocked(display);
if (oldGroup != null && oldGroup != newGroup) {
oldGroup.removeDisplayLocked(display);
}
}
/**
* Resets the current layout in preparation for a new layout; essentially just marks
* all the currently layed out displays as disabled. This ensures the display devices
* are turned off. If they are meant to be used in the new layout,
* {@link #applyLayoutLocked()} will reenabled them.
*/
private void resetLayoutLocked() {
final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState);
for (int i = layout.size() - 1; i >= 0; i--) {
final Layout.Display displayLayout = layout.getAt(i);
final LogicalDisplay display = getDisplayLocked(displayLayout.getLogicalDisplayId());
if (display != null) {
enableDisplayLocked(display, false);
}
}
}
/**
* Apply (or reapply) the currently selected display layout.
*/
private void applyLayoutLocked() {
final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState);
mCurrentLayout = layout;
Slog.i(TAG, "Applying the display layout for device state(" + mDeviceState
+ "): " + layout);
// Go through each of the displays in the current layout set.
final int size = layout.size();
for (int i = 0; i < size; i++) {
final Layout.Display displayLayout = layout.getAt(i);
// If the underlying display-device we want to use for this display
// doesn't exist, then skip it. This can happen at startup as display-devices
// trickle in one at a time, or if the layout has an error.
final DisplayAddress address = displayLayout.getAddress();
final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
if (device == null) {
Slog.w(TAG, "The display device (" + address + "), is not available"
+ " for the display state " + mDeviceState);
continue;
}
// 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(
null /*displayDevice*/, logicalDisplayId);
}
// Now swap the underlying display devices between the old display and the new display
final LogicalDisplay oldDisplay = getDisplayLocked(device);
if (newDisplay != oldDisplay) {
newDisplay.swapDisplaysLocked(oldDisplay);
}
enableDisplayLocked(newDisplay, true);
}
}
/**
* Creates a new logical display for the specified device and display Id and adds it to the list
* of logical displays.
*
* @param device The device to associate with the LogicalDisplay.
* @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
* @param isDefault Indicates if we are creating the default display.
* @return The new logical display if created, null otherwise.
*/
private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
final int layerStack = assignLayerStackLocked(displayId);
final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
display.updateLocked(mDisplayDeviceRepo);
mLogicalDisplays.put(displayId, display);
// Internal displays start off disabled. The display is enabled later if it is part of the
// currently selected display layout.
final boolean isEnabled = device != null
&& device.getDisplayDeviceInfoLocked().type != Display.TYPE_INTERNAL;
enableDisplayLocked(display, isEnabled);
return display;
}
private void enableDisplayLocked(LogicalDisplay display, boolean isEnabled) {
final int displayId = display.getDisplayIdLocked();
final DisplayInfo info = display.getDisplayInfoLocked();
final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
&& (info.type != Display.TYPE_INTERNAL);
if (isEnabled && disallowSecondaryDisplay) {
Slog.i(TAG, "Not creating a logical display for a secondary display because single"
+ " display demo mode is enabled: " + display.getDisplayInfoLocked());
isEnabled = false;
}
display.setEnabled(isEnabled);
}
private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) {
return isOwnDisplayGroup ? mNextNonDefaultGroupId++ : Display.DEFAULT_DISPLAY_GROUP;
}
private void initializeInternalDisplayDeviceLocked(DisplayDevice device) {
// We always want to make sure that our default display layout creates a logical
// display for every internal display device that is found.
// To that end, when we are notified of a new internal display, we add it to
// the default definition if it is not already there.
final Layout layoutSet = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isDefault = (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
layoutSet.createDisplayLocked(info.address, isDefault);
private int assignDisplayGroupIdLocked(boolean isDefault) {
return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++;
}
private int assignLayerStackLocked(int displayId) {

View File

@@ -1,328 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.display;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.os.Parcel;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class LogicalDisplayMapperTest {
private static int sUniqueTestDisplayId = 0;
private DisplayDeviceRepository mDisplayDeviceRepo;
private LogicalDisplayMapper mLogicalDisplayMapper;
private Context mContext;
@Mock LogicalDisplayMapper.Listener mListenerMock;
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
@Before
public void setUp() {
// Share classloader to allow package private access.
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getContext();
mDisplayDeviceRepo = new DisplayDeviceRepository(
new DisplayManagerService.SyncRoot(),
new PersistentDataStore(new PersistentDataStore.Injector() {
@Override
public InputStream openRead() {
return null;
}
@Override
public OutputStream startWrite() {
return null;
}
@Override
public void finishWrite(OutputStream os, boolean success) {}
}));
// Disable binder caches in this process.
PropertyInvalidatedCache.disableForTestMode();
mLogicalDisplayMapper = new LogicalDisplayMapper(
mContext, mDisplayDeviceRepo, mListenerMock);
}
/////////////////
// Test Methods
/////////////////
@Test
public void testDisplayDeviceAddAndRemove_Internal() {
DisplayDevice device = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
// add
LogicalDisplay displayAdded = add(device);
assertEquals(info(displayAdded).address, info(device).address);
assertEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
// remove
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
verify(mListenerMock).onLogicalDisplayEventLocked(
mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
assertEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
assertEquals(displayAdded, displayRemoved);
}
@Test
public void testDisplayDeviceAddAndRemove_NonInternalTypes() {
testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_EXTERNAL);
testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_WIFI);
testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_OVERLAY);
testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_VIRTUAL);
testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_UNKNOWN);
// Call the internal test again, just to verify that adding non-internal displays
// doesn't affect the ability for an internal display to become the default display.
testDisplayDeviceAddAndRemove_Internal();
}
@Test
public void testDisplayDeviceAdd_TwoInternalOneDefault() {
DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0);
DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
LogicalDisplay display1 = add(device1);
assertEquals(info(display1).address, info(device1).address);
assertNotEquals(Display.DEFAULT_DISPLAY, id(display1));
LogicalDisplay display2 = add(device2);
assertEquals(info(display2).address, info(device2).address);
assertEquals(Display.DEFAULT_DISPLAY, id(display2));
}
@Test
public void testDisplayDeviceAdd_TwoInternalBothDefault() {
DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
LogicalDisplay display1 = add(device1);
assertEquals(info(display1).address, info(device1).address);
assertEquals(Display.DEFAULT_DISPLAY, id(display1));
LogicalDisplay display2 = add(device2);
assertEquals(info(display2).address, info(device2).address);
// Despite the flags, we can only have one default display
assertNotEquals(Display.DEFAULT_DISPLAY, id(display2));
}
@Test
public void testGetDisplayIdsLocked() {
add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0));
add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
assertEquals(3, ids.length);
Arrays.sort(ids);
assertEquals(Display.DEFAULT_DISPLAY, ids[0]);
}
@Test
public void testSingleDisplayGroup() {
LogicalDisplay display1 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@Test
public void testMultipleDisplayGroups() {
LogicalDisplay display1 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
TestDisplayDevice device3 = createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800,
DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
LogicalDisplay display3 = add(device3);
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
assertNotEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
// Now switch it back to the default group by removing the flag and issuing an update
DisplayDeviceInfo info = device3.getSourceInfo();
info.flags = info.flags & ~DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
// Verify the new group is correct.
assertEquals(Display.DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
/////////////////
// Helper Methods
/////////////////
private TestDisplayDevice createDisplayDevice(int type, int width, int height, int flags) {
return createDisplayDevice(new DisplayAddressImpl(), type, width, height, flags);
}
private TestDisplayDevice createDisplayDevice(
DisplayAddress address, int type, int width, int height, int flags) {
TestDisplayDevice device = new TestDisplayDevice();
DisplayDeviceInfo displayDeviceInfo = device.getSourceInfo();
displayDeviceInfo.type = type;
displayDeviceInfo.width = width;
displayDeviceInfo.height = height;
displayDeviceInfo.flags = flags;
displayDeviceInfo.address = new DisplayAddressImpl();
return device;
}
private DisplayDeviceInfo info(DisplayDevice device) {
return device.getDisplayDeviceInfoLocked();
}
private DisplayInfo info(LogicalDisplay display) {
return display.getDisplayInfoLocked();
}
private int id(LogicalDisplay display) {
return display.getDisplayIdLocked();
}
private LogicalDisplay add(DisplayDevice device) {
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_ADDED);
ArgumentCaptor<LogicalDisplay> displayCaptor =
ArgumentCaptor.forClass(LogicalDisplay.class);
verify(mListenerMock).onLogicalDisplayEventLocked(
displayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_ADDED));
clearInvocations(mListenerMock);
return displayCaptor.getValue();
}
private void testDisplayDeviceAddAndRemove_NonInternal(int type) {
DisplayDevice device = createDisplayDevice(type, 600, 800, 0);
// add
LogicalDisplay displayAdded = add(device);
assertEquals(info(displayAdded).address, info(device).address);
assertNotEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
// remove
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
verify(mListenerMock).onLogicalDisplayEventLocked(
mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
assertNotEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
}
/**
* Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
* display-address implementation in our code. Intentionally uses default object (reference)
* equality rules.
*/
class DisplayAddressImpl extends DisplayAddress {
@Override
public void writeToParcel(Parcel out, int flags) { }
}
class TestDisplayDevice extends DisplayDevice {
private DisplayDeviceInfo mInfo = new DisplayDeviceInfo();
private DisplayDeviceInfo mSentInfo;
TestDisplayDevice() {
super(null, null, "test_display_" + sUniqueTestDisplayId++, mContext);
mInfo = new DisplayDeviceInfo();
}
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mSentInfo == null) {
mSentInfo = new DisplayDeviceInfo();
mSentInfo.copyFrom(mInfo);
}
return mSentInfo;
}
@Override
public void applyPendingDisplayDeviceInfoChangesLocked() {
mSentInfo = null;
}
@Override
public boolean hasStableUniqueId() {
return true;
}
public DisplayDeviceInfo getSourceInfo() {
return mInfo;
}
}
}