diff options
author | Jeff Brown <jeffbrown@google.com> | 2013-06-18 22:36:28 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-06-18 22:36:29 +0000 |
commit | 9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0 (patch) | |
tree | e5914567c4e5cc6604e9590dd255ca7aba22478b | |
parent | 602c0d8b499aa9593e5713cb92f15c45cc9f9a54 (diff) | |
parent | a506a6ec94863a35acca9feb165db76ddac3892c (diff) | |
download | frameworks_base-9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0.zip frameworks_base-9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0.tar.gz frameworks_base-9fd6d4e7dc576a5146469c25b12d43b30e8a3cd0.tar.bz2 |
Merge "Add an API to allow for creating private virtual displays."
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; } |