summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2013-06-18 22:36:28 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-06-18 22:36:29 +0000
commit9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0 (patch)
treee5914567c4e5cc6604e9590dd255ca7aba22478b
parent602c0d8b499aa9593e5713cb92f15c45cc9f9a54 (diff)
parenta506a6ec94863a35acca9feb165db76ddac3892c (diff)
downloadframeworks_base-9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0.zip
frameworks_base-9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0.tar.gz
frameworks_base-9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0.tar.bz2
Merge "Add an API to allow for creating private virtual displays."
-rw-r--r--api/current.txt7
-rw-r--r--core/java/android/hardware/display/DisplayManager.java37
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java50
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl8
-rw-r--r--core/java/android/hardware/display/VirtualDisplay.java62
-rw-r--r--core/java/android/view/Display.java69
-rw-r--r--core/java/android/view/DisplayInfo.java47
-rw-r--r--services/java/com/android/server/display/DisplayDeviceInfo.java68
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java265
-rw-r--r--services/java/com/android/server/display/LogicalDisplay.java5
-rw-r--r--services/java/com/android/server/display/VirtualDisplayAdapter.java161
-rw-r--r--services/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java15
13 files changed, 724 insertions, 77 deletions
diff --git a/api/current.txt b/api/current.txt
index 025bb35..d026c24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10548,6 +10548,7 @@ package android.hardware {
package android.hardware.display {
public final class DisplayManager {
+ method public android.hardware.display.VirtualDisplay createPrivateVirtualDisplay(java.lang.String, int, int, int, android.view.Surface);
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
method public android.view.Display[] getDisplays(java.lang.String);
@@ -10562,6 +10563,11 @@ package android.hardware.display {
method public abstract void onDisplayRemoved(int);
}
+ public final class VirtualDisplay {
+ method public android.view.Display getDisplay();
+ method public void release();
+ }
+
}
package android.hardware.input {
@@ -25050,6 +25056,7 @@ package android.view {
method public deprecated int getWidth();
method public boolean isValid();
field public static final int DEFAULT_DISPLAY = 0; // 0x0
+ field public static final int FLAG_PRIVATE = 4; // 0x4
field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0a7a2e7..dcf50cd 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.os.Handler;
import android.util.SparseArray;
import android.view.Display;
+import android.view.Surface;
import java.util.ArrayList;
@@ -134,6 +135,7 @@ public final class DisplayManager {
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
}
return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
} finally {
@@ -275,6 +277,41 @@ public final class DisplayManager {
}
/**
+ * Creates a private virtual display.
+ * <p>
+ * The content of a virtual display is rendered to a {@link Surface} provided
+ * by the application that created the virtual display.
+ * </p><p>
+ * Only the application that created a private virtual display is allowed to
+ * place windows upon it. The private virtual display also does not participate
+ * in display mirroring: it will neither receive mirrored content from another
+ * display nor allow its own content to be mirrored elsewhere. More precisely,
+ * the only processes that are allowed to enumerate or interact with a private
+ * display are those that have the same UID as the application that originally
+ * created the private virtual display.
+ * </p><p>
+ * The private virtual display should be {@link VirtualDisplay#release released}
+ * when no longer needed. Because a private virtual display renders to a surface
+ * provided by the application, it will be released automatically when the
+ * process terminates and all remaining windows on it will be forcibly removed.
+ * </p>
+ *
+ * @param name The name of the virtual display, must be non-empty.
+ * @param width The width of the virtual display in pixels, must be greater than 0.
+ * @param height The height of the virtual display in pixels, must be greater than 0.
+ * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
+ * @param surface The surface to which the content of the virtual display should
+ * be rendered, must be non-null.
+ * @return The newly created virtual display, or null if the application could
+ * not create the virtual display.
+ */
+ public VirtualDisplay createPrivateVirtualDisplay(String name,
+ int width, int height, int densityDpi, Surface surface) {
+ return mGlobal.createPrivateVirtualDisplay(mContext,
+ name, width, height, densityDpi, surface);
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a858681..3ab882d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,17 +18,20 @@ package android.hardware.display;
import android.content.Context;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.CompatibilityInfoHolder;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import java.util.ArrayList;
@@ -315,6 +318,53 @@ public final class DisplayManagerGlobal {
}
}
+ public VirtualDisplay createPrivateVirtualDisplay(Context context, String name,
+ int width, int height, int densityDpi, Surface surface) {
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("name must be non-null and non-empty");
+ }
+ if (width <= 0 || height <= 0 || densityDpi <= 0) {
+ throw new IllegalArgumentException("width, height, and densityDpi must be "
+ + "greater than 0");
+ }
+ if (surface == null) {
+ throw new IllegalArgumentException("surface must not be null");
+ }
+
+ Binder token = new Binder();
+ int displayId;
+ try {
+ displayId = mDm.createPrivateVirtualDisplay(token, context.getPackageName(),
+ name, width, height, densityDpi, surface);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Could not create private virtual display: " + name, ex);
+ return null;
+ }
+ if (displayId < 0) {
+ Log.e(TAG, "Could not create private virtual display: " + name);
+ return null;
+ }
+ Display display = getRealDisplay(displayId);
+ if (display == null) {
+ Log.wtf(TAG, "Could not obtain display info for newly created "
+ + "private virtual display: " + name);
+ try {
+ mDm.releaseVirtualDisplay(token);
+ } catch (RemoteException ex) {
+ }
+ return null;
+ }
+ return new VirtualDisplay(this, display, token);
+ }
+
+ public void releaseVirtualDisplay(IBinder token) {
+ try {
+ mDm.releaseVirtualDisplay(token);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to release virtual display.", ex);
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 79aad78..cd4896a 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -20,6 +20,7 @@ import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
import android.view.DisplayInfo;
+import android.view.Surface;
/** @hide */
interface IDisplayManager {
@@ -46,4 +47,11 @@ interface IDisplayManager {
// No permissions required.
WifiDisplayStatus getWifiDisplayStatus();
+
+ // No permissions required.
+ int createPrivateVirtualDisplay(IBinder token, String packageName,
+ String name, int width, int height, int densityDpi, in Surface surface);
+
+ // No permissions required but must be same Uid as the creator.
+ void releaseVirtualDisplay(in IBinder token);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
new file mode 100644
index 0000000..145a217
--- /dev/null
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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.hardware.display;
+
+import android.os.IBinder;
+import android.view.Display;
+
+/**
+ * Represents a virtual display.
+ *
+ * @see DisplayManager#createPrivateVirtualDisplay
+ */
+public final class VirtualDisplay {
+ private final DisplayManagerGlobal mGlobal;
+ private final Display mDisplay;
+ private IBinder mToken;
+
+ VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token) {
+ mGlobal = global;
+ mDisplay = display;
+ mToken = token;
+ }
+
+ /**
+ * Gets the virtual display.
+ */
+ public Display getDisplay() {
+ return mDisplay;
+ }
+
+ /**
+ * Releases the virtual display and destroys its underlying surface.
+ * <p>
+ * All remaining windows on the virtual display will be forcibly removed
+ * as part of releasing the virtual display.
+ * </p>
+ */
+ public void release() {
+ if (mToken != null) {
+ mGlobal.releaseVirtualDisplay(mToken);
+ mToken = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + "}";
+ }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index e6a7950..4d984fd 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -20,6 +20,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Process;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -57,6 +58,8 @@ public final class Display {
private final int mFlags;
private final int mType;
private final String mAddress;
+ private final int mOwnerUid;
+ private final String mOwnerPackageName;
private final CompatibilityInfoHolder mCompatibilityInfo;
private DisplayInfo mDisplayInfo; // never null
@@ -143,6 +146,18 @@ public final class Display {
public static final int FLAG_SECURE = 1 << 1;
/**
+ * Display flag: Indicates that the display is private. Only the application that
+ * owns the display can create windows on it.
+ * <p>
+ * This flag is associated with displays that were created using
+ * {@link android.hardware.display.DisplayManager#createPrivateVirtualDisplay}.
+ * </p>
+ *
+ * @see #getFlags
+ */
+ public static final int FLAG_PRIVATE = 1 << 2;
+
+ /**
* Display type: Unknown display type.
* @hide
*/
@@ -173,6 +188,12 @@ public final class Display {
public static final int TYPE_OVERLAY = 4;
/**
+ * Display type: Virtual display.
+ * @hide
+ */
+ public static final int TYPE_VIRTUAL = 5;
+
+ /**
* Internal method to create a display.
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
* or {@link android.hardware.display.DisplayManager#getDisplay}
@@ -194,6 +215,8 @@ public final class Display {
mFlags = displayInfo.flags;
mType = displayInfo.type;
mAddress = displayInfo.address;
+ mOwnerUid = displayInfo.ownerUid;
+ mOwnerPackageName = displayInfo.ownerPackageName;
}
/**
@@ -262,6 +285,7 @@ public final class Display {
*
* @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
* @see #FLAG_SECURE
+ * @see #FLAG_PRIVATE
*/
public int getFlags() {
return mFlags;
@@ -277,6 +301,7 @@ public final class Display {
* @see #TYPE_HDMI
* @see #TYPE_WIFI
* @see #TYPE_OVERLAY
+ * @see #TYPE_VIRTUAL
* @hide
*/
public int getType() {
@@ -295,6 +320,32 @@ public final class Display {
}
/**
+ * Gets the UID of the application that owns this display, or zero if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ *
+ * @hide
+ */
+ public int getOwnerUid() {
+ return mOwnerUid;
+ }
+
+ /**
+ * Gets the package name of the application that owns this display, or null if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ *
+ * @hide
+ */
+ public String getOwnerPackageName() {
+ return mOwnerPackageName;
+ }
+
+ /**
* Gets the compatibility info used by this display instance.
*
* @return The compatibility info holder, or null if none is required.
@@ -564,6 +615,22 @@ public final class Display {
}
}
+ /**
+ * Returns true if the specified UID has access to this display.
+ * @hide
+ */
+ public boolean hasAccess(int uid) {
+ return Display.hasAccess(uid, mFlags, mOwnerUid);
+ }
+
+ /** @hide */
+ public static boolean hasAccess(int uid, int flags, int ownerUid) {
+ return (flags & Display.FLAG_PRIVATE) == 0
+ || uid == ownerUid
+ || uid == Process.SYSTEM_UID
+ || uid == 0;
+ }
+
private void updateDisplayInfoLocked() {
// Note: The display manager caches display info objects on our behalf.
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
@@ -624,6 +691,8 @@ public final class Display {
return "WIFI";
case TYPE_OVERLAY:
return "OVERLAY";
+ case TYPE_VIRTUAL:
+ return "VIRTUAL";
default:
return Integer.toString(type);
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 9fcd9b1..1442cb7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -19,6 +19,7 @@ package android.view;
import android.content.res.CompatibilityInfo;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.util.DisplayMetrics;
import libcore.util.Objects;
@@ -177,6 +178,23 @@ public final class DisplayInfo implements Parcelable {
*/
public float physicalYDpi;
+ /**
+ * The UID of the application that owns this display, or zero if it is owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public int ownerUid;
+
+ /**
+ * The package name of the application that owns this display, or null if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public String ownerPackageName;
+
public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -228,7 +246,9 @@ public final class DisplayInfo implements Parcelable {
&& refreshRate == other.refreshRate
&& logicalDensityDpi == other.logicalDensityDpi
&& physicalXDpi == other.physicalXDpi
- && physicalYDpi == other.physicalYDpi;
+ && physicalYDpi == other.physicalYDpi
+ && ownerUid == other.ownerUid
+ && Objects.equal(ownerPackageName, other.ownerPackageName);
}
@Override
@@ -259,6 +279,8 @@ public final class DisplayInfo implements Parcelable {
logicalDensityDpi = other.logicalDensityDpi;
physicalXDpi = other.physicalXDpi;
physicalYDpi = other.physicalYDpi;
+ ownerUid = other.ownerUid;
+ ownerPackageName = other.ownerPackageName;
}
public void readFromParcel(Parcel source) {
@@ -284,6 +306,8 @@ public final class DisplayInfo implements Parcelable {
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
physicalYDpi = source.readFloat();
+ ownerUid = source.readInt();
+ ownerPackageName = source.readString();
}
@Override
@@ -310,6 +334,8 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
dest.writeFloat(physicalYDpi);
+ dest.writeInt(ownerUid);
+ dest.writeString(ownerPackageName);
}
@Override
@@ -335,6 +361,13 @@ public final class DisplayInfo implements Parcelable {
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, CompatibilityInfoHolder cih,
int width, int height) {
outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi;
@@ -402,8 +435,13 @@ public final class DisplayInfo implements Parcelable {
sb.append(layerStack);
sb.append(", type ");
sb.append(Display.typeToString(type));
- sb.append(", address ");
- sb.append(address);
+ if (address != null) {
+ sb.append(", address ").append(address);
+ }
+ 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();
@@ -417,6 +455,9 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
result.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
+ if ((flags & Display.FLAG_PRIVATE) != 0) {
+ result.append(", FLAG_PRIVATE");
+ }
return result.toString();
}
}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index 247d8a0..11f8d6a 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -61,6 +61,18 @@ final class DisplayDeviceInfo {
public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1 << 3;
/**
+ * Flag: Indicates that the display device is owned by a particular application
+ * and that no other application should be able to interact with it.
+ */
+ public static final int FLAG_PRIVATE = 1 << 4;
+
+ /**
+ * Flag: Indicates that the display device is not blanked automatically by
+ * the power manager.
+ */
+ public static final int FLAG_NEVER_BLANK = 1 << 5;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
@@ -150,6 +162,23 @@ final class DisplayDeviceInfo {
*/
public String address;
+ /**
+ * The UID of the application that owns this display, or zero if it is owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public int ownerUid;
+
+ /**
+ * The package name of the application that owns this display, or null if it is
+ * owned by the system.
+ * <p>
+ * If the display is private, then only the owner can use it.
+ * </p>
+ */
+ public String ownerPackageName;
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -176,7 +205,9 @@ final class DisplayDeviceInfo {
&& touch == other.touch
&& rotation == other.rotation
&& type == other.type
- && Objects.equal(address, other.address);
+ && Objects.equal(address, other.address)
+ && ownerUid == other.ownerUid
+ && Objects.equal(ownerPackageName, other.ownerPackageName);
}
@Override
@@ -197,19 +228,32 @@ final class DisplayDeviceInfo {
rotation = other.rotation;
type = other.type;
address = other.address;
+ ownerUid = other.ownerUid;
+ ownerPackageName = other.ownerPackageName;
}
// For debugging purposes
@Override
public String toString() {
- return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", "
- + refreshRate + " fps, "
- + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
- + ", touch " + touchToString(touch) + flagsToString(flags)
- + ", rotation " + rotation
- + ", type " + Display.typeToString(type)
- + ", address " + address
- + "}";
+ StringBuilder sb = new StringBuilder();
+ sb.append("DisplayDeviceInfo{\"");
+ sb.append(name).append("\": ").append(width).append(" x ").append(height);
+ sb.append(", ").append(refreshRate).append(" fps, ");
+ sb.append("density ").append(densityDpi);
+ sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
+ sb.append(", touch ").append(touchToString(touch));
+ sb.append(", rotation ").append(rotation);
+ sb.append(", type ").append(Display.typeToString(type));
+ if (address != null) {
+ sb.append(", address ").append(address);
+ }
+ 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 touchToString(int touch) {
@@ -239,6 +283,12 @@ final class DisplayDeviceInfo {
if ((flags & FLAG_SUPPORTS_PROTECTED_BUFFERS) != 0) {
msg.append(", FLAG_SUPPORTS_PROTECTED_BUFFERS");
}
+ if ((flags & FLAG_PRIVATE) != 0) {
+ msg.append(", FLAG_PRIVATE");
+ }
+ if ((flags & FLAG_NEVER_BLANK) != 0) {
+ msg.append(", FLAG_NEVER_BLANK");
+ }
return msg.toString();
}
}
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index ca85e42..c339c26 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -33,15 +33,19 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
+
import com.android.server.UiThread;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -171,6 +175,9 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
// The Wifi display adapter, or null if not registered.
private WifiDisplayAdapter mWifiDisplayAdapter;
+ // The virtual display adapter, or null if not registered.
+ private VirtualDisplayAdapter mVirtualDisplayAdapter;
+
// Viewports of the default display and the display that should receive touch
// input from an external source. Used by the input system.
private final DisplayViewport mDefaultViewport = new DisplayViewport();
@@ -363,13 +370,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
synchronized (mSyncRoot) {
if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) {
mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED;
-
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
- device.blankLocked();
- }
-
+ updateAllDisplayBlankingLocked();
scheduleTraversalLocked(false);
}
}
@@ -382,13 +383,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
synchronized (mSyncRoot) {
if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) {
mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED;
-
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- DisplayDevice device = mDisplayDevices.get(i);
- device.unblankLocked();
- }
-
+ updateAllDisplayBlankingLocked();
scheduleTraversalLocked(false);
}
}
@@ -403,12 +398,21 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
*/
@Override // Binder call
public DisplayInfo getDisplayInfo(int displayId) {
- synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplays.get(displayId);
- if (display != null) {
- return display.getDisplayInfoLocked();
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ DisplayInfo info = display.getDisplayInfoLocked();
+ if (info.hasAccess(callingUid)) {
+ return info;
+ }
+ }
+ return null;
}
- return null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -417,13 +421,27 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
*/
@Override // Binder call
public int[] getDisplayIds() {
- synchronized (mSyncRoot) {
- final int count = mLogicalDisplays.size();
- int[] displayIds = new int[count];
- for (int i = 0; i < count; i++) {
- displayIds[i] = mLogicalDisplays.keyAt(i);
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ final int count = mLogicalDisplays.size();
+ int[] displayIds = new int[count];
+ int n = 0;
+ for (int i = 0; i < count; i++) {
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ DisplayInfo info = display.getDisplayInfoLocked();
+ if (info.hasAccess(callingUid)) {
+ displayIds[n++] = mLogicalDisplays.keyAt(i);
+ }
+ }
+ if (n != count) {
+ displayIds = Arrays.copyOfRange(displayIds, 0, n);
+ }
+ return displayIds;
}
- return displayIds;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -570,6 +588,95 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
== PackageManager.PERMISSION_GRANTED;
}
+ @Override // Binder call
+ public int createPrivateVirtualDisplay(IBinder appToken, String packageName,
+ String name, int width, int height, int densityDpi, Surface surface) {
+ final int callingUid = Binder.getCallingUid();
+ if (!validatePackageName(callingUid, packageName)) {
+ throw new SecurityException("packageName must match the calling uid");
+ }
+ if (appToken == null) {
+ throw new IllegalArgumentException("appToken must not be null");
+ }
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("name must be non-null and non-empty");
+ }
+ if (width <= 0 || height <= 0 || densityDpi <= 0) {
+ throw new IllegalArgumentException("width, height, and densityDpi must be "
+ + "greater than 0");
+ }
+ if (surface == null) {
+ throw new IllegalArgumentException("surface must not be null");
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ Slog.w(TAG, "Rejecting request to create private virtual display "
+ + "because the virtual display adapter is not available.");
+ return -1;
+ }
+
+ DisplayDevice device = mVirtualDisplayAdapter.createPrivateVirtualDisplayLocked(
+ appToken, callingUid, packageName, name, width, height, densityDpi,
+ surface);
+ if (device == null) {
+ return -1;
+ }
+
+ handleDisplayDeviceAddedLocked(device);
+ LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+ if (display != null) {
+ return display.getDisplayIdLocked();
+ }
+
+ // Something weird happened and the logical display was not created.
+ Slog.w(TAG, "Rejecting request to create private virtual display "
+ + "because the logical display was not created.");
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
+ handleDisplayDeviceRemovedLocked(device);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return -1;
+ }
+
+ @Override // Binder call
+ public void releaseVirtualDisplay(IBinder appToken) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ return;
+ }
+
+ DisplayDevice device =
+ mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
+ if (device != null) {
+ handleDisplayDeviceRemovedLocked(device);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private boolean validatePackageName(int uid, String packageName) {
+ if (packageName != null) {
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packageNames != null) {
+ for (String n : packageNames) {
+ if (n.equals(packageName)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
private void registerDefaultDisplayAdapter() {
// Register default display adapter.
synchronized (mSyncRoot) {
@@ -588,6 +695,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
registerOverlayDisplayAdapterLocked();
registerWifiDisplayAdapterLocked();
+ registerVirtualDisplayAdapterLocked();
}
}
}
@@ -608,6 +716,12 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
}
}
+ private void registerVirtualDisplayAdapterLocked() {
+ mVirtualDisplayAdapter = new VirtualDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener);
+ registerDisplayAdapterLocked(mVirtualDisplayAdapter);
+ }
+
private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
// In safe mode, we disable non-essential display adapters to give the user
// an opportunity to fix broken settings or other problems that might affect
@@ -625,29 +739,23 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
private void handleDisplayDeviceAdded(DisplayDevice device) {
synchronized (mSyncRoot) {
- if (mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to add already added display device: "
- + device.getDisplayDeviceInfoLocked());
- return;
- }
+ handleDisplayDeviceAddedLocked(device);
+ }
+ }
- Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+ private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
+ if (mDisplayDevices.contains(device)) {
+ Slog.w(TAG, "Attempted to add already added display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
- mDisplayDevices.add(device);
- addLogicalDisplayLocked(device);
- scheduleTraversalLocked(false);
+ Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
- // Blank or unblank the display immediately to match the state requested
- // by the power manager (if known).
- switch (mAllDisplayBlankStateFromPowerManager) {
- case DISPLAY_BLANK_STATE_BLANKED:
- device.blankLocked();
- break;
- case DISPLAY_BLANK_STATE_UNBLANKED:
- device.unblankLocked();
- break;
- }
- }
+ mDisplayDevices.add(device);
+ addLogicalDisplayLocked(device);
+ updateDisplayBlankingLocked(device);
+ scheduleTraversalLocked(false);
}
private void handleDisplayDeviceChanged(DisplayDevice device) {
@@ -669,17 +777,44 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
private void handleDisplayDeviceRemoved(DisplayDevice device) {
synchronized (mSyncRoot) {
- if (!mDisplayDevices.remove(device)) {
- Slog.w(TAG, "Attempted to remove non-existent display device: "
- + device.getDisplayDeviceInfoLocked());
- return;
- }
+ handleDisplayDeviceRemovedLocked(device);
+ }
+ }
+ private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
+ if (!mDisplayDevices.remove(device)) {
+ Slog.w(TAG, "Attempted to remove non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
+ return;
+ }
- Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
+ Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
- mRemovedDisplayDevices.add(device);
- updateLogicalDisplaysLocked();
- scheduleTraversalLocked(false);
+ mRemovedDisplayDevices.add(device);
+ updateLogicalDisplaysLocked();
+ scheduleTraversalLocked(false);
+ }
+
+ private void updateAllDisplayBlankingLocked() {
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ DisplayDevice device = mDisplayDevices.get(i);
+ updateDisplayBlankingLocked(device);
+ }
+ }
+
+ private void updateDisplayBlankingLocked(DisplayDevice device) {
+ // Blank or unblank the display immediately to match the state requested
+ // by the power manager (if known).
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ switch (mAllDisplayBlankStateFromPowerManager) {
+ case DISPLAY_BLANK_STATE_BLANKED:
+ device.blankLocked();
+ break;
+ case DISPLAY_BLANK_STATE_UNBLANKED:
+ device.unblankLocked();
+ break;
+ }
}
}
@@ -812,13 +947,21 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
}
private void configureDisplayInTransactionLocked(DisplayDevice device) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ boolean isPrivate = (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0;
+
// Find the logical display that the display device is showing.
+ // Private displays never mirror other displays.
LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
- if (display != null && !display.hasContentLocked()) {
- display = null;
- }
- if (display == null) {
- display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ if (!isPrivate) {
+ if (display != null && !display.hasContentLocked()) {
+ // If the display does not have any content of its own, then
+ // automatically mirror the default logical display contents.
+ display = null;
+ }
+ if (display == null) {
+ display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ }
}
// Apply the logical display configuration to the display device.
@@ -828,11 +971,11 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
+ device.getDisplayDeviceInfoLocked());
return;
}
- boolean isBlanked = (mAllDisplayBlankStateFromPowerManager == DISPLAY_BLANK_STATE_BLANKED);
+ boolean isBlanked = (mAllDisplayBlankStateFromPowerManager == DISPLAY_BLANK_STATE_BLANKED)
+ && (info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0;
display.configureDisplayInTransactionLocked(device, isBlanked);
// Update the viewports if needed.
- DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if (!mDefaultViewport.valid
&& (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
setViewportLocked(mDefaultViewport, display, device);
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index 424ec36..775ebb2 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -202,6 +202,9 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_SECURE;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_PRIVATE;
+ }
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
@@ -218,6 +221,8 @@ final class LogicalDisplay {
mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
+ mBaseDisplayInfo.ownerUid = deviceInfo.ownerUid;
+ mBaseDisplayInfo.ownerPackageName = deviceInfo.ownerPackageName;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo = null;
diff --git a/services/java/com/android/server/display/VirtualDisplayAdapter.java b/services/java/com/android/server/display/VirtualDisplayAdapter.java
new file mode 100644
index 0000000..634fba7
--- /dev/null
+++ b/services/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 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.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * A display adapter that provides virtual displays on behalf of applications.
+ * <p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class VirtualDisplayAdapter extends DisplayAdapter {
+ static final String TAG = "VirtualDisplayAdapter";
+ static final boolean DEBUG = false;
+
+ private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
+ new ArrayMap<IBinder, VirtualDisplayDevice>();
+
+ // Called with SyncRoot lock held.
+ public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
+ }
+
+ public DisplayDevice createPrivateVirtualDisplayLocked(IBinder appToken,
+ int ownerUid, String ownerPackageName,
+ String name, int width, int height, int densityDpi, Surface surface) {
+ IBinder displayToken = SurfaceControl.createDisplay(name, false /*secure*/);
+ VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
+ ownerUid, ownerPackageName, name, width, height, densityDpi, surface);
+
+ try {
+ appToken.linkToDeath(device, 0);
+ } catch (RemoteException ex) {
+ device.releaseLocked();
+ return null;
+ }
+
+ mVirtualDisplayDevices.put(appToken, device);
+
+ // Return the display device without actually sending the event indicating
+ // that it was added. The caller will handle it.
+ return device;
+ }
+
+ public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+ if (device != null) {
+ appToken.unlinkToDeath(device, 0);
+ }
+
+ // Return the display device that was removed without actually sending the
+ // event indicating that it was removed. The caller will handle it.
+ return device;
+ }
+
+ private void handleBinderDiedLocked(IBinder appToken) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
+ if (device != null) {
+ Slog.i(TAG, "Virtual display device released because application token died: "
+ + device.mOwnerPackageName);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+
+ private final class VirtualDisplayDevice extends DisplayDevice
+ implements DeathRecipient {
+ private final IBinder mAppToken;
+ private final int mOwnerUid;
+ final String mOwnerPackageName;
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+
+ private boolean mReleased;
+ private Surface mSurface;
+ private DisplayDeviceInfo mInfo;
+
+ public VirtualDisplayDevice(IBinder displayToken,
+ IBinder appToken, int ownerUid, String ownerPackageName,
+ String name, int width, int height, int densityDpi, Surface surface) {
+ super(VirtualDisplayAdapter.this, displayToken);
+ mAppToken = appToken;
+ mOwnerUid = ownerUid;
+ mOwnerPackageName = ownerPackageName;
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mSurface = surface;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (getSyncRoot()) {
+ if (!mReleased) {
+ handleBinderDiedLocked(mAppToken);
+ }
+ }
+ }
+
+ public void releaseLocked() {
+ mReleased = true;
+ sendTraversalRequestLocked();
+ }
+
+ @Override
+ public void performTraversalInTransactionLocked() {
+ if (mReleased && mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ setSurfaceInTransactionLocked(mSurface);
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = mName;
+ mInfo.width = mWidth;
+ mInfo.height = mHeight;
+ mInfo.refreshRate = 60;
+ mInfo.densityDpi = mDensityDpi;
+ mInfo.xDpi = mDensityDpi;
+ mInfo.yDpi = mDensityDpi;
+ mInfo.flags = DisplayDeviceInfo.FLAG_PRIVATE | DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ mInfo.type = Display.TYPE_VIRTUAL;
+ mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+ mInfo.ownerUid = mOwnerUid;
+ mInfo.ownerPackageName = mOwnerPackageName;
+ }
+ return mInfo;
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 82e8c7f..feac370 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -140,6 +140,13 @@ class DisplayContent {
return mDisplayInfo;
}
+ /**
+ * Returns true if the specified UID has access to this display.
+ */
+ public boolean hasAccess(int uid) {
+ return mDisplay.hasAccess(uid);
+ }
+
boolean homeOnTop() {
return mStackBoxes.get(0).mStack != mHomeStack;
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index faeb37c..afbc4a9 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2105,6 +2105,13 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent == null) {
+ Slog.w(TAG, "Attempted to add window to a display that does not exist: "
+ + displayId + ". Aborting.");
+ return WindowManagerGlobal.ADD_INVALID_DISPLAY;
+ }
+ if (!displayContent.hasAccess(session.mUid)) {
+ Slog.w(TAG, "Attempted to add window to a display for which the application "
+ + "does not have access: " + displayId + ". Aborting.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
@@ -7521,7 +7528,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void getInitialDisplaySize(int displayId, Point size) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
size.x = displayContent.mInitialDisplayWidth;
size.y = displayContent.mInitialDisplayHeight;
@@ -7534,7 +7541,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void getBaseDisplaySize(int displayId, Point size) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
size.x = displayContent.mBaseDisplayWidth;
size.y = displayContent.mBaseDisplayHeight;
@@ -7649,7 +7656,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int getInitialDisplayDensity(int displayId) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
return displayContent.mInitialDisplayDensity;
}
@@ -7662,7 +7669,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int getBaseDisplayDensity(int displayId) {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent != null) {
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
synchronized(displayContent.mDisplaySizeLock) {
return displayContent.mBaseDisplayDensity;
}