diff options
7 files changed, 340 insertions, 134 deletions
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java new file mode 100644 index 000000000000..d4556ed5f9fa --- /dev/null +++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java @@ -0,0 +1,121 @@ +/* + * 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 android.content.Context; +import android.hardware.devicestate.DeviceStateManager; +import android.text.TextUtils; +import android.util.IndentingPrintWriter; +import android.util.Slog; +import android.util.SparseArray; +import android.view.DisplayAddress; + +import com.android.server.display.layout.Layout; + +import java.util.Arrays; + +/** + * Mapping from device states into {@link Layout}s. This allows us to map device + * states into specific layouts for the connected displays; particularly useful for + * foldable and multi-display devices where the default display and which displays are ON can + * change depending on the state of the device. + */ +class DeviceStateToLayoutMap { + private static final String TAG = "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) { + mLayoutMap.append(STATE_DEFAULT, new Layout()); + loadFoldedDisplayConfig(context); + } + + public void dumpLocked(IndentingPrintWriter ipw) { + ipw.println("DeviceStateToLayoutMap:"); + ipw.increaseIndent(); + + ipw.println("Registered Layouts:"); + for (int i = 0; i < mLayoutMap.size(); i++) { + ipw.println("state(" + mLayoutMap.keyAt(i) + "): " + mLayoutMap.valueAt(i)); + } + } + + Layout get(int state) { + Layout layout = mLayoutMap.get(state); + if (layout == null) { + layout = mLayoutMap.get(STATE_DEFAULT); + } + return layout; + } + + private Layout create(int state) { + if (mLayoutMap.contains(state)) { + Slog.e(TAG, "Attempted to create a second layout for state " + state); + return null; + } + + final Layout layout = new Layout(); + mLayoutMap.append(state, layout); + return layout; + } + + 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) + + "]"); + return; + } + + final long[] displayIds; + try { + displayIds = new long[] { + Long.parseLong(strDisplayIds[0]), + Long.parseLong(strDisplayIds[1]) + }; + } catch (NumberFormatException nfe) { + Slog.w(TAG, "Folded display config non numerical: " + Arrays.toString(strDisplayIds)); + return; + } + + final int[] foldedDeviceStates = context.getResources().getIntArray( + com.android.internal.R.array.config_foldedDeviceStates); + // Only add folded states if folded state config is not empty + if (foldedDeviceStates.length == 0) { + return; + } + + // Create the folded state layout + final Layout foldedLayout = create(STATE_FOLDED); + foldedLayout.createDisplayLocked( + DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/); + + // Create the unfolded state layout + final Layout unfoldedLayout = create(STATE_UNFOLDED); + unfoldedLayout.createDisplayLocked( + DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/); + } +} diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 5c0fceb9b795..57f44864d2c0 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.annotation.NonNull; import android.util.Slog; import android.view.Display; +import android.view.DisplayAddress; import com.android.internal.annotations.GuardedBy; import com.android.server.display.DisplayManagerService.SyncRoot; @@ -112,12 +113,11 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { } } - public DisplayDevice getByIdLocked(@NonNull String uniqueId) { - final int count = mDisplayDevices.size(); - for (int i = 0; i < count; i++) { - final DisplayDevice d = mDisplayDevices.get(i); - if (uniqueId.equals(d.getUniqueId())) { - return d; + public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) { + for (int i = mDisplayDevices.size() - 1; i >= 0; i--) { + final DisplayDevice device = mDisplayDevices.get(i); + if (address.equals(device.getDisplayDeviceInfoLocked().address)) { + return device; } } return null; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 01fee5645475..aaf085a1923d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1188,6 +1188,7 @@ public final class DisplayManagerService extends SystemService { final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device); final int state; final int displayId = display.getDisplayIdLocked(); + if (display.isEnabled()) { state = mDisplayStates.get(displayId); } else { @@ -1564,12 +1565,6 @@ public final class DisplayManagerService extends SystemService { } } - void setFoldOverride(Boolean isFolded) { - synchronized (mSyncRoot) { - mLogicalDisplayMapper.setFoldOverrideLocked(isFolded); - } - } - private void clearViewportsLocked() { mViewports.clear(); } diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index aaea15a27e01..d1d04966bdec 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -60,8 +60,6 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayModeDirectorLoggingEnabled(false); case "dwb-set-cct": return setAmbientColorTemperatureOverride(); - case "set-fold": - return setFoldOverride(); default: return handleDefaultCommands(cmd); } @@ -92,8 +90,6 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Disable display mode director logging."); pw.println(" dwb-set-cct CCT"); pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable)."); - pw.println(" set-fold [fold|unfold|reset]"); - pw.println(" Simulates the 'fold' state of a device. 'reset' for default behavior."); pw.println(); Intent.printIntentArgsHelp(pw , ""); } @@ -165,26 +161,4 @@ class DisplayManagerShellCommand extends ShellCommand { mService.setAmbientColorTemperatureOverride(cct); return 0; } - - private int setFoldOverride() { - String state = getNextArg(); - if (state == null) { - getErrPrintWriter().println("Error: no parameter specified for set-fold"); - return 1; - } - final Boolean isFolded; - if ("fold".equals(state)) { - isFolded = true; - } else if ("unfold".equals(state)) { - isFolded = false; - } else if ("reset".equals(state)) { - isFolded = null; - } else { - getErrPrintWriter().println("Error: Invalid fold state request: " + state); - return 1; - } - - mService.setFoldOverride(isFolded); - return 0; - } } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 80781d280d5e..20b133ce4d0a 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -32,6 +32,7 @@ import android.view.SurfaceControl; import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Arrays; import java.util.Objects; @@ -718,6 +719,7 @@ final class LogicalDisplay { public void dumpLocked(PrintWriter pw) { pw.println("mDisplayId=" + mDisplayId); pw.println("mLayerStack=" + mLayerStack); + pw.println("mIsEnabled=" + mIsEnabled); pw.println("mHasContent=" + mHasContent); pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}"); pw.println("mRequestedColorMode=" + mRequestedColorMode); @@ -731,4 +733,11 @@ final class LogicalDisplay { pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides)); pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids); } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + dumpLocked(new PrintWriter(sw)); + return sw.toString(); + } } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 16c4b2641433..a054db533e3f 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -27,10 +27,10 @@ import android.view.Display; import android.view.DisplayEventReceiver; import android.view.DisplayInfo; +import com.android.server.display.layout.Layout; import java.io.PrintWriter; import java.util.Arrays; -import java.util.Objects; import java.util.function.Consumer; /** @@ -75,40 +75,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final boolean mSingleDisplayDemoMode; /** - * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay - * when {@link mIsFolded} is set to {@code true}. - */ - private String mDisplayIdToUseWhenFolded; - - /** - * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay - * when {@link mIsFolded} is set to {@code false}. - */ - private String mDisplayIdToUseWhenUnfolded; - - /** Overrides the folded state of the device. For use with ADB commands. */ - private Boolean mIsFoldedOverride; - - /** Saves the last device fold state. */ - private boolean mIsFolded; - - /** * 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>(); - private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; - private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; /** 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; + private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + private Layout mCurrentLayout = null; + private boolean mIsFolded = false; + LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) { mDisplayDeviceRepo = repo; mListener = listener; @@ -118,7 +103,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mFoldedDeviceStates = context.getResources().getIntArray( com.android.internal.R.array.config_foldedDeviceStates); - loadFoldedDisplayConfig(context); + mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(context); } @Override @@ -213,13 +198,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { ipw.increaseIndent(); ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); - ipw.println("mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); + + ipw.println("mCurrentLayout=" + mCurrentLayout); final int logicalDisplayCount = mLogicalDisplays.size(); ipw.println(); ipw.println("Logical Displays: size=" + logicalDisplayCount); - - for (int i = 0; i < logicalDisplayCount; i++) { int displayId = mLogicalDisplays.keyAt(i); LogicalDisplay display = mLogicalDisplays.valueAt(i); @@ -229,6 +213,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { ipw.decreaseIndent(); ipw.println(); } + mDeviceStateToLayoutMap.dumpLocked(ipw); } void setDeviceStateLocked(int state) { @@ -244,79 +229,55 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { void setDeviceFoldedLocked(boolean isFolded) { mIsFolded = isFolded; - if (mIsFoldedOverride != null) { - isFolded = mIsFoldedOverride.booleanValue(); + + // 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); } - if (mDisplayIdToUseWhenFolded == null || mDisplayIdToUseWhenUnfolded == null - || mLogicalDisplays.size() < 2) { - // Do nothing if this behavior is disabled or there are less than two displays. + final Layout layout = mDeviceStateToLayoutMap.get(state); + if (layout == null) { return; } - - final DisplayDevice deviceFolded = - mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenFolded); - final DisplayDevice deviceUnfolded = - mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenUnfolded); - if (deviceFolded == null || deviceUnfolded == null) { - // If the expected devices for folding functionality are not present, return early. + final Layout.Display displayLayout = layout.getById(Display.DEFAULT_DISPLAY); + if (displayLayout == null) { return; } - - // Find the associated LogicalDisplays for the configured "folding" DeviceDisplays. - final LogicalDisplay displayFolded = getLocked(deviceFolded); - final LogicalDisplay displayUnfolded = getLocked(deviceUnfolded); - if (displayFolded == null || displayUnfolded == null) { - // If the expected displays are not present, return early. + final DisplayDevice newDefaultDevice = + mDisplayDeviceRepo.getByAddressLocked(displayLayout.getAddress()); + if (newDefaultDevice == null) { return; } - // Find out which display is currently default and which is disabled. final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY); - final LogicalDisplay disabledDisplay; - if (defaultDisplay == displayFolded) { - disabledDisplay = displayUnfolded; - } else if (defaultDisplay == displayUnfolded) { - disabledDisplay = displayFolded; - } else { - // If neither folded or unfolded displays are currently set to the default display, we - // are in an unknown state and it's best to log the error and bail. - Slog.e(TAG, "Unexpected: when attempting to swap displays, neither of the two" - + " configured displays were set up as the default display. Default: " - + defaultDisplay.getDisplayInfoLocked() + ", ConfiguredDisplays: [ folded=" - + displayFolded.getDisplayInfoLocked() + ", unfolded=" - + displayUnfolded.getDisplayInfoLocked() + " ]"); + mCurrentLayout = layout; + + // If we're already set up accurately, return early + if (defaultDisplay.getPrimaryDisplayDeviceLocked() == newDefaultDevice) { return; } - if (isFolded == (defaultDisplay == displayFolded)) { - // Nothing to do, already in the right state. + // 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; } - - // Everything was checked and we need to swap, lets swap. - displayFolded.swapDisplaysLocked(displayUnfolded); + 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); - disabledDisplay.setEnabled(false); + displayToSwap.setEnabled(false); // Update the world updateLogicalDisplaysLocked(); - - if (DEBUG) { - Slog.d(TAG, "Folded displays: isFolded: " + isFolded + ", defaultDisplay? " - + defaultDisplay.getDisplayInfoLocked()); - } - } - - void setFoldOverrideLocked(Boolean isFolded) { - if (!Objects.equals(isFolded, mIsFoldedOverride)) { - mIsFoldedOverride = isFolded; - setDeviceFoldedLocked(mIsFolded); - } } private void handleDisplayDeviceAddedLocked(DisplayDevice device) { @@ -333,7 +294,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return; } - final int displayId = assignDisplayIdLocked(isDefault); + final int displayId = Layout.assignDisplayIdLocked(isDefault); final int layerStack = assignLayerStackLocked(displayId); final DisplayGroup displayGroup; @@ -356,8 +317,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return; } - mLogicalDisplays.put(displayId, display); + // 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); @@ -375,6 +343,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(), LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED); } + + if (DEBUG) { + Slog.d(TAG, "New Display added: " + display); + } } /** @@ -466,10 +438,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } - private int assignDisplayIdLocked(boolean isDefault) { - return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++; - } - private int assignDisplayGroupIdLocked(boolean isDefault) { return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++; } @@ -480,21 +448,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return displayId; } - private void loadFoldedDisplayConfig(Context context) { - final String[] displayIds = context.getResources().getStringArray( - com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds); - - if (displayIds.length != 2 || TextUtils.isEmpty(displayIds[0]) - || TextUtils.isEmpty(displayIds[1])) { - Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(displayIds) - + "]"); - return; - } - - mDisplayIdToUseWhenFolded = displayIds[0]; - mDisplayIdToUseWhenUnfolded = displayIds[1]; - } - public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); void onDisplayGroupEventLocked(int groupId, int event); diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java new file mode 100644 index 000000000000..18f39e6ade1d --- /dev/null +++ b/services/core/java/com/android/server/display/layout/Layout.java @@ -0,0 +1,154 @@ +/* + * 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.layout; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.annotation.NonNull; +import android.util.Slog; +import android.view.DisplayAddress; + +import java.util.ArrayList; +import java.util.List; + +/** + * Holds a collection of {@link Display}s. A single instance of this class describes + * how to organize one or more DisplayDevices into LogicalDisplays for a particular device + * state. For example, there may be one instance of this class to describe display layout when + * a foldable device is folded, and a second instance for when the device is unfolded. + */ +public class Layout { + private static final String TAG = "Layout"; + private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1; + + private final List<Display> mDisplays = new ArrayList<>(2); + + /** + * @return The default display ID, or a new unique one to use. + */ + public static int assignDisplayIdLocked(boolean isDefault) { + return isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++; + } + + @Override + public String toString() { + return mDisplays.toString(); + } + + /** + * 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. + * @return The new layout. + */ + public Display createDisplayLocked( + @NonNull DisplayAddress address, boolean isDefault) { + if (contains(address)) { + Slog.w(TAG, "Attempting to add second definition for display-device: " + address); + return null; + } + + // See if we're dealing with the "default" display + if (isDefault && getById(DEFAULT_DISPLAY) != null) { + Slog.w(TAG, "Ignoring attempt to add a second default display: " + address); + isDefault = false; + } + + // 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 = assignDisplayIdLocked(isDefault); + final Display layout = new Display(address, logicalDisplayId); + + mDisplays.add(layout); + return layout; + } + + /** + * @param address The address to check. + * + * @return True if the specified address is used in this layout. + */ + public boolean contains(@NonNull DisplayAddress address) { + final int size = mDisplays.size(); + for (int i = 0; i < size; i++) { + if (address.equals(mDisplays.get(i).getAddress())) { + return true; + } + } + return false; + } + + /** + * @param id The display ID to check. + * + * @return The display corresponding to the specified display ID. + */ + public Display getById(int id) { + for (int i = 0; i < mDisplays.size(); i++) { + Display layout = mDisplays.get(i); + if (id == layout.getLogicalDisplayId()) { + return layout; + } + } + return null; + } + + /** + * @param index The index of the display to return. + * + * @return the display at the specified index. + */ + public Display getAt(int index) { + return mDisplays.get(index); + } + + /** + * @return The number of displays defined for this layout. + */ + public int size() { + return mDisplays.size(); + } + + /** + * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s. + */ + public static class Display { + private final DisplayAddress mAddress; + private final int mLogicalDisplayId; + + Display(@NonNull DisplayAddress address, int logicalDisplayId) { + mAddress = address; + mLogicalDisplayId = logicalDisplayId; + } + + @Override + public String toString() { + return "{addr: " + mAddress + ", dispId: " + mLogicalDisplayId + "}"; + } + + public DisplayAddress getAddress() { + return mAddress; + } + + public int getLogicalDisplayId() { + return mLogicalDisplayId; + } + } +} |