From 92207df753c27b094e9e0ca80d41bc0d54dc6bd5 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 16 Apr 2014 13:16:07 -0700 Subject: [PATCH] Add support for dynamically setting the virtual display surface. Previously, the surface that backs a virtual display had to be set at the time when the display was created. This change now makes it possible to set or remove the surface later. The virtual display is treated as if it were "off" while no surface is attached to it. Change-Id: Ib4fdbbb8b4ee79f0fb9ceb648f9bda4a8fa6a2ca --- api/current.txt | 2 + .../hardware/display/DisplayManager.java | 10 ++++- .../display/DisplayManagerGlobal.java | 13 ++++-- .../hardware/display/IDisplayManager.aidl | 3 ++ .../hardware/display/VirtualDisplay.java | 43 ++++++++++++++++--- .../server/display/DisplayManagerService.java | 23 ++++++++-- .../server/display/VirtualDisplayAdapter.java | 19 ++++++++ 7 files changed, 100 insertions(+), 13 deletions(-) diff --git a/api/current.txt b/api/current.txt index 554ef8aa0a247..a3824d7221646 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10950,7 +10950,9 @@ package android.hardware.display { public final class VirtualDisplay { method public android.view.Display getDisplay(); + method public android.view.Surface getSurface(); method public void release(); + method public void setSurface(android.view.Surface); } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index a517bc5d71c6f..79673b32d1993 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -437,6 +437,14 @@ public final class DisplayManager { * The behavior of the virtual display depends on the flags that are provided * to this method. By default, virtual displays are created to be private, * non-presentation and unsecure. Permissions may be required to use certain flags. + *

+ * As of {@link android.os.Build.VERSION_CODES#KITKAT_WATCH}, the surface may + * be attached or detached dynamically using {@link VirtualDisplay#setSurface}. + * Previously, the surface had to be non-null when {@link #createVirtualDisplay} + * was called and could not be changed for the lifetime of the display. + *

+ * Detaching the surface that backs a virtual display has a similar effect to + * turning off the screen. *

* * @param name The name of the virtual display, must be non-empty. @@ -444,7 +452,7 @@ public final class DisplayManager { * @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. + * be rendered, or null if there is none initially. * @param flags A combination of virtual display flags: * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, or {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 34174301ac8eb..a8d55e83a24a3 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -377,9 +377,6 @@ public final class DisplayManagerGlobal { 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; @@ -404,7 +401,15 @@ public final class DisplayManagerGlobal { } return null; } - return new VirtualDisplay(this, display, token); + return new VirtualDisplay(this, display, token, surface); + } + + public void setVirtualDisplaySurface(IBinder token, Surface surface) { + try { + mDm.setVirtualDisplaySurface(token, surface); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to set virtual display surface.", ex); + } } public void releaseVirtualDisplay(IBinder token) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 68eb13fb6d58d..23c58c84dadba 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -62,6 +62,9 @@ interface IDisplayManager { int createVirtualDisplay(IBinder token, String packageName, String name, int width, int height, int densityDpi, in Surface surface, int flags); + // No permissions required but must be same Uid as the creator. + void setVirtualDisplaySurface(in IBinder token, 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 index 01e5bac01d850..691d6a076ae03 100644 --- a/core/java/android/hardware/display/VirtualDisplay.java +++ b/core/java/android/hardware/display/VirtualDisplay.java @@ -17,15 +17,18 @@ package android.hardware.display; import android.os.IBinder; import android.view.Display; +import android.view.Surface; /** * Represents a virtual display. The content of a virtual display is rendered to a * {@link android.view.Surface} that you must provide to {@link DisplayManager#createVirtualDisplay * createVirtualDisplay()}. - *

Because a virtual display renders to a surface provided by the application, it will be + *

+ * Because a 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. However, you should also explicitly call {@link #release} when you're - * done with it. + * be forcibly removed. However, you should also explicitly call {@link #release} when + * you're done with it. + *

* * @see DisplayManager#createVirtualDisplay */ @@ -33,11 +36,14 @@ public final class VirtualDisplay { private final DisplayManagerGlobal mGlobal; private final Display mDisplay; private IBinder mToken; + private Surface mSurface; - VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token) { + VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token, + Surface surface) { mGlobal = global; mDisplay = display; mToken = token; + mSurface = surface; } /** @@ -47,6 +53,32 @@ public final class VirtualDisplay { return mDisplay; } + /** + * Gets the surface that backs the virtual display. + */ + public Surface getSurface() { + return mSurface; + } + + /** + * Sets the surface that backs the virtual display. + *

+ * Detaching the surface that backs a virtual display has a similar effect to + * turning off the screen. + *

+ * It is still the caller's responsibility to destroy the surface after it has + * been detached. + *

+ * + * @param surface The surface to set, or null to detach the surface from the virtual display. + */ + public void setSurface(Surface surface) { + if (mSurface != surface) { + mGlobal.setVirtualDisplaySurface(mToken, surface); + mSurface = surface; + } + } + /** * Releases the virtual display and destroys its underlying surface. *

@@ -63,6 +95,7 @@ public final class VirtualDisplay { @Override public String toString() { - return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + "}"; + return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + + ", surface=" + mSurface + "}"; } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 071417bd63fe9..6697b60891e68 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -517,6 +517,16 @@ public final class DisplayManagerService extends SystemService { return -1; } + private void setVirtualDisplaySurfaceInternal(IBinder appToken, Surface surface) { + synchronized (mSyncRoot) { + if (mVirtualDisplayAdapter == null) { + return; + } + + mVirtualDisplayAdapter.setVirtualDisplaySurfaceLocked(appToken, surface); + } + } + private void releaseVirtualDisplayInternal(IBinder appToken) { synchronized (mSyncRoot) { if (mVirtualDisplayAdapter == null) { @@ -1221,9 +1231,6 @@ public final class DisplayManagerService extends SystemService { throw new IllegalArgumentException("width, height, and densityDpi must be " + "greater than 0"); } - if (surface == null) { - throw new IllegalArgumentException("surface must not be null"); - } if (callingUid != Process.SYSTEM_UID && (flags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { if (mContext.checkCallingPermission(android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) @@ -1254,6 +1261,16 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public void setVirtualDisplaySurface(IBinder appToken, Surface surface) { + final long token = Binder.clearCallingIdentity(); + try { + setVirtualDisplaySurfaceInternal(appToken, surface); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override // Binder call public void releaseVirtualDisplay(IBinder appToken) { final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 95ca0d2b4b433..a165f26248ad8 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -69,6 +69,13 @@ final class VirtualDisplayAdapter extends DisplayAdapter { return device; } + public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) { + VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); + if (device != null) { + device.setSurfaceLocked(surface); + } + } + public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); if (device != null) { @@ -144,6 +151,17 @@ final class VirtualDisplayAdapter extends DisplayAdapter { } } + public void setSurfaceLocked(Surface surface) { + if (mSurface != surface) { + if ((mSurface != null) != (surface != null)) { + sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); + } + sendTraversalRequestLocked(); + mSurface = surface; + mInfo = null; + } + } + @Override public DisplayDeviceInfo getDisplayDeviceInfoLocked() { if (mInfo == null) { @@ -171,6 +189,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { } mInfo.type = Display.TYPE_VIRTUAL; mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; + mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF; mInfo.ownerUid = mOwnerUid; mInfo.ownerPackageName = mOwnerPackageName; }