LogicalDisplay.java revision 63f1c43fbef157397869475ef30d23e631b88bbe
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.display;
18
19import android.graphics.Rect;
20import android.view.DisplayInfo;
21import android.view.Surface;
22
23import java.io.PrintWriter;
24import java.util.List;
25
26import libcore.util.Objects;
27
28/**
29 * Describes how a logical display is configured.
30 * <p>
31 * At this time, we only support logical displays that are coupled to a particular
32 * primary display device from which the logical display derives its basic properties
33 * such as its size, density and refresh rate.
34 * </p><p>
35 * A logical display may be mirrored onto multiple display devices in addition to its
36 * primary display device.  Note that the contents of a logical display may not
37 * always be visible, even on its primary display device, such as in the case where
38 * the primary display device is currently mirroring content from a different
39 * logical display.
40 * </p><p>
41 * This object is designed to encapsulate as much of the policy of logical
42 * displays as possible.  The idea is to make it easy to implement new kinds of
43 * logical displays mostly by making local changes to this class.
44 * </p><p>
45 * Note: The display manager architecture does not actually require logical displays
46 * to be associated with any individual display device.  Logical displays and
47 * display devices are orthogonal concepts.  Some mapping will exist between
48 * logical displays and display devices but it can be many-to-many and
49 * and some might have no relation at all.
50 * </p><p>
51 * Logical displays are guarded by the {@link DisplayManagerService.SyncRoot} lock.
52 * </p>
53 */
54final class LogicalDisplay {
55    private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
56
57    private final int mLayerStack;
58    private DisplayInfo mOverrideDisplayInfo; // set by the window manager
59    private DisplayInfo mInfo;
60
61    // The display device that this logical display is based on and which
62    // determines the base metrics that it uses.
63    private DisplayDevice mPrimaryDisplayDevice;
64    private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
65
66    // Temporary rectangle used when needed.
67    private final Rect mTempLayerStackRect = new Rect();
68    private final Rect mTempDisplayRect = new Rect();
69
70    public LogicalDisplay(int layerStack, DisplayDevice primaryDisplayDevice) {
71        mLayerStack = layerStack;
72        mPrimaryDisplayDevice = primaryDisplayDevice;
73    }
74
75    /**
76     * Gets the primary display device associated with this logical display.
77     *
78     * @return The primary display device.
79     */
80    public DisplayDevice getPrimaryDisplayDeviceLocked() {
81        return mPrimaryDisplayDevice;
82    }
83
84    /**
85     * Gets information about the logical display.
86     *
87     * @return The device info, which should be treated as immutable by the caller.
88     * The logical display should allocate a new display info object whenever
89     * the data changes.
90     */
91    public DisplayInfo getDisplayInfoLocked() {
92        if (mInfo == null) {
93            mInfo = new DisplayInfo();
94            if (mOverrideDisplayInfo != null) {
95                mInfo.copyFrom(mOverrideDisplayInfo);
96                mInfo.layerStack = mBaseDisplayInfo.layerStack;
97                mInfo.name = mBaseDisplayInfo.name;
98            } else {
99                mInfo.copyFrom(mBaseDisplayInfo);
100            }
101        }
102        return mInfo;
103    }
104
105    /**
106     * Sets overridden logical display information from the window manager.
107     * This method can be used to adjust application insets, rotation, and other
108     * properties that the window manager takes care of.
109     *
110     * @param info The logical display information, may be null.
111     */
112    public void setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
113        if (info != null) {
114            if (mOverrideDisplayInfo == null) {
115                mOverrideDisplayInfo = new DisplayInfo(info);
116                mInfo = null;
117            } else if (!mOverrideDisplayInfo.equals(info)) {
118                mOverrideDisplayInfo.copyFrom(info);
119                mInfo = null;
120            }
121        } else if (mOverrideDisplayInfo != null) {
122            mOverrideDisplayInfo = null;
123            mInfo = null;
124        }
125    }
126
127    /**
128     * Returns true if the logical display is in a valid state.
129     * This method should be checked after calling {@link #update} to handle the
130     * case where a logical display should be removed because all of its associated
131     * display devices are gone or if it is otherwise no longer needed.
132     *
133     * @return True if the logical display is still valid.
134     */
135    public boolean isValidLocked() {
136        return mPrimaryDisplayDevice != null;
137    }
138
139    /**
140     * Updates the state of the logical display based on the available display devices.
141     * The logical display might become invalid if it is attached to a display device
142     * that no longer exists.
143     *
144     * @param devices The list of all connected display devices.
145     */
146    public void updateLocked(List<DisplayDevice> devices) {
147        // Nothing to update if already invalid.
148        if (mPrimaryDisplayDevice == null) {
149            return;
150        }
151
152        // Check whether logical display has become invalid.
153        if (!devices.contains(mPrimaryDisplayDevice)) {
154            mPrimaryDisplayDevice = null;
155            return;
156        }
157
158        // Bootstrap the logical display using its associated primary physical display.
159        // We might use more elaborate configurations later.  It's possible that the
160        // configuration of several physical displays might be used to determine the
161        // logical display that they are sharing.  (eg. Adjust size for pixel-perfect
162        // mirroring over HDMI.)
163        DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked();
164        if (!Objects.equal(mPrimaryDisplayDeviceInfo, deviceInfo)) {
165            mBaseDisplayInfo.layerStack = mLayerStack;
166            mBaseDisplayInfo.name = deviceInfo.name;
167            mBaseDisplayInfo.appWidth = deviceInfo.width;
168            mBaseDisplayInfo.appHeight = deviceInfo.height;
169            mBaseDisplayInfo.logicalWidth = deviceInfo.width;
170            mBaseDisplayInfo.logicalHeight = deviceInfo.height;
171            mBaseDisplayInfo.rotation = Surface.ROTATION_0;
172            mBaseDisplayInfo.refreshRate = deviceInfo.refreshRate;
173            mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
174            mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
175            mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
176            mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
177            mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
178            mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
179            mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
180
181            mPrimaryDisplayDeviceInfo = deviceInfo;
182            mInfo = null;
183        }
184    }
185
186    /**
187     * Applies the layer stack and transformation to the given display device
188     * so that it shows the contents of this logical display.
189     *
190     * We know that the given display device is only ever showing the contents of
191     * a single logical display, so this method is expected to blow away all of its
192     * transformation properties to make it happen regardless of what the
193     * display device was previously showing.
194     *
195     * The caller must have an open Surface transaction.
196     *
197     * The display device may not be the primary display device, in the case
198     * where the display is being mirrored.
199     *
200     * @param device The display device to modify.
201     */
202    public void configureDisplayInTransactionLocked(DisplayDevice device) {
203        final DisplayInfo displayInfo = getDisplayInfoLocked();
204        final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
205
206        // Set the layer stack.
207        device.setLayerStackInTransactionLocked(mLayerStack);
208
209        // Set the viewport.
210        // This is the area of the logical display that we intend to show on the
211        // display device.  For now, it is always the full size of the logical display.
212        mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
213
214        // Set the orientation.
215        // The orientation specifies how the physical coordinate system of the display
216        // is rotated when the contents of the logical display are rendered.
217        int orientation = Surface.ROTATION_0;
218        if (device == mPrimaryDisplayDevice
219                && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) {
220            orientation = displayInfo.rotation;
221        }
222
223        // Set the frame.
224        // The frame specifies the rotated physical coordinates into which the viewport
225        // is mapped.  We need to take care to preserve the aspect ratio of the viewport.
226        // Currently we maximize the area to fill the display, but we could try to be
227        // more clever and match resolutions.
228        boolean rotated = (orientation == Surface.ROTATION_90
229                || orientation == Surface.ROTATION_270);
230        int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
231        int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
232
233        // Determine whether the width or height is more constrained to be scaled.
234        //    physWidth / displayInfo.logicalWidth    => letter box
235        // or physHeight / displayInfo.logicalHeight  => pillar box
236        //
237        // We avoid a division (and possible floating point imprecision) here by
238        // multiplying the fractions by the product of their denominators before
239        // comparing them.
240        int displayRectWidth, displayRectHeight;
241        if (physWidth * displayInfo.logicalHeight
242                < physHeight * displayInfo.logicalWidth) {
243            // Letter box.
244            displayRectWidth = physWidth;
245            displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
246        } else {
247            // Pillar box.
248            displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
249            displayRectHeight = physHeight;
250        }
251        int displayRectTop = (physHeight - displayRectHeight) / 2;
252        int displayRectLeft = (physWidth - displayRectWidth) / 2;
253        mTempDisplayRect.set(displayRectLeft, displayRectTop,
254                displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
255
256        device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
257    }
258
259    public void dumpLocked(PrintWriter pw) {
260        pw.println("mLayerStack=" + mLayerStack);
261        pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
262                mPrimaryDisplayDevice.getNameLocked() : "null"));
263        pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
264        pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
265    }
266}