blob: 1acd5d097525e28ec1484650af84824b12bd9345 [file] [log] [blame]
/*
* 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.hardware.devicestate.DeviceStateManager;
import android.os.Environment;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAddress;
import com.android.server.display.config.layout.Layouts;
import com.android.server.display.config.layout.XmlParser;
import com.android.server.display.layout.Layout;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.datatype.DatatypeConfigurationException;
/**
* 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;
private static final String CONFIG_FILE_PATH =
"etc/displayconfig/display_layout_configuration.xml";
private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
DeviceStateToLayoutMap() {
loadLayoutsFromConfig();
createLayout(STATE_DEFAULT);
}
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 createLayout(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;
}
/**
* Reads display-layout-configuration files to get the layouts to use for this device.
*/
private void loadLayoutsFromConfig() {
final File configFile = Environment.buildPath(
Environment.getVendorDirectory(), CONFIG_FILE_PATH);
if (!configFile.exists()) {
return;
}
Slog.i(TAG, "Loading display layouts from " + configFile);
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
final Layouts layouts = XmlParser.read(in);
if (layouts == null) {
Slog.i(TAG, "Display layout config not found: " + configFile);
return;
}
for (com.android.server.display.config.layout.Layout l : layouts.getLayout()) {
final int state = l.getState().intValue();
final Layout layout = createLayout(state);
for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
layout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
d.getIsDefault(),
d.getEnabled());
}
}
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
Slog.e(TAG, "Encountered an error while reading/parsing display layout config file: "
+ configFile, e);
}
}
}