Merge "Sync SurfaceView position changes to parent render."
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 017364a..1be2f95 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -96,6 +96,23 @@
             out Rect outOutsets, out Configuration outConfig, out Surface outSurface);
 
     /**
+     *  Position a window relative to it's parent (attached) window without triggering
+     *  a full relayout. This action may be deferred until a given frame number
+     *  for the parent window appears. This allows for synchronizing movement of a child
+     *  to repainting the contents of the parent.
+     *
+     *  @param window The window being modified. Must be attached to a parent window
+     *  or this call will fail.
+     *  @param x The new x position
+     *  @param y The new y position
+     *  @param deferTransactionUntilFrame Frame number from our parent (attached) to
+     *  defer this action until.
+     *  @param outFrame Rect in which is placed the new position/size on screen.
+     */
+    void repositionChild(IWindow childWindow, int x, int y, long deferTransactionUntilFrame,
+            out Rect outFrame);
+
+    /**
      * If a call to relayout() asked to have the surface destroy deferred,
      * it must call this once it is okay to destroy that surface.
      */
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6de4d3e..394660f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -56,6 +56,8 @@
     private static native int nativeGetWidth(long nativeObject);
     private static native int nativeGetHeight(long nativeObject);
 
+    private static native long nativeGetNextFrameNumber(long nativeObject);
+
     public static final Parcelable.Creator<Surface> CREATOR =
             new Parcelable.Creator<Surface>() {
         @Override
@@ -220,6 +222,18 @@
     }
 
     /**
+     * Returns the next frame number which will be dequeued for rendering.
+     * Intended for use with SurfaceFlinger's deferred transactions API.
+     *
+     * @hide
+     */
+    public long getNextFrameNumber() {
+        synchronized (mLock) {
+            return nativeGetNextFrameNumber(mNativeObject);
+        }
+    }
+
+    /**
      * Returns true if the consumer of this Surface is running behind the producer.
      *
      * @return True if the consumer is more than one buffer ahead of the producer.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bcf9b2c..b58c68f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -81,6 +81,9 @@
     private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
     private static native void nativeSetDisplayPowerMode(
             IBinder displayToken, int mode);
+    private static native void nativeDeferTransactionUntil(long nativeObject,
+            IBinder handle, long frame);
+    private static native IBinder nativeGetHandle(long nativeObject);
 
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -358,6 +361,14 @@
         nativeCloseTransaction();
     }
 
+    public void deferTransactionUntil(IBinder handle, long frame) {
+        nativeDeferTransactionUntil(mNativeObject, handle, frame);
+    }
+
+    public IBinder getHandle() {
+        return nativeGetHandle(mNativeObject);
+    }
+
     /** flag the transaction as an animation */
     public static void setAnimationTransaction() {
         nativeSetAnimationTransaction();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index db68c29..dddea21 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -448,11 +448,10 @@
         final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
         final boolean visibleChanged = mVisible != mRequestedVisible;
         final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
+        final boolean positionChanged = mLeft != mLocation[0] || mTop != mLocation[1];
 
         if (force || creating || formatChanged || sizeChanged || visibleChanged
-            || mLeft != mLocation[0] || mTop != mLocation[1]
             || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {
-
             if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                     + " format=" + formatChanged + " size=" + sizeChanged
                     + " visible=" + visibleChanged
@@ -616,11 +615,22 @@
                     mSession.performDeferredDestroy(mWindow);
                 }
             } catch (RemoteException ex) {
+                Log.e(TAG, "Exception from relayout", ex);
             }
             if (DEBUG) Log.v(
                 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                 " w=" + mLayout.width + " h=" + mLayout.height +
                 ", frame=" + mSurfaceFrame);
+        } else if (positionChanged) { // Only the position has changed
+            mLeft = mLocation[0];
+            mTop = mLocation[1];
+            try {
+                mSession.repositionChild(mWindow, mLeft, mTop,
+                        viewRoot != null ? viewRoot.getNextFrameNumber() : -1,
+                        mWinFrame);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Exception from relayout", ex);
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e17bdd7..faeb353 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6607,6 +6607,20 @@
         }
     }
 
+    long getNextFrameNumber() {
+        long frameNumber = -1;
+        if (mSurfaceHolder != null) {
+            mSurfaceHolder.mSurfaceLock.lock();
+        }
+        if (mSurface.isValid()) {
+            frameNumber =  mSurface.getNextFrameNumber();
+        }
+        if (mSurfaceHolder != null) {
+            mSurfaceHolder.mSurfaceLock.unlock();
+        }
+        return frameNumber;
+    }
+
     class TakenSurfaceHolder extends BaseSurfaceHolder {
         @Override
         public boolean onAllowLockCanvas() {
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index da96b93..ff51e4e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -460,6 +460,10 @@
     anw->query(anw, NATIVE_WINDOW_HEIGHT, &value);
     return value;
 }
+static jlong nativeGetNextFrameNumber(JNIEnv *env, jclass clazz, jlong nativeObject) {
+    Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+    return surface->getNextFrameNumber();
+}
 
 namespace uirenderer {
 
@@ -536,6 +540,7 @@
             (void*)nativeWriteToParcel },
     {"nativeGetWidth", "(J)I", (void*)nativeGetWidth },
     {"nativeGetHeight", "(J)I", (void*)nativeGetHeight },
+    {"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber },
 
     // HWUI context
     {"nHwuiCreate", "(JJ)J", (void*) hwui::create },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 931ad54..1dfe40a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -571,6 +571,21 @@
     return JNI_TRUE;
 }
 
+
+static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
+        jobject handleObject, jlong frameNumber) {
+    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    sp<IBinder> handle = ibinderForJavaObject(env, handleObject);
+
+    ctrl->deferTransactionUntil(handle, frameNumber);
+}
+
+static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
+    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
+    return javaObjectForIBinder(env, ctrl->getHandle());
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -638,6 +653,10 @@
             (void*)nativeGetAnimationFrameStats },
     {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V",
             (void*)nativeSetDisplayPowerMode },
+    {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
+            (void*)nativeDeferTransactionUntil },
+    {"nativeGetHandle", "(J)Landroid/os/IBinder;",
+            (void*)nativeGetHandle }
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f62bc1..c47c377 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -187,6 +187,12 @@
         mService.removeWindow(this, window);
     }
 
+    @Override
+    public void repositionChild(IWindow window, int x, int y, long deferTransactionUntilFrame,
+             Rect outFrame) {
+        mService.repositionChild(this, window, x, y, deferTransactionUntilFrame, outFrame);
+    }
+
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 95aff1f..230e81b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2468,6 +2468,54 @@
         }
     }
 
+    void repositionChild(Session session, IWindow client,
+            int x, int y, long deferTransactionUntilFrame, Rect outFrame) {
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
+        long origId = Binder.clearCallingIdentity();
+
+        try {
+            synchronized(mWindowMap) {
+                WindowState win = windowForClientLocked(session, client, false);
+                if (win == null) {
+                    return;
+                }
+                if (win.mAttachedWindow == null) {
+                    throw new IllegalArgumentException(
+                            "repositionChild called but window is not"
+                            + "attached to a parent win=" + win);
+                }
+
+                win.mFrame.left = x;
+                win.mFrame.top = y;
+
+                win.mWinAnimator.computeShownFrameLocked();
+
+                if (SHOW_TRANSACTIONS) {
+                    Slog.i(TAG, ">>> OPEN TRANSACTION repositionChild");
+                }
+
+                SurfaceControl.openTransaction();
+
+                if (deferTransactionUntilFrame > 0) {
+                    win.mWinAnimator.mSurfaceControl.deferTransactionUntil(
+                            win.mAttachedWindow.mWinAnimator.mSurfaceControl.getHandle(),
+                            deferTransactionUntilFrame);
+                }
+                win.mWinAnimator.setSurfaceBoundariesLocked(false);
+
+                SurfaceControl.closeTransaction();
+                if (SHOW_TRANSACTIONS) {
+                    Slog.i(TAG, "<<< CLOSE TRANSACTION repositionChild");
+                }
+
+                outFrame = win.mCompatFrame;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
     public int relayoutWindow(Session session, IWindow client, int seq,
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,