Added turn on/off virtual display

VirtualDisplays are considered on if they have a surface and off if they don't.
This causes problems for displays that don't have a surface. Instead,
create a separate API to allow the client to request the surface be on
or off.

To ensure backwards compatibility for VirtualDisplays, set the display
to on or off when the surface is added or removed.

Change-Id: If9d8db94a66d6484ac492a53c1cd8fb7da851b88
Fixes: 119209373
Test: Created surfaceless virtual display that can be manually turned on or off
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 680fed8..d079bdd 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -16,6 +16,10 @@
 
 package android.app;
 
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager.StackInfo;
@@ -32,7 +36,6 @@
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
@@ -81,7 +84,6 @@
     private boolean mOpened; // Protected by mGuard.
 
     private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private Surface mTmpSurface = new Surface();
 
     @UnsupportedAppUsage
     public ActivityView(Context context) {
@@ -309,20 +311,20 @@
     private class SurfaceCallback implements SurfaceHolder.Callback {
         @Override
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
-            mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
                 initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
             } else {
-                // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-                // whether it has a surface. Setting a fake surface here so DisplayManager will
-                // consider this display on.
-                mVirtualDisplay.setSurface(mTmpSurface);
                 mTmpTransaction.reparent(mRootSurfaceControl,
                         mSurfaceView.getSurfaceControl().getHandle()).apply();
             }
+
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.setDisplayState(true);
+            }
+
             updateLocation();
         }
 
@@ -336,10 +338,8 @@
 
         @Override
         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
-            mTmpSurface.release();
-            mTmpSurface = null;
             if (mVirtualDisplay != null) {
-                mVirtualDisplay.setSurface(null);
+                mVirtualDisplay.setDisplayState(false);
             }
             cleanTapExcludeRegion();
         }
@@ -354,15 +354,11 @@
         final int height = mSurfaceView.getHeight();
         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
 
-        // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-        // whether it has a surface. Setting a fake surface here so DisplayManager will consider
-        // this display on.
         mVirtualDisplay = displayManager.createVirtualDisplay(
-                DISPLAY_NAME + "@" + System.identityHashCode(this),
-                width, height, getBaseDisplayDensity(), mTmpSurface,
-                DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
+                DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
+                getBaseDisplayDensity(), null,
+                VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+                        | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
         if (mVirtualDisplay == null) {
             Log.e(TAG, "Failed to initialize ActivityView");
             return;
@@ -423,11 +419,6 @@
             displayReleased = false;
         }
 
-        if (mTmpSurface != null) {
-            mTmpSurface.release();
-            mTmpSurface = null;
-        }
-
         if (displayReleased && mActivityViewCallback != null) {
             mActivityViewCallback.onActivityViewDestroyed(this);
         }
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7304ab4..00ab72d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -447,6 +447,7 @@
     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
         try {
             mDm.setVirtualDisplaySurface(token, surface);
+            setVirtualDisplayState(token, surface != null);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -469,6 +470,14 @@
         }
     }
 
+    void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
+        try {
+            mDm.setVirtualDisplayState(token, isOn);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Gets the stable device display size, in pixels.
      */
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b575997..edd37d0 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -85,6 +85,9 @@
     // No permissions required but must be same Uid as the creator.
     void releaseVirtualDisplay(in IVirtualDisplayCallback token);
 
+    // No permissions required but must be same Uid as the creator.
+    void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn);
+
     // Get a stable metric for the device's display size. No permissions required.
     Point getStableDisplaySize();
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index d354666..bf62c95 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -104,6 +104,18 @@
         }
     }
 
+    /**
+     * Sets the on/off state for a virtual display.
+     *
+     * @param isOn Whether the display should be on or off.
+     * @hide
+     */
+    public void setDisplayState(boolean isOn) {
+        if (mToken != null) {
+            mGlobal.setVirtualDisplayState(mToken, isOn);
+        }
+    }
+
     @Override
     public String toString() {
         return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b1ba05c..4dcbc24 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -812,6 +812,16 @@
         }
     }
 
+    private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) {
+        synchronized (mSyncRoot) {
+            if (mVirtualDisplayAdapter == null) {
+                return;
+            }
+
+            mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn);
+        }
+    }
+
     private void registerDefaultDisplayAdapters() {
         // Register default display adapters.
         synchronized (mSyncRoot) {
@@ -1956,6 +1966,16 @@
         }
 
         @Override // Binder call
+        public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setVirtualDisplayStateInternal(callback.asBinder(), isOn);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 5aa585f..1ca8dd3 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -147,6 +147,13 @@
         return device;
     }
 
+    void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+        if (device != null) {
+            device.setDisplayState(isOn);
+        }
+    }
+
     /**
      * Returns the next unique index for the uniqueIdPrefix
      */
@@ -206,6 +213,7 @@
         private int mPendingChanges;
         private int mUniqueIndex;
         private Display.Mode mMode;
+        private boolean mIsDisplayOn;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
@@ -226,6 +234,7 @@
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
             mUniqueIndex = uniqueIndex;
+            mIsDisplayOn = surface != null;
         }
 
         @Override
@@ -304,6 +313,14 @@
             }
         }
 
+        void setDisplayState(boolean isOn) {
+            if (mIsDisplayOn != isOn) {
+                mIsDisplayOn = isOn;
+                mInfo = null;
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+            }
+        }
+
         public void stopLocked() {
             setSurfaceLocked(null);
             mStopped = true;
@@ -375,7 +392,9 @@
                 mInfo.type = Display.TYPE_VIRTUAL;
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
-                mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
+
+                mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF;
+
                 mInfo.ownerUid = mOwnerUid;
                 mInfo.ownerPackageName = mOwnerPackageName;
             }