/*
* Copyright (C) 2012 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 android.view;
import android.content.res.CompatibilityInfo;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.util.DisplayMetrics;
import libcore.util.Objects;
/**
* Describes the characteristics of a particular logical display.
* @hide
*/
public final class DisplayInfo implements Parcelable {
/**
* The surface flinger layer stack associated with this logical display.
*/
public int layerStack;
/**
* Display flags.
*/
public int flags;
/**
* Display type.
*/
public int type;
/**
* Display address, or null if none.
* Interpretation varies by display type.
*/
public String address;
/**
* The human-readable name of the display.
*/
public String name;
/**
* The width of the portion of the display that is available to applications, in pixels.
* Represents the size of the display minus any system decorations.
*/
public int appWidth;
/**
* The height of the portion of the display that is available to applications, in pixels.
* Represents the size of the display minus any system decorations.
*/
public int appHeight;
/**
* The smallest value of {@link #appWidth} that an application is likely to encounter,
* in pixels, excepting cases where the width may be even smaller due to the presence
* of a soft keyboard, for example.
*/
public int smallestNominalAppWidth;
/**
* The smallest value of {@link #appHeight} that an application is likely to encounter,
* in pixels, excepting cases where the height may be even smaller due to the presence
* of a soft keyboard, for example.
*/
public int smallestNominalAppHeight;
/**
* The largest value of {@link #appWidth} that an application is likely to encounter,
* in pixels, excepting cases where the width may be even larger due to system decorations
* such as the status bar being hidden, for example.
*/
public int largestNominalAppWidth;
/**
* The largest value of {@link #appHeight} that an application is likely to encounter,
* in pixels, excepting cases where the height may be even larger due to system decorations
* such as the status bar being hidden, for example.
*/
public int largestNominalAppHeight;
/**
* The logical width of the display, in pixels.
* Represents the usable size of the display which may be smaller than the
* physical size when the system is emulating a smaller display.
*/
public int logicalWidth;
/**
* The logical height of the display, in pixels.
* Represents the usable size of the display which may be smaller than the
* physical size when the system is emulating a smaller display.
*/
public int logicalHeight;
/**
* @hide
* Number of overscan pixels on the left side of the display.
*/
public int overscanLeft;
/**
* @hide
* Number of overscan pixels on the top side of the display.
*/
public int overscanTop;
/**
* @hide
* Number of overscan pixels on the right side of the display.
*/
public int overscanRight;
/**
* @hide
* Number of overscan pixels on the bottom side of the display.
*/
public int overscanBottom;
/**
* The rotation of the display relative to its natural orientation.
* May be one of {@link android.view.Surface#ROTATION_0},
* {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180},
* {@link android.view.Surface#ROTATION_270}.
*
* The value of this field is indeterminate if the logical display is presented on
* more than one physical display.
*
*/
public int rotation;
/**
* The refresh rate of this display in frames per second.
*
* The value of this field is indeterminate if the logical display is presented on
* more than one physical display.
*
*/
public float refreshRate;
/**
* The logical display density which is the basis for density-independent
* pixels.
*/
public int logicalDensityDpi;
/**
* The exact physical pixels per inch of the screen in the X dimension.
*
* The value of this field is indeterminate if the logical display is presented on
* more than one physical display.
*
*/
public float physicalXDpi;
/**
* The exact physical pixels per inch of the screen in the Y dimension.
*
* The value of this field is indeterminate if the logical display is presented on
* more than one physical display.
*
*/
public float physicalYDpi;
/**
* The state of the display, such as {@link android.view.Display#STATE_ON}.
*/
public int state;
/**
* The UID of the application that owns this display, or zero if it is owned by the system.
*
* If the display is private, then only the owner can use it.
*
*/
public int ownerUid;
/**
* The package name of the application that owns this display, or null if it is
* owned by the system.
*
* If the display is private, then only the owner can use it.
*
*/
public String ownerPackageName;
public static final Creator CREATOR = new Creator() {
@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.equal(address, other.address)
&& Objects.equal(name, other.name)
&& 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
&& rotation == other.rotation
&& refreshRate == other.refreshRate
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
&& physicalYDpi == other.physicalYDpi
&& state == other.state
&& ownerUid == other.ownerUid
&& Objects.equal(ownerPackageName, other.ownerPackageName);
}
@Override
public int hashCode() {
return 0; // don't care
}
public void copyFrom(DisplayInfo other) {
layerStack = other.layerStack;
flags = other.flags;
type = other.type;
address = other.address;
name = other.name;
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;
rotation = other.rotation;
refreshRate = other.refreshRate;
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
}
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();
rotation = source.readInt();
refreshRate = source.readFloat();
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
state = source.readInt();
ownerUid = source.readInt();
ownerPackageName = source.readString();
}
@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);
dest.writeInt(rotation);
dest.writeFloat(refreshRate);
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
dest.writeInt(state);
dest.writeInt(ownerUid);
dest.writeString(ownerPackageName);
}
@Override
public int describeContents() {
return 0;
}
public void getAppMetrics(DisplayMetrics outMetrics) {
getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
public void getAppMetrics(DisplayMetrics outMetrics, DisplayAdjustments displayAdjustments) {
getMetricsWithSize(outMetrics, displayAdjustments.getCompatibilityInfo(),
displayAdjustments.getActivityToken(), appWidth, appHeight);
}
public void getAppMetrics(DisplayMetrics outMetrics, CompatibilityInfo ci, IBinder token) {
getMetricsWithSize(outMetrics, ci, token, appWidth, appHeight);
}
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
IBinder token) {
getMetricsWithSize(outMetrics, compatInfo, token, 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;
}
/**
* Returns true if the specified UID has access to this display.
*/
public boolean hasAccess(int uid) {
return Display.hasAccess(uid, flags, ownerUid);
}
private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
IBinder token, int width, int height) {
outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi;
outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width;
outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;
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;
if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
compatInfo.applyToDisplayMetrics(outMetrics);
}
}
// For debugging purposes
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("DisplayInfo{\"");
sb.append(name);
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(", ");
sb.append(refreshRate);
sb.append(" fps, 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(", 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("}");
return sb.toString();
}
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");
}
return result.toString();
}
}