• Home
  • 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;
18 
19 import android.annotation.NonNull;
20 import android.hardware.devicestate.DeviceStateManager;
21 import android.os.Environment;
22 import android.util.IndentingPrintWriter;
23 import android.util.Slog;
24 import android.util.SparseArray;
25 import android.view.DisplayAddress;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.server.display.config.layout.Layouts;
29 import com.android.server.display.config.layout.XmlParser;
30 import com.android.server.display.layout.DisplayIdProducer;
31 import com.android.server.display.layout.Layout;
32 
33 import org.xmlpull.v1.XmlPullParserException;
34 
35 import java.io.BufferedInputStream;
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.math.BigInteger;
41 
42 import javax.xml.datatype.DatatypeConfigurationException;
43 
44 /**
45  * Mapping from device states into {@link Layout}s. This allows us to map device
46  * states into specific layouts for the connected displays; particularly useful for
47  * foldable and multi-display devices where the default display and which displays are ON can
48  * change depending on the state of the device.
49  */
50 class DeviceStateToLayoutMap {
51     private static final String TAG = "DeviceStateToLayoutMap";
52 
53     public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;
54 
55     // Direction of the display relative to the default display, whilst in this state
56     private static final int POSITION_UNKNOWN = Layout.Display.POSITION_UNKNOWN;
57     private static final int POSITION_FRONT = Layout.Display.POSITION_FRONT;
58     private static final int POSITION_REAR = Layout.Display.POSITION_REAR;
59 
60     private static final String FRONT_STRING = "front";
61     private static final String REAR_STRING = "rear";
62 
63     private static final String CONFIG_FILE_PATH =
64             "etc/displayconfig/display_layout_configuration.xml";
65 
66     private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
67     private final DisplayIdProducer mIdProducer;
68 
DeviceStateToLayoutMap(DisplayIdProducer idProducer)69     DeviceStateToLayoutMap(DisplayIdProducer idProducer) {
70         this(idProducer, Environment.buildPath(
71                 Environment.getVendorDirectory(), CONFIG_FILE_PATH));
72     }
73 
DeviceStateToLayoutMap(DisplayIdProducer idProducer, File configFile)74     DeviceStateToLayoutMap(DisplayIdProducer idProducer, File configFile) {
75         mIdProducer = idProducer;
76         loadLayoutsFromConfig(configFile);
77         createLayout(STATE_DEFAULT);
78     }
79 
dumpLocked(IndentingPrintWriter ipw)80     public void dumpLocked(IndentingPrintWriter ipw) {
81         ipw.println("DeviceStateToLayoutMap:");
82         ipw.increaseIndent();
83 
84         ipw.println("Registered Layouts:");
85         for (int i = 0; i < mLayoutMap.size(); i++) {
86             ipw.println("state(" + mLayoutMap.keyAt(i) + "): " + mLayoutMap.valueAt(i));
87         }
88     }
89 
get(int state)90     Layout get(int state) {
91         Layout layout = mLayoutMap.get(state);
92         if (layout == null) {
93             layout = mLayoutMap.get(STATE_DEFAULT);
94         }
95         return layout;
96     }
97 
size()98     int size() {
99         return mLayoutMap.size();
100     }
101 
102     /**
103      * Reads display-layout-configuration files to get the layouts to use for this device.
104      */
105     @VisibleForTesting
loadLayoutsFromConfig(@onNull File configFile)106     void loadLayoutsFromConfig(@NonNull File configFile) {
107         if (!configFile.exists()) {
108             return;
109         }
110 
111         Slog.i(TAG, "Loading display layouts from " + configFile);
112         try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
113             final Layouts layouts = XmlParser.read(in);
114             if (layouts == null) {
115                 Slog.i(TAG, "Display layout config not found: " + configFile);
116                 return;
117             }
118             for (com.android.server.display.config.layout.Layout l : layouts.getLayout()) {
119                 final int state = l.getState().intValue();
120                 final Layout layout = createLayout(state);
121                 for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
122                     assert layout != null;
123                     int position = getPosition(d.getPosition());
124                     BigInteger leadDisplayPhysicalId = d.getLeadDisplayAddress();
125                     DisplayAddress leadDisplayAddress = leadDisplayPhysicalId == null ? null
126                             : DisplayAddress.fromPhysicalDisplayId(
127                                     leadDisplayPhysicalId.longValue());
128                     layout.createDisplayLocked(
129                             DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
130                             d.isDefaultDisplay(),
131                             d.isEnabled(),
132                             d.getDisplayGroup(),
133                             mIdProducer,
134                             position,
135                             leadDisplayAddress,
136                             d.getBrightnessThrottlingMapId(),
137                             d.getRefreshRateZoneId(),
138                             d.getRefreshRateThermalThrottlingMapId());
139                 }
140                 layout.postProcessLocked();
141             }
142         } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
143             Slog.e(TAG, "Encountered an error while reading/parsing display layout config file: "
144                     + configFile, e);
145         }
146     }
147 
getPosition(@onNull String position)148     private int getPosition(@NonNull String position) {
149         int positionInt = POSITION_UNKNOWN;
150         if (FRONT_STRING.equals(position)) {
151             positionInt = POSITION_FRONT;
152         } else if (REAR_STRING.equals(position)) {
153             positionInt = POSITION_REAR;
154         }
155         return positionInt;
156     }
157 
createLayout(int state)158     private Layout createLayout(int state) {
159         if (mLayoutMap.contains(state)) {
160             Slog.e(TAG, "Attempted to create a second layout for state " + state);
161             return null;
162         }
163 
164         final Layout layout = new Layout();
165         mLayoutMap.append(state, layout);
166         return layout;
167     }
168 }
169