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