Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3fc996d authored by Fiona Campbell's avatar Fiona Campbell
Browse files

Add position to displaylayoutconfig

Position of each display in a state can be defined in
display_layout_configuration.xml

Bug: 259038047
Test: dumpsys display | grep addr
Test: atest DeviceStateToLayoutMapTest

Change-Id: Idac00886cd0a88d1db7d7401a707d7185a4eb8d8
parent ea89a254
Loading
Loading
Loading
Loading
+44 −19
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.display;

import android.annotation.NonNull;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Environment;
import android.util.IndentingPrintWriter;
@@ -23,8 +24,10 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAddress;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.config.layout.Layouts;
import com.android.server.display.config.layout.XmlParser;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;

import org.xmlpull.v1.XmlPullParserException;
@@ -48,13 +51,28 @@ class DeviceStateToLayoutMap {

    public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;

    // Direction of the display relative to the default display, whilst in this state
    private static final int POSITION_UNKNOWN = Layout.Display.POSITION_UNKNOWN;
    private static final int POSITION_FRONT = Layout.Display.POSITION_FRONT;
    private static final int POSITION_REAR = Layout.Display.POSITION_REAR;

    private static final String FRONT_STRING = "front";
    private static final String REAR_STRING = "rear";

    private static final String CONFIG_FILE_PATH =
            "etc/displayconfig/display_layout_configuration.xml";

    private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
    private final DisplayIdProducer mIdProducer;

    DeviceStateToLayoutMap(DisplayIdProducer idProducer) {
        this(idProducer, Environment.buildPath(
                Environment.getVendorDirectory(), CONFIG_FILE_PATH));
    }

    DeviceStateToLayoutMap() {
        loadLayoutsFromConfig();
    DeviceStateToLayoutMap(DisplayIdProducer idProducer, File configFile) {
        mIdProducer = idProducer;
        loadLayoutsFromConfig(configFile);
        createLayout(STATE_DEFAULT);
    }

@@ -76,24 +94,11 @@ class DeviceStateToLayoutMap {
        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);

    @VisibleForTesting
    void loadLayoutsFromConfig(@NonNull File configFile) {
        if (!configFile.exists()) {
            return;
        }
@@ -109,10 +114,19 @@ class DeviceStateToLayoutMap {
                final int state = l.getState().intValue();
                final Layout layout = createLayout(state);
                for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
                    layout.createDisplayLocked(
                    Layout.Display display = layout.createDisplayLocked(
                            DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
                            d.isDefaultDisplay(),
                            d.isEnabled());
                            d.isEnabled(),
                            mIdProducer);

                    if (FRONT_STRING.equals(d.getPosition())) {
                        display.setPosition(POSITION_FRONT);
                    } else if (REAR_STRING.equals(d.getPosition())) {
                        display.setPosition(POSITION_REAR);
                    } else {
                        display.setPosition(POSITION_UNKNOWN);
                    }
                }
            }
        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
@@ -120,4 +134,15 @@ class DeviceStateToLayoutMap {
                    + configFile, e);
        }
    }

    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;
    }
}
+12 −4
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.view.DisplayAddress;
import android.view.DisplayInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;

import java.io.PrintWriter;
@@ -82,6 +83,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    private static final int UPDATE_STATE_TRANSITION = 1;
    private static final int UPDATE_STATE_UPDATED = 2;

    private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;

    /**
     * Temporary display info, used for comparing display configurations.
     */
@@ -170,6 +173,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    private final ArrayMap<String, Integer> mVirtualDeviceDisplayMapping = new ArrayMap<>();

    private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
    private final DisplayIdProducer mIdProducer = (isDefault) ->
            isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
    private Layout mCurrentLayout = null;
    private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
    private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
@@ -179,7 +184,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
    LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
            @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
            @NonNull Handler handler) {
        this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap());
        this(context, repo, listener, syncRoot, handler,
                new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
                        : sNextNonDefaultDisplayId++));
    }

    LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
@@ -588,7 +595,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {

        // Create a logical display for the new display device
        LogicalDisplay display = createNewLogicalDisplayLocked(
                device, Layout.assignDisplayIdLocked(false /*isDefault*/));
                device, mIdProducer.getId(/* isDefault= */ false));

        applyLayoutLocked();
        updateLogicalDisplaysLocked();
@@ -621,7 +628,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
                        & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0
                        && !nextDeviceInfo.address.equals(deviceInfo.address)) {
                    layout.createDisplayLocked(nextDeviceInfo.address,
                            /* isDefault= */ true, /* isEnabled= */ true);
                            /* isDefault= */ true, /* isEnabled= */ true, mIdProducer);
                    applyLayoutLocked();
                    return;
                }
