am 18a69b93: am 8e1e4006: Merge "Add support for dynamically setting the virtual display surface." into klp-modular-dev

* commit '18a69b936afa6bdb8c65b6b582989c4dcdac45ee':
  Add support for dynamically setting the virtual display surface.
diff --git a/api/current.txt b/api/current.txt
index a0a5049..5155c89 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12120,7 +12120,9 @@
 
   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 a517bc5..79673b3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -437,6 +437,14 @@
      * 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.
+     * </p><p>
+     * 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.
+     * </p><p>
+     * Detaching the surface that backs a virtual display has a similar effect to
+     * turning off the screen.
      * </p>
      *
      * @param name The name of the virtual display, must be non-empty.
@@ -444,7 +452,7 @@
      * @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 3417430..a8d55e8 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -377,9 +377,6 @@
             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 @@
             }
             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 68eb13f..23c58c8 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -63,5 +63,8 @@
             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 01e5bac..691d6a0 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -17,15 +17,18 @@
 
 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()}.
- * <p>Because a virtual display renders to a surface provided by the application, it will be
+ * <p>
+ * 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.
+ * </p>
  *
  * @see DisplayManager#createVirtualDisplay
  */
@@ -33,11 +36,14 @@
     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;
     }
 
     /**
@@ -48,6 +54,32 @@
     }
 
     /**
+     * Gets the surface that backs the virtual display.
+     */
+    public Surface getSurface() {
+        return mSurface;
+    }
+
+    /**
+     * Sets the surface that backs the virtual display.
+     * <p>
+     * Detaching the surface that backs a virtual display has a similar effect to
+     * turning off the screen.
+     * </p><p>
+     * It is still the caller's responsibility to destroy the surface after it has
+     * been detached.
+     * </p>
+     *
+     * @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.
      * <p>
      * All remaining windows on the virtual display will be forcibly removed
@@ -63,6 +95,7 @@
 
     @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 071417b..6697b60 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 @@
         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 @@
                 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)
@@ -1255,6 +1262,16 @@
         }
 
         @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();
             try {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 95ca0d2..a165f26 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 @@
         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 @@
             }
         }
 
+        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 @@
                 }
                 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;
             }