package android.view;
import static android.view.DisplayInfoProto.APP_HEIGHT;
import static android.view.DisplayInfoProto.APP_WIDTH;
import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
import static android.view.DisplayInfoProto.NAME;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;
import java.util.Arrays;
import java.util.Objects;
public final class DisplayInfo implements Parcelable {
public int layerStack;
public int flags;
public int type;
public String address;
public String name;
public String uniqueId;
public int appWidth;
public int appHeight;
public int smallestNominalAppWidth;
public int smallestNominalAppHeight;
public int largestNominalAppWidth;
public int largestNominalAppHeight;
public int logicalWidth;
public int logicalHeight;
public int overscanLeft;
public int overscanTop;
public int overscanRight;
public int overscanBottom;
public DisplayCutout displayCutout;
@Surface.Rotation
public int rotation;
public int modeId;
public int defaultModeId;
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
public int colorMode;
public int[] supportedColorModes = { Display.COLOR_MODE_DEFAULT };
public Display.HdrCapabilities hdrCapabilities;
public int logicalDensityDpi;
public float physicalXDpi;
public float physicalYDpi;
public long appVsyncOffsetNanos;
public long presentationDeadlineNanos;
public int state;
public int ownerUid;
public String ownerPackageName;
public int removeMode = Display.REMOVE_MODE_MOVE_CONTENT_TO_PRIMARY;
public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
return new DisplayInfo(source);
}
@Override
public DisplayInfo[] newArray(int size) {
return new DisplayInfo[size];
}
};
public DisplayInfo() {
}
public DisplayInfo(DisplayInfo other) {
copyFrom(other);
}
private DisplayInfo(Parcel source) {
readFromParcel(source);
}
@Override
public boolean equals(Object o) {
return o instanceof DisplayInfo && equals((DisplayInfo)o);
}
public boolean equals(DisplayInfo other) {
return other != null
&& layerStack == other.layerStack
&& flags == other.flags
&& type == other.type
&& Objects.equals(address, other.address)
&& Objects.equals(uniqueId, other.uniqueId)
&& appWidth == other.appWidth
&& appHeight == other.appHeight
&& smallestNominalAppWidth == other.smallestNominalAppWidth
&& smallestNominalAppHeight == other.smallestNominalAppHeight
&& largestNominalAppWidth == other.largestNominalAppWidth
&& largestNominalAppHeight == other.largestNominalAppHeight
&& logicalWidth == other.logicalWidth
&& logicalHeight == other.logicalHeight
&& overscanLeft == other.overscanLeft
&& overscanTop == other.overscanTop
&& overscanRight == other.overscanRight
&& overscanBottom == other.overscanBottom
&& Objects.equals(displayCutout, other.displayCutout)
&& rotation == other.rotation
&& modeId == other.modeId
&& defaultModeId == other.defaultModeId
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
&& Objects.equals(hdrCapabilities, other.hdrCapabilities)
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
&& appVsyncOffsetNanos == other.appVsyncOffsetNanos
&& presentationDeadlineNanos == other.presentationDeadlineNanos
&& state == other.state
&& ownerUid == other.ownerUid
&& Objects.equals(ownerPackageName, other.ownerPackageName)
&& removeMode == other.removeMode;
}
@Override
public int hashCode() {
return 0;
}
public void copyFrom(DisplayInfo other) {
layerStack = other.layerStack;
flags = other.flags;
type = other.type;
address = other.address;
name = other.name;
uniqueId = other.uniqueId;
appWidth = other.appWidth;
appHeight = other.appHeight;
smallestNominalAppWidth = other.smallestNominalAppWidth;
smallestNominalAppHeight = other.smallestNominalAppHeight;
largestNominalAppWidth = other.largestNominalAppWidth;
largestNominalAppHeight = other.largestNominalAppHeight;
logicalWidth = other.logicalWidth;
logicalHeight = other.logicalHeight;
overscanLeft = other.overscanLeft;
overscanTop = other.overscanTop;
overscanRight = other.overscanRight;
overscanBottom = other.overscanBottom;
displayCutout = other.displayCutout;
rotation = other.rotation;
modeId = other.modeId;
defaultModeId = other.defaultModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
colorMode = other.colorMode;
supportedColorModes = Arrays.copyOf(
other.supportedColorModes, other.supportedColorModes.length);
hdrCapabilities = other.hdrCapabilities;
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
appVsyncOffsetNanos = other.appVsyncOffsetNanos;
presentationDeadlineNanos = other.presentationDeadlineNanos;
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
removeMode = other.removeMode;
}
public void readFromParcel(Parcel source) {
layerStack = source.readInt();
flags = source.readInt();
type = source.readInt();
address = source.readString();
name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
smallestNominalAppWidth = source.readInt();
smallestNominalAppHeight = source.readInt();
largestNominalAppWidth = source.readInt();
largestNominalAppHeight = source.readInt();
logicalWidth = source.readInt();
logicalHeight = source.readInt();
overscanLeft = source.readInt();
overscanTop = source.readInt();
overscanRight = source.readInt();
overscanBottom = source.readInt();
displayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(source);
rotation = source.readInt();
modeId = source.readInt();
defaultModeId = source.readInt();
int nModes = source.readInt();
supportedModes = new Display.Mode[nModes];
for (int i = 0; i < nModes; i++) {
supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source);
}
colorMode = source.readInt();
int nColorModes = source.readInt();
supportedColorModes = new int[nColorModes];
for (int i = 0; i < nColorModes; i++) {
supportedColorModes[i] = source.readInt();
}
hdrCapabilities = source.readParcelable(null);
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
appVsyncOffsetNanos = source.readLong();
presentationDeadlineNanos = source.readLong();
state = source.readInt();
ownerUid = source.readInt();
ownerPackageName = source.readString();
uniqueId = source.readString();
removeMode = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(layerStack);
dest.writeInt(this.flags);
dest.writeInt(type);
dest.writeString(address);
dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
dest.writeInt(smallestNominalAppWidth);
dest.writeInt(smallestNominalAppHeight);
dest.writeInt(largestNominalAppWidth);
dest.writeInt(largestNominalAppHeight);
dest.writeInt(logicalWidth);
dest.writeInt(logicalHeight);
dest.writeInt(overscanLeft);
dest.writeInt(overscanTop);
dest.writeInt(overscanRight);
dest.writeInt(overscanBottom);
DisplayCutout.ParcelableWrapper.writeCutoutToParcel(displayCutout, dest, flags);
dest.writeInt(rotation);
dest.writeInt(modeId);
dest.writeInt(defaultModeId);
dest.writeInt(supportedModes.length);
for (int i = 0; i < supportedModes.length; i++) {
supportedModes[i].writeToParcel(dest, flags);
}
dest.writeInt(colorMode);
dest.writeInt(supportedColorModes.length);
for (int i = 0; i < supportedColorModes.length; i++) {
dest.writeInt(supportedColorModes[i]);
}
dest.writeParcelable(hdrCapabilities, flags);
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
dest.writeLong(appVsyncOffsetNanos);
dest.writeLong(presentationDeadlineNanos);
dest.writeInt(state);
dest.writeInt(ownerUid);
dest.writeString(ownerPackageName);
dest.writeString(uniqueId);
dest.writeInt(removeMode);
}
@Override
public int describeContents() {
return 0;
}
public Display.Mode getMode() {
return findMode(modeId);
}
public Display.Mode getDefaultMode() {
return findMode(defaultModeId);
}
private Display.Mode findMode(int id) {
for (int i = 0; i < supportedModes.length; i++) {
if (supportedModes[i].getModeId() == id) {
return supportedModes[i];
}
}
throw new IllegalStateException("Unable to locate mode " + id);
}
public int findDefaultModeByRefreshRate(float refreshRate) {
Display.Mode[] modes = supportedModes;
Display.Mode defaultMode = getDefaultMode();
for (int i = 0; i < modes.length; i++) {
if (modes[i].matches(
defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) {
return modes[i].getModeId();
}
}
return 0;
}
public float[] getDefaultRefreshRates() {
Display.Mode[] modes = supportedModes;
ArraySet<Float> rates = new ArraySet<>();
Display.Mode defaultMode = getDefaultMode();
for (int i = 0; i < modes.length; i++) {
Display.Mode mode = modes[i];
if (mode.getPhysicalWidth() == defaultMode.getPhysicalWidth()
&& mode.getPhysicalHeight() == defaultMode.getPhysicalHeight()) {
rates.add(mode.getRefreshRate());
}
}
float[] result = new float[rates.size()];
int i = 0;
for (Float rate : rates) {
result[i++] = rate;
}
return result;
}
public void getAppMetrics(DisplayMetrics outMetrics) {
getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
public void getAppMetrics(DisplayMetrics outMetrics, DisplayAdjustments displayAdjustments) {
getMetricsWithSize(outMetrics, displayAdjustments.getCompatibilityInfo(),
displayAdjustments.getConfiguration(), appWidth, appHeight);
}
public void getAppMetrics(DisplayMetrics outMetrics, CompatibilityInfo ci,
Configuration configuration) {
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
}
public int getNaturalHeight() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalHeight : logicalWidth;
}
public boolean isHdr() {
int[] types = hdrCapabilities != null ? hdrCapabilities.getSupportedHdrTypes() : null;
return types != null && types.length > 0;
}
public boolean isWideColorGamut() {
for (int colorMode : supportedColorModes) {
if (colorMode == Display.COLOR_MODE_DCI_P3 || colorMode > Display.COLOR_MODE_SRGB) {
return true;
}
}
return false;
}
public boolean hasAccess(int uid) {
return Display.hasAccess(uid, flags, ownerUid);
}
private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration, int width, int height) {
outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi;
outMetrics.density = outMetrics.noncompatDensity =
logicalDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
outMetrics.scaledDensity = outMetrics.noncompatScaledDensity = outMetrics.density;
outMetrics.xdpi = outMetrics.noncompatXdpi = physicalXDpi;
outMetrics.ydpi = outMetrics.noncompatYdpi = physicalYDpi;
final Rect appBounds = configuration != null
? configuration.windowConfiguration.getAppBounds() : null;
width = appBounds != null ? appBounds.width() : width;
height = appBounds != null ? appBounds.height() : height;
outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width;
outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;
if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
compatInfo.applyToDisplayMetrics(outMetrics);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("DisplayInfo{\"");
sb.append(name);
sb.append("\", uniqueId \"");
sb.append(uniqueId);
sb.append("\", app ");
sb.append(appWidth);
sb.append(" x ");
sb.append(appHeight);
sb.append(", real ");
sb.append(logicalWidth);
sb.append(" x ");
sb.append(logicalHeight);
if (overscanLeft != 0 || overscanTop != 0 || overscanRight != 0 || overscanBottom != 0) {
sb.append(", overscan (");
sb.append(overscanLeft);
sb.append(",");
sb.append(overscanTop);
sb.append(",");
sb.append(overscanRight);
sb.append(",");
sb.append(overscanBottom);
sb.append(")");
}
sb.append(", largest app ");
sb.append(largestNominalAppWidth);
sb.append(" x ");
sb.append(largestNominalAppHeight);
sb.append(", smallest app ");
sb.append(smallestNominalAppWidth);
sb.append(" x ");
sb.append(smallestNominalAppHeight);
sb.append(", mode ");
sb.append(modeId);
sb.append(", defaultMode ");
sb.append(defaultModeId);
sb.append(", modes ");
sb.append(Arrays.toString(supportedModes));
sb.append(", colorMode ");
sb.append(colorMode);
sb.append(", supportedColorModes ");
sb.append(Arrays.toString(supportedColorModes));
sb.append(", hdrCapabilities ");
sb.append(hdrCapabilities);
sb.append(", rotation ");
sb.append(rotation);
sb.append(", density ");
sb.append(logicalDensityDpi);
sb.append(" (");
sb.append(physicalXDpi);
sb.append(" x ");
sb.append(physicalYDpi);
sb.append(") dpi, layerStack ");
sb.append(layerStack);
sb.append(", appVsyncOff ");
sb.append(appVsyncOffsetNanos);
sb.append(", presDeadline ");
sb.append(presentationDeadlineNanos);
sb.append(", type ");
sb.append(Display.typeToString(type));
if (address != null) {
sb.append(", address ").append(address);
}
sb.append(", state ");
sb.append(Display.stateToString(state));
if (ownerUid != 0 || ownerPackageName != null) {
sb.append(", owner ").append(ownerPackageName);
sb.append(" (uid ").append(ownerUid).append(")");
}
sb.append(flagsToString(flags));
sb.append(", removeMode ");
sb.append(removeMode);
sb.append("}");
return sb.toString();
}
public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
final long token = protoOutputStream.start(fieldId);
protoOutputStream.write(LOGICAL_WIDTH, logicalWidth);
protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight);
protoOutputStream.write(APP_WIDTH, appWidth);
protoOutputStream.write(APP_HEIGHT, appHeight);
protoOutputStream.write(NAME, name);
protoOutputStream.end(token);
}
private static String flagsToString(int flags) {
StringBuilder result = new StringBuilder();
if ((flags & Display.FLAG_SECURE) != 0) {
result.append(", FLAG_SECURE");
}
if ((flags & Display.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
result.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
if ((flags & Display.FLAG_PRIVATE) != 0) {
result.append(", FLAG_PRIVATE");
}
if ((flags & Display.FLAG_PRESENTATION) != 0) {
result.append(", FLAG_PRESENTATION");
}
if ((flags & Display.FLAG_SCALING_DISABLED) != 0) {
result.append(", FLAG_SCALING_DISABLED");
}
if ((flags & Display.FLAG_ROUND) != 0) {
result.append(", FLAG_ROUND");
}
return result.toString();
}
}