@@ -1036,7 +1043,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
            return;
        }
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        layout.createDisplayLocked(info.address, /* isDefault= */ true, /* isEnabled= */ true);
        layout.createDisplayLocked(info.address, /* isDefault= */ true, /* isEnabled= */ true,
                mIdProducer);
    }

    private int assignLayerStackLocked(int displayId) {
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.layout;

/**
 * Interface for producing logical display ids.
 */
public interface DisplayIdProducer {

    /**
     * Generates a new display ID
     * @param isDefault if requested display is the default display.
     * @return the next unique logical display Id.
     */
    int getId(boolean isDefault);
}
+66 −5
Original line number Diff line number Diff line
@@ -50,15 +50,33 @@ public class Layout {
        return mDisplays.toString();
    }

    @Override
    public boolean equals(Object obj) {

        if (!(obj instanceof  Layout)) {
            return false;
        }

        Layout otherLayout = (Layout) obj;
        return this.mDisplays.equals(otherLayout.mDisplays);
    }

    @Override
    public int hashCode() {
        return mDisplays.hashCode();
    }

    /**
     * Creates a simple 1:1 LogicalDisplay mapping for the specified DisplayDevice.
     *
     * @param address Address of the device.
     * @param isDefault Indicates if the device is meant to be the default display.
     * @param isEnabled Indicates if this display is usable and can be switched on
     * @return The new layout.
     */
    public Display createDisplayLocked(
            @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled) {
            @NonNull DisplayAddress address, boolean isDefault, boolean isEnabled,
            DisplayIdProducer idProducer) {
        if (contains(address)) {
            Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
            return null;
@@ -74,7 +92,7 @@ public class Layout {
        // Note that the logical display ID is saved into the layout, so when switching between
        // different layouts, a logical display can be destroyed and later recreated with the
        // same logical display ID.
        final int logicalDisplayId = assignDisplayIdLocked(isDefault);
        final int logicalDisplayId = idProducer.getId(isDefault);
        final Display display = new Display(address, logicalDisplayId, isEnabled);

        mDisplays.add(display);
@@ -158,25 +176,64 @@ public class Layout {
     * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s.
     */
    public static class Display {
        public static final int POSITION_UNKNOWN = -1;
        public static final int POSITION_FRONT = 0;
        public static final int POSITION_REAR = 1;

        // Address of the display device to map to this display.
        private final DisplayAddress mAddress;

        // Logical Display ID to apply to this display.
        private final int mLogicalDisplayId;

        // Indicates that this display is not usable and should remain off.
        // Indicates if this display is usable and can be switched on
        private final boolean mIsEnabled;

        // The direction the display faces
        // {@link DeviceStateToLayoutMap.POSITION_FRONT} or
        // {@link DeviceStateToLayoutMap.POSITION_REAR}.
        // {@link DeviceStateToLayoutMap.POSITION_UNKNOWN} is unspecified.
        private int mPosition;

        Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled) {
            mAddress = address;
            mLogicalDisplayId = logicalDisplayId;
            mIsEnabled = isEnabled;
            mPosition = POSITION_UNKNOWN;
        }

        @Override
        public String toString() {
            return "{addr: " + mAddress + ", dispId: " + mLogicalDisplayId
                    + "(" + (mIsEnabled ? "ON" : "OFF") + ")}";
            return "{"
                    + "dispId: " + mLogicalDisplayId
                    + "(" + (mIsEnabled ? "ON" : "OFF") + ")"
                    + ", addr: " + mAddress
                    +  ((mPosition == POSITION_UNKNOWN) ? "" : ", position: " + mPosition)
                    + "}";
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Display)) {
                return false;
            }

            Display otherDisplay = (Display) obj;

            return otherDisplay.mIsEnabled == this.mIsEnabled
                    && otherDisplay.mPosition == this.mPosition
                    && otherDisplay.mLogicalDisplayId == this.mLogicalDisplayId
                    && this.mAddress.equals(otherDisplay.mAddress);
        }

        @Override
        public int hashCode() {
            int result = 1;
            result = 31 * result + Boolean.hashCode(mIsEnabled);
            result = 31 * result + mPosition;
            result = 31 * result + mLogicalDisplayId;
            result = 31 * result + mAddress.hashCode();
            return result;
        }

        public DisplayAddress getAddress() {
@@ -190,5 +247,9 @@ public class Layout {
        public boolean isEnabled() {
            return mIsEnabled;
        }

        public void setPosition(int position) {
            mPosition = position;
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
    <xs:complexType name="display">
        <xs:sequence>
            <xs:element name="address" type="xs:nonNegativeInteger"/>
            <xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" />
        </xs:sequence>
        <xs:attribute name="enabled" type="xs:boolean" use="optional" />
        <xs:attribute name="defaultDisplay" type="xs:boolean" use="optional" />
Loading