blob: b55d7d5d9d3cc9da846d25a2ad9a73cc851e9969 [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.layout;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
import android.view.DisplayAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Holds a collection of {@link Display}s. A single instance of this class describes
* how to organize one or more DisplayDevices into LogicalDisplays for a particular device
* state. For example, there may be one instance of this class to describe display layout when
* a foldable device is folded, and a second instance for when the device is unfolded.
*/
public class Layout {
public static final String DEFAULT_DISPLAY_GROUP_NAME = "";
private static final String TAG = "Layout";
// Lead display Id is set to this if this is not a follower display, and therefore
// has no lead.
public static final int NO_LEAD_DISPLAY = -1;
private final List<Display> mDisplays = new ArrayList<>(2);
@Override
public String toString() {
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 the default 1:1 LogicalDisplay mapping for the specified DisplayDevice.
*
* @param address Address of the device.
* @param idProducer Produces the logical display id.
*/
public void createDefaultDisplayLocked(@NonNull DisplayAddress address,
DisplayIdProducer idProducer) {
createDisplayLocked(address, /* isDefault= */ true, /* isEnabled= */ true,
DEFAULT_DISPLAY_GROUP_NAME, idProducer, POSITION_UNKNOWN,
NO_LEAD_DISPLAY, /* brightnessThrottlingMapId= */ null,
/* refreshRateZoneId= */ null, /* refreshRateThermalThrottlingMapId= */ null);
}
/**
* 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
* @param displayGroupName Name of the display group to which the display is assigned.
* @param idProducer Produces the logical display id.
* @param position Indicates the position this display is facing in this layout.
* @param leadDisplayId Display that this one follows (-1 if none).
* @param brightnessThrottlingMapId Name of which brightness throttling policy should be used.
* @param refreshRateZoneId Layout limited refresh rate zone name.
* @param refreshRateThermalThrottlingMapId Name of which refresh rate throttling
* policy should be used.
* @exception IllegalArgumentException When a default display owns a display group other than
* DEFAULT_DISPLAY_GROUP.
*/
public void createDisplayLocked(
@NonNull DisplayAddress address, boolean isDefault, boolean isEnabled,
String displayGroupName, DisplayIdProducer idProducer, int position, int leadDisplayId,
String brightnessThrottlingMapId, @Nullable String refreshRateZoneId,
@Nullable String refreshRateThermalThrottlingMapId) {
if (contains(address)) {
Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
return;
}
// See if we're dealing with the "default" display
if (isDefault && getById(DEFAULT_DISPLAY) != null) {
Slog.w(TAG, "Ignoring attempt to add a second default display: " + address);
return;
}
// Assign a logical display ID and create the new display.
// 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.
if (displayGroupName == null) {
displayGroupName = DEFAULT_DISPLAY_GROUP_NAME;
}
if (isDefault && !displayGroupName.equals(DEFAULT_DISPLAY_GROUP_NAME)) {
throw new IllegalArgumentException("Default display should own DEFAULT_DISPLAY_GROUP");
}
final int logicalDisplayId = idProducer.getId(isDefault);
leadDisplayId = isDefault ? NO_LEAD_DISPLAY : leadDisplayId;
final Display display = new Display(address, logicalDisplayId, isEnabled, displayGroupName,
brightnessThrottlingMapId, position, leadDisplayId, refreshRateZoneId,
refreshRateThermalThrottlingMapId);
mDisplays.add(display);
}
/**
* @param id The ID of the display to remove.
*/
public void removeDisplayLocked(int id) {
Display display = getById(id);
if (display != null) {
mDisplays.remove(display);
}
}
/**
* @param address The address to check.
*
* @return True if the specified address is used in this layout.
*/
public boolean contains(@NonNull DisplayAddress address) {
final int size = mDisplays.size();
for (int i = 0; i < size; i++) {
if (address.equals(mDisplays.get(i).getAddress())) {
return true;
}
}
return false;
}
/**
* @param id The display ID to check.
*
* @return The display corresponding to the specified display ID.
*/
@Nullable
public Display getById(int id) {
for (int i = 0; i < mDisplays.size(); i++) {
Display display = mDisplays.get(i);
if (id == display.getLogicalDisplayId()) {
return display;
}
}
return null;
}
/**
* @param address The display address to check.
*
* @return The display corresponding to the specified address.
*/
@Nullable
public Display getByAddress(@NonNull DisplayAddress address) {
for (int i = 0; i < mDisplays.size(); i++) {
Display display = mDisplays.get(i);
if (address.equals(display.getAddress())) {
return display;
}
}
return null;
}
/**
* @param index The index of the display to return.
*
* @return the display at the specified index.
*/
public Display getAt(int index) {
return mDisplays.get(index);
}
/**
* @return The number of displays defined for this layout.
*/
public int size() {
return mDisplays.size();
}
/**
* 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 if this display is usable and can be switched on
private final boolean mIsEnabled;
// Name of display group to which the display is assigned
private final String mDisplayGroupName;
// The direction the display faces
// {@link DeviceStateToLayoutMap.POSITION_FRONT} or
// {@link DeviceStateToLayoutMap.POSITION_REAR}.
// {@link DeviceStateToLayoutMap.POSITION_UNKNOWN} is unspecified.
private final int mPosition;
// The ID of the thermal brightness throttling map that should be used. This can change
// e.g. in concurrent displays mode in which a stricter brightness throttling policy might
// need to be used.
@Nullable
private final String mThermalBrightnessThrottlingMapId;
// The ID of the lead display that this display will follow in a layout. -1 means no lead.
private final int mLeadDisplayId;
// Refresh rate zone id for specific layout
@Nullable
private final String mRefreshRateZoneId;
@Nullable
private final String mThermalRefreshRateThrottlingMapId;
private Display(@NonNull DisplayAddress address, int logicalDisplayId, boolean isEnabled,
@NonNull String displayGroupName, String brightnessThrottlingMapId, int position,
int leadDisplayId, @Nullable String refreshRateZoneId,
@Nullable String refreshRateThermalThrottlingMapId) {
mAddress = address;
mLogicalDisplayId = logicalDisplayId;
mIsEnabled = isEnabled;
mDisplayGroupName = displayGroupName;
mPosition = position;
mThermalBrightnessThrottlingMapId = brightnessThrottlingMapId;
mRefreshRateZoneId = refreshRateZoneId;
mThermalRefreshRateThrottlingMapId = refreshRateThermalThrottlingMapId;
mLeadDisplayId = leadDisplayId;
}
@Override
public String toString() {
return "{"
+ "dispId: " + mLogicalDisplayId
+ "(" + (mIsEnabled ? "ON" : "OFF") + ")"
+ ", displayGroupName: " + mDisplayGroupName
+ ", addr: " + mAddress
+ ((mPosition == POSITION_UNKNOWN) ? "" : ", position: " + mPosition)
+ ", mThermalBrightnessThrottlingMapId: " + mThermalBrightnessThrottlingMapId
+ ", mRefreshRateZoneId: " + mRefreshRateZoneId
+ ", mLeadDisplayId: " + mLeadDisplayId
+ ", mThermalRefreshRateThrottlingMapId: " + mThermalRefreshRateThrottlingMapId
+ "}";
}
@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.mDisplayGroupName.equals(otherDisplay.mDisplayGroupName)
&& this.mAddress.equals(otherDisplay.mAddress)
&& Objects.equals(mThermalBrightnessThrottlingMapId,
otherDisplay.mThermalBrightnessThrottlingMapId)
&& Objects.equals(otherDisplay.mRefreshRateZoneId, this.mRefreshRateZoneId)
&& this.mLeadDisplayId == otherDisplay.mLeadDisplayId
&& Objects.equals(mThermalRefreshRateThrottlingMapId,
otherDisplay.mThermalRefreshRateThrottlingMapId);
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + Boolean.hashCode(mIsEnabled);
result = 31 * result + mPosition;
result = 31 * result + mLogicalDisplayId;
result = 31 * result + mDisplayGroupName.hashCode();
result = 31 * result + mAddress.hashCode();
result = 31 * result + mThermalBrightnessThrottlingMapId.hashCode();
result = 31 * result + Objects.hashCode(mRefreshRateZoneId);
result = 31 * result + mLeadDisplayId;
result = 31 * result + Objects.hashCode(mThermalRefreshRateThrottlingMapId);
return result;
}
public DisplayAddress getAddress() {
return mAddress;
}
public int getLogicalDisplayId() {
return mLogicalDisplayId;
}
public boolean isEnabled() {
return mIsEnabled;
}
public String getDisplayGroupName() {
return mDisplayGroupName;
}
@Nullable
public String getRefreshRateZoneId() {
return mRefreshRateZoneId;
}
/**
* Gets the id of the thermal brightness throttling map that should be used.
* @return The ID of the thermal brightness throttling map that this display should use,
* null if unspecified, will fall back to default.
*/
public String getThermalBrightnessThrottlingMapId() {
return mThermalBrightnessThrottlingMapId;
}
/**
* @return the position that this display is facing.
*/
public int getPosition() {
return mPosition;
}
/**
* @return logical displayId of the display that this one follows.
*/
public int getLeadDisplayId() {
return mLeadDisplayId;
}
public String getRefreshRateThermalThrottlingMapId() {
return mThermalRefreshRateThrottlingMapId;
}
}
}