Add resize method for virtual displays

Change-Id: I2632fc56c2d2cba356379e42f5c1a3e283b11d1e
diff --git a/api/current.txt b/api/current.txt
index 5469c6f..a8b60ed 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13247,6 +13247,7 @@
     method public android.view.Display getDisplay();
     method public android.view.Surface getSurface();
     method public void release();
+    method public void resize(int, int, int);
     method public void setSurface(android.view.Surface);
   }
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1b9a0c5..8b44f3b 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -416,6 +416,15 @@
         }
     }
 
+    public void resizeVirtualDisplay(IVirtualDisplayCallbacks token,
+            int width, int height, int densityDpi) {
+        try {
+            mDm.resizeVirtualDisplay(token, width, height, densityDpi);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Failed to resize virtual display.", ex);
+        }
+    }
+
     public void releaseVirtualDisplay(IVirtualDisplayCallbacks token) {
         try {
             mDm.releaseVirtualDisplay(token);
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 44ffbc4..cfaa5a0 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -65,6 +65,10 @@
             in IMediaProjection projectionToken, 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 resizeVirtualDisplay(in IVirtualDisplayCallbacks token,
+            int width, int height, int densityDpi);
+
     // No permissions required but must be same Uid as the creator.
     void setVirtualDisplaySurface(in IVirtualDisplayCallbacks token, in Surface surface);
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index df6116b..1dd6978 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -80,6 +80,18 @@
     }
 
     /**
+     * Asks the virtual display to resize.
+     *<p>
+     * This is really just a convenience to allow applications using
+     * virtual displays to adapt to changing conditions without having
+     * to tear down and recreate the display.
+     * </p>
+     */
+    public void resize(int width, int height, int densityDpi) {
+        mGlobal.resizeVirtualDisplay(mToken, width, height, densityDpi);
+    }
+
+    /**
      * Releases the virtual display and destroys its underlying surface.
      * <p>
      * All remaining windows on the virtual display will be forcibly removed
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 191ad64..1e28e33 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -74,6 +74,7 @@
             IBinder displayToken, int orientation,
             int l, int t, int r, int b,
             int L, int T, int R, int B);
+    private static native void nativeSetDisplaySize(IBinder displayToken, int width, int height);
     private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
             IBinder displayToken);
     private static native int nativeGetActiveConfig(IBinder displayToken);
@@ -588,6 +589,17 @@
         }
     }
 
+    public static void setDisplaySize(IBinder displayToken, int width, int height) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width and height must be positive");
+        }
+
+        nativeSetDisplaySize(displayToken, width, height);
+    }
+
     public static IBinder createDisplay(String name, boolean secure) {
         if (name == null) {
             throw new IllegalArgumentException("name must not be null");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9783e91..3fb084a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -369,6 +369,13 @@
     SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect);
 }
 
+static void nativeSetDisplaySize(JNIEnv* env, jclass clazz,
+        jobject tokenObj, jint width, jint height) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == NULL) return;
+    SurfaceComposerClient::setDisplaySize(token, width, height);
+}
+
 static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz,
         jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
@@ -620,6 +627,8 @@
             (void*)nativeSetDisplayLayerStack },
     {"nativeSetDisplayProjection", "(Landroid/os/IBinder;IIIIIIIII)V",
             (void*)nativeSetDisplayProjection },
+    {"nativeSetDisplaySize", "(Landroid/os/IBinder;II)V",
+            (void*)nativeSetDisplaySize },
     {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo;",
             (void*)nativeGetDisplayConfigs },
     {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b9acea5..7d3738c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -523,6 +523,17 @@
         return -1;
     }
 
+    private void resizeVirtualDisplayInternal(IBinder appToken,
+            int width, int height, int densityDpi) {
+        synchronized (mSyncRoot) {
+            if (mVirtualDisplayAdapter == null) {
+                return;
+            }
+
+            mVirtualDisplayAdapter.resizeVirtualDisplayLocked(appToken, width, height, densityDpi);
+        }
+    }
+
     private void setVirtualDisplaySurfaceInternal(IBinder appToken, Surface surface) {
         synchronized (mSyncRoot) {
             if (mVirtualDisplayAdapter == null) {
@@ -1304,6 +1315,17 @@
         }
 
         @Override // Binder call
+        public void resizeVirtualDisplay(IVirtualDisplayCallbacks callbacks,
+                int width, int height, int densityDpi) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                resizeVirtualDisplayInternal(callbacks.asBinder(), width, height, densityDpi);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setVirtualDisplaySurface(IVirtualDisplayCallbacks callbacks, Surface surface) {
             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 1032081..0ebd2de 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -83,6 +83,15 @@
         return device;
     }
 
+    public void resizeVirtualDisplayLocked(IBinder appToken,
+            int width, int height, int densityDpi) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+        if (device != null) {
+            device.resizeLocked(width, height, densityDpi);
+        }
+    }
+
+
     public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
         if (device != null) {
@@ -122,20 +131,24 @@
     }
 
     private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
+        private static final int PENDING_SURFACE_CHANGE = 0x01;
+        private static final int PENDING_RESIZE = 0x02;
+
         private final IBinder mAppToken;
         private final int mOwnerUid;
         final String mOwnerPackageName;
         final String mName;
-        private final int mWidth;
-        private final int mHeight;
-        private final int mDensityDpi;
         private final int mFlags;
         private final Callbacks mCallbacks;
 
+        private int mWidth;
+        private int mHeight;
+        private int mDensityDpi;
         private Surface mSurface;
         private DisplayDeviceInfo mInfo;
-        private int mState;
+        private int mDisplayState;
         private boolean mStopped;
+        private int mPendingChanges;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
@@ -152,7 +165,8 @@
             mSurface = surface;
             mFlags = flags;
             mCallbacks = callbacks;
-            mState = Display.STATE_UNKNOWN;
+            mDisplayState = Display.STATE_UNKNOWN;
+            mPendingChanges |= PENDING_SURFACE_CHANGE;
         }
 
         @Override
@@ -175,8 +189,8 @@
 
         @Override
         public void requestDisplayStateLocked(int state) {
-            if (state != mState) {
-                mState = state;
+            if (state != mDisplayState) {
+                mDisplayState = state;
                 if (state == Display.STATE_OFF) {
                     mCallbacks.dispatchDisplayPaused();
                 } else {
@@ -187,7 +201,13 @@
 
         @Override
         public void performTraversalInTransactionLocked() {
-            setSurfaceInTransactionLocked(mSurface);
+            if ((mPendingChanges & PENDING_RESIZE) != 0) {
+                SurfaceControl.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight);
+            }
+            if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) {
+                setSurfaceInTransactionLocked(mSurface);
+            }
+            mPendingChanges = 0;
         }
 
         public void setSurfaceLocked(Surface surface) {
@@ -198,6 +218,19 @@
                 sendTraversalRequestLocked();
                 mSurface = surface;
                 mInfo = null;
+                mPendingChanges |= PENDING_SURFACE_CHANGE;
+            }
+        }
+
+        public void resizeLocked(int width, int height, int densityDpi) {
+            if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) {
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+                sendTraversalRequestLocked();
+                mWidth = width;
+                mHeight = height;
+                mDensityDpi = densityDpi;
+                mInfo = null;
+                mPendingChanges |= PENDING_RESIZE;
             }
         }
 
@@ -210,7 +243,7 @@
         public void dumpLocked(PrintWriter pw) {
             super.dumpLocked(pw);
             pw.println("mFlags=" + mFlags);
-            pw.println("mState=" + Display.stateToString(mState));
+            pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
             pw.println("mStopped=" + mStopped);
         }