• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.server.display.layout;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 
21 import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.text.TextUtils;
26 import android.util.ArraySet;
27 import android.util.Slog;
28 import android.view.DisplayAddress;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Objects;
33 
34 /**
35  * Holds a collection of {@link Display}s. A single instance of this class describes
36  * how to organize one or more DisplayDevices into LogicalDisplays for a particular device
37  * state. For example, there may be one instance of this class to describe display layout when
38  * a foldable device is folded, and a second instance for when the device is unfolded.
39  */
40 public class Layout {
41     public static final String DEFAULT_DISPLAY_GROUP_NAME = "";
42 
43     private static final String TAG = "Layout";
44 
45     // Lead display Id is set to this if this is not a follower display, and therefore
46     // has no lead.
47     public static final int NO_LEAD_DISPLAY = -1;
48 
49     private final List<Display> mDisplays = new ArrayList<>(2);
50 
51     @Override
toString()52     public String toString() {
53         return mDisplays.toString();
54     }
55 
56     @Override
equals(Object obj)57     public boolean equals(Object obj) {
58 
59         if (!(obj instanceof  Layout)) {
60             return false;
61         }
62 
63         Layout otherLayout = (Layout) obj;
64         return this.mDisplays.equals(otherLayout.mDisplays);
65     }
66 
67     @Override
hashCode()68     public int hashCode() {
69         return mDisplays.hashCode();
70     }
71 
72     /**
73      * Creates the default 1:1 LogicalDisplay mapping for the specified DisplayDevice.
74      *
75      * @param address Address of the device.
76      * @param idProducer Produces the logical display id.
77      */
createDefaultDisplayLocked(@onNull DisplayAddress address, DisplayIdProducer idProducer)78     public void createDefaultDisplayLocked(@NonNull DisplayAddress address,
79             DisplayIdProducer idProducer) {
80         createDisplayLocked(address, /* isDefault= */ true, /* isEnabled= */ true,
81                 DEFAULT_DISPLAY_GROUP_NAME, idProducer, POSITION_UNKNOWN,
82                 /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null,
83                 /* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null,
84                 /* powerThrottlingMapId= */ null);
85     }
86 
87     /**
88      * Creates a simple 1:1 LogicalDisplay mapping for the specified DisplayDevice.
89      *
90      * @param address Address of the device.
91      * @param isDefault Indicates if the device is meant to be the default display.
92      * @param isEnabled Indicates if this display is usable and can be switched on
93      * @param displayGroupName Name of the display group to which the display is assigned.
94      * @param idProducer Produces the logical display id.
95      * @param position Indicates the position this display is facing in this layout.
96      * @param leadDisplayAddress Address of a display that this one follows ({@code null} if none).
97      * @param brightnessThrottlingMapId Name of which brightness throttling policy should be used.
98      * @param refreshRateZoneId Layout limited refresh rate zone name.
99      * @param refreshRateThermalThrottlingMapId Name of which refresh rate throttling
100      *                                          policy should be used.
101      * @param powerThrottlingMapId Name of which power throttling policy should be used.
102      *
103      * @exception IllegalArgumentException When a default display owns a display group other than
104      *            DEFAULT_DISPLAY_GROUP.
105      */
createDisplayLocked( @onNull DisplayAddress address, boolean isDefault, boolean isEnabled, String displayGroupName, DisplayIdProducer idProducer, int position, @Nullable DisplayAddress leadDisplayAddress, String brightnessThrottlingMapId, @Nullable String refreshRateZoneId, @Nullable String refreshRateThermalThrottlingMapId, @Nullable String powerThrottlingMapId)106     public void createDisplayLocked(
107             @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled,
108             String displayGroupName, DisplayIdProducer idProducer, int position,
109             @Nullable DisplayAddress leadDisplayAddress, String brightnessThrottlingMapId,
110             @Nullable String refreshRateZoneId,
111             @Nullable String refreshRateThermalThrottlingMapId,
112             @Nullable String powerThrottlingMapId) {
113         if (contains(address)) {
114             Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
115             return;
116         }
117 
118         // See if we're dealing with the "default" display
119         if (isDefault && getById(DEFAULT_DISPLAY) != null) {
120             Slog.w(TAG, "Ignoring attempt to add a second default display: " + address);
121             return;
122         }
123 
124         if (displayGroupName == null) {
125             displayGroupName = DEFAULT_DISPLAY_GROUP_NAME;
126         }
127         if (isDefault && !displayGroupName.equals(DEFAULT_DISPLAY_GROUP_NAME)) {
128             throw new IllegalArgumentException("Default display should own DEFAULT_DISPLAY_GROUP");
129         }
130         if (isDefault && leadDisplayAddress != null) {
131             throw new IllegalArgumentException("Default display cannot have a lead display");
132         }
133         if (address.equals(leadDisplayAddress)) {
134             throw new IllegalArgumentException("Lead display address cannot be the same as display "
135                     + " address");
136         }
137         // Assign a logical display ID and create the new display.
138         // Note that the logical display ID is saved into the layout, so when switching between
139         // different layouts, a logical display can be destroyed and later recreated with the
140         // same logical display ID.
141         final int logicalDisplayId = idProducer.getId(isDefault);
142 
143         final Display display = new Display(address, logicalDisplayId, isEnabled, displayGroupName,
144                 brightnessThrottlingMapId, position, leadDisplayAddress, refreshRateZoneId,
145                 refreshRateThermalThrottlingMapId, powerThrottlingMapId);
146 
147         mDisplays.add(display);
148     }
149 
150     /**
151      * @param id The ID of the display to remove.
152      */
removeDisplayLocked(int id)153     public void removeDisplayLocked(int id) {
154         Display display = getById(id);
155         if (display != null) {
156             mDisplays.remove(display);
157         }
158     }
159 
160     /**
161      * Applies post-processing to displays to make sure the information of each display is
162      * up-to-date.
163      *
164      * <p>At creation of a display, lead display is specified by display address. At post
165      * processing, we convert it to logical display ID.
166      */
postProcessLocked()167     public void postProcessLocked() {
168         for (int i = 0; i < mDisplays.size(); i++) {
169             Display display = mDisplays.get(i);
170             if (display.getLogicalDisplayId() == DEFAULT_DISPLAY) {
171                 display.setLeadDisplayId(NO_LEAD_DISPLAY);
172                 continue;
173             }
174             DisplayAddress leadDisplayAddress = display.getLeadDisplayAddress();
175             if (leadDisplayAddress == null) {
176                 display.setLeadDisplayId(NO_LEAD_DISPLAY);
177                 continue;
178             }
179             Display leadDisplay = getByAddress(leadDisplayAddress);
180             if (leadDisplay == null) {
181                 throw new IllegalArgumentException("Cannot find a lead display whose address is "
182                         + leadDisplayAddress);
183             }
184             if (!TextUtils.equals(display.getDisplayGroupName(),
185                     leadDisplay.getDisplayGroupName())) {
186                 throw new IllegalArgumentException("Lead display(" + leadDisplay + ") should be in "
187                         + "the same display group of the display(" + display + ")");
188             }
189             if (hasCyclicLeadDisplay(display)) {
190                 throw new IllegalArgumentException("Display(" + display + ") has a cyclic lead "
191                         + "display");
192             }
193             display.setLeadDisplayId(leadDisplay.getLogicalDisplayId());
194         }
195     }
196 
197     /**
198      * @param address The address to check.
199      *
200      * @return True if the specified address is used in this layout.
201      */
contains(@onNull DisplayAddress address)202     public boolean contains(@NonNull DisplayAddress address) {
203         return getByAddress(address) != null;
204     }
205 
206     /**
207      * @param id The display ID to check.
208      *
209      * @return The display corresponding to the specified display ID.
210      */
211     @Nullable
getById(int id)212     public Display getById(int id) {
213         for (int i = 0; i < mDisplays.size(); i++) {
214             Display display = mDisplays.get(i);
215             if (id == display.getLogicalDisplayId()) {
216                 return display;
217             }
218         }
219         return null;
220     }
221 
222     /**
223      * @param address The display address to check.
224      *
225      * @return The display corresponding to the specified address.
226      */
227     @Nullable
getByAddress(@onNull DisplayAddress address)228     public Display getByAddress(@NonNull DisplayAddress address) {
229         for (int i = 0; i < mDisplays.size(); i++) {
230             Display display = mDisplays.get(i);
231             if (address.equals(display.getAddress())) {
232                 return display;
233             }
234             if (DisplayAddress.Physical.isPortMatch(address, display.getAddress())) {
235                 return display;
236             }
237         }
238         return null;
239     }
240 
241     /**
242      * @param index The index of the display to return.
243      *
244      * @return the display at the specified index.
245      */
getAt(int index)246     public Display getAt(int index) {
247         return mDisplays.get(index);
248     }
249 
250     /**
251      * @return The number of displays defined for this layout.
252      */
size()253     public int size() {
254         return mDisplays.size();
255     }
256 
hasCyclicLeadDisplay(Display display)257     private boolean hasCyclicLeadDisplay(Display display) {
258         ArraySet<Display> visited = new ArraySet<>();
259 
260         while (display != null) {
261             if (visited.contains(display)) {
262                 return true;
263             }
264             visited.add(display);
265             DisplayAddress leadDisplayAddress = display.getLeadDisplayAddress();
266             display = leadDisplayAddress == null ? null : getByAddress(leadDisplayAddress);
267         }
268         return false;
269     }
270 
271     /**
272      * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s.
273      */
274     public static class Display {
275         public static final int POSITION_UNKNOWN = -1;
276         public static final int POSITION_FRONT = 0;
277         public static final int POSITION_REAR = 1;
278 
279         // Address of the display device to map to this display.
280         private final DisplayAddress mAddress;
281 
282         // Logical Display ID to apply to this display.
283         private final int mLogicalDisplayId;
284 
285         // Indicates if this display is usable and can be switched on
286         private final boolean mIsEnabled;
287 
288         // Name of display group to which the display is assigned
289         private final String mDisplayGroupName;
290 
291         // The direction the display faces
292         // {@link DeviceStateToLayoutMap.POSITION_FRONT} or
293         // {@link DeviceStateToLayoutMap.POSITION_REAR}.
294         // {@link DeviceStateToLayoutMap.POSITION_UNKNOWN} is unspecified.
295         private final int mPosition;
296 
297         // The ID of the thermal brightness throttling map that should be used. This can change
298         // e.g. in concurrent displays mode in which a stricter brightness throttling policy might
299         // need to be used.
300         @Nullable
301         private final String mThermalBrightnessThrottlingMapId;
302 
303         // The address of the lead display that is specified in display-layout-configuration.
304         @Nullable
305         private final DisplayAddress mLeadDisplayAddress;
306 
307         // Refresh rate zone id for specific layout
308         @Nullable
309         private final String mRefreshRateZoneId;
310 
311         @Nullable
312         private final String mThermalRefreshRateThrottlingMapId;
313 
314         @Nullable
315         private final String mPowerThrottlingMapId;
316 
317         // The ID of the lead display that this display will follow in a layout. -1 means no lead.
318         // This is determined using {@code mLeadDisplayAddress}.
319         private int mLeadDisplayId;
320 
Display(@onNull DisplayAddress address, int logicalDisplayId, boolean isEnabled, @NonNull String displayGroupName, String brightnessThrottlingMapId, int position, @Nullable DisplayAddress leadDisplayAddress, @Nullable String refreshRateZoneId, @Nullable String refreshRateThermalThrottlingMapId, @Nullable String powerThrottlingMapId)321         private Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled,
322                 @NonNull String displayGroupName, String brightnessThrottlingMapId, int position,
323                 @Nullable DisplayAddress leadDisplayAddress, @Nullable String refreshRateZoneId,
324                 @Nullable String refreshRateThermalThrottlingMapId,
325                 @Nullable String powerThrottlingMapId) {
326             mAddress = address;
327             mLogicalDisplayId = logicalDisplayId;
328             mIsEnabled = isEnabled;
329             mDisplayGroupName = displayGroupName;
330             mPosition = position;
331             mThermalBrightnessThrottlingMapId = brightnessThrottlingMapId;
332             mLeadDisplayAddress = leadDisplayAddress;
333             mRefreshRateZoneId = refreshRateZoneId;
334             mThermalRefreshRateThrottlingMapId = refreshRateThermalThrottlingMapId;
335             mPowerThrottlingMapId = powerThrottlingMapId;
336             mLeadDisplayId = NO_LEAD_DISPLAY;
337         }
338 
339         @Override
toString()340         public String toString() {
341             return "{"
342                     + "dispId: " + mLogicalDisplayId
343                     + "(" + (mIsEnabled ? "ON" : "OFF") + ")"
344                     + ", displayGroupName: " + mDisplayGroupName
345                     + ", addr: " + mAddress
346                     +  ((mPosition == POSITION_UNKNOWN) ? "" : ", position: " + mPosition)
347                     + ", mThermalBrightnessThrottlingMapId: " + mThermalBrightnessThrottlingMapId
348                     + ", mRefreshRateZoneId: " + mRefreshRateZoneId
349                     + ", mLeadDisplayId: " + mLeadDisplayId
350                     + ", mLeadDisplayAddress: " + mLeadDisplayAddress
351                     + ", mThermalRefreshRateThrottlingMapId: " + mThermalRefreshRateThrottlingMapId
352                     + ", mPowerThrottlingMapId: " + mPowerThrottlingMapId
353                     + "}";
354         }
355 
356         @Override
equals(Object obj)357         public boolean equals(Object obj) {
358             if (!(obj instanceof Display)) {
359                 return false;
360             }
361 
362             Display otherDisplay = (Display) obj;
363 
364             return otherDisplay.mIsEnabled == this.mIsEnabled
365                     && otherDisplay.mPosition == this.mPosition
366                     && otherDisplay.mLogicalDisplayId == this.mLogicalDisplayId
367                     && this.mDisplayGroupName.equals(otherDisplay.mDisplayGroupName)
368                     && this.mAddress.equals(otherDisplay.mAddress)
369                     && Objects.equals(mThermalBrightnessThrottlingMapId,
370                     otherDisplay.mThermalBrightnessThrottlingMapId)
371                     && Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId)
372                     && this.mLeadDisplayId == otherDisplay.mLeadDisplayId
373                     && Objects.equals(mLeadDisplayAddress, otherDisplay.mLeadDisplayAddress)
374                     && Objects.equals(mThermalRefreshRateThrottlingMapId,
375                     otherDisplay.mThermalRefreshRateThrottlingMapId)
376                     && Objects.equals(mPowerThrottlingMapId,
377                     otherDisplay.mPowerThrottlingMapId);
378         }
379 
380         @Override
hashCode()381         public int hashCode() {
382             int result = 1;
383             result = 31 * result + Boolean.hashCode(mIsEnabled);
384             result = 31 * result + mPosition;
385             result = 31 * result + mLogicalDisplayId;
386             result = 31 * result + mDisplayGroupName.hashCode();
387             result = 31 * result + mAddress.hashCode();
388             result = 31 * result + Objects.hashCode(mThermalBrightnessThrottlingMapId);
389             result = 31 * result + Objects.hashCode(mRefreshRateZoneId);
390             result = 31 * result + mLeadDisplayId;
391             result = 31 * result + Objects.hashCode(mLeadDisplayAddress);
392             result = 31 * result + Objects.hashCode(mThermalRefreshRateThrottlingMapId);
393             result = 31 * result + Objects.hashCode(mPowerThrottlingMapId);
394             return result;
395         }
396 
getAddress()397         public DisplayAddress getAddress() {
398             return mAddress;
399         }
400 
getLogicalDisplayId()401         public int getLogicalDisplayId() {
402             return mLogicalDisplayId;
403         }
404 
isEnabled()405         public boolean isEnabled() {
406             return mIsEnabled;
407         }
408 
getDisplayGroupName()409         public String getDisplayGroupName() {
410             return mDisplayGroupName;
411         }
412 
413         @Nullable
getRefreshRateZoneId()414         public String getRefreshRateZoneId() {
415             return mRefreshRateZoneId;
416         }
417 
418         /**
419          * Gets the id of the thermal brightness throttling map that should be used.
420          * @return The ID of the thermal brightness throttling map that this display should use,
421          *         null if unspecified, will fall back to default.
422          */
getThermalBrightnessThrottlingMapId()423         public String getThermalBrightnessThrottlingMapId() {
424             return mThermalBrightnessThrottlingMapId;
425         }
426 
427         /**
428          * @return the position that this display is facing.
429          */
getPosition()430         public int getPosition() {
431             return mPosition;
432         }
433 
434         /**
435          * @return logical displayId of the display that this one follows.
436          */
getLeadDisplayId()437         public int getLeadDisplayId() {
438             return mLeadDisplayId;
439         }
440 
441         /**
442          * @return Display address of the display that this one follows.
443          */
444         @Nullable
getLeadDisplayAddress()445         public DisplayAddress getLeadDisplayAddress() {
446             return mLeadDisplayAddress;
447         }
448 
getRefreshRateThermalThrottlingMapId()449         public String getRefreshRateThermalThrottlingMapId() {
450             return mThermalRefreshRateThrottlingMapId;
451         }
452 
453         /**
454          * Gets the id of the power throttling map that should be used.
455          * @return The ID of the power throttling map that this display should use,
456          *         null if unspecified, will fall back to default.
457          */
getPowerThrottlingMapId()458         public String getPowerThrottlingMapId() {
459             return mPowerThrottlingMapId;
460         }
461 
setLeadDisplayId(int id)462         private void setLeadDisplayId(int id) {
463             mLeadDisplayId = id;
464         }
465     }
466 }
467