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; }