Merge "Provide non-blocking SurfaceView draw notification path."
diff --git a/api/current.txt b/api/current.txt
index b125d1e..c38b5a8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28580,7 +28580,7 @@
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
-    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+    method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -43068,6 +43068,7 @@
 
   public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback {
     method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder);
+    method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable);
   }
 
   public class SurfaceView extends android.view.View {
diff --git a/api/system-current.txt b/api/system-current.txt
index ff37b5b..0d116f6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -31144,7 +31144,7 @@
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
-    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+    method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -46286,6 +46286,7 @@
 
   public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback {
     method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder);
+    method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable);
   }
 
   public class SurfaceView extends android.view.View {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1f59185..bdda72e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28667,7 +28667,7 @@
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
-    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+    method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -43357,6 +43357,7 @@
 
   public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback {
     method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder);
+    method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable);
   }
 
   public class SurfaceView extends android.view.View {
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index a3e8312..2fd2e96 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -116,9 +116,34 @@
          * size before it has been correctly drawn that way).  This will
          * typically be preceeded by a call to {@link #surfaceChanged}.
          *
+         * As of O, {@link #surfaceRedrawNeededAsync} may be implemented
+         * to provide a non-blocking implementation. If {@link #surfaceRedrawNeededAsync}
+         * is not implemented, then this will be called instead.
+         *
          * @param holder The SurfaceHolder whose surface has changed.
          */
-        public void surfaceRedrawNeeded(SurfaceHolder holder);
+        void surfaceRedrawNeeded(SurfaceHolder holder);
+
+        /**
+         * An alternative to surfaceRedrawNeeded where it is not required to block
+         * until the redraw is complete. You should initiate the redraw, and return,
+         * later invoking drawingFinished when your redraw is complete.
+         *
+         * This can be useful to avoid blocking your main application thread on rendering.
+         *
+         * As of O, if this is implemented {@link #surfaceRedrawNeeded} will not be called.
+         * However it is still recommended to implement {@link #surfaceRedrawNeeded} for
+         * compatibility with older versions of the platform.
+         *
+         * @param holder The SurfaceHolder which needs redrawing.
+         * @param drawingFinished A runnable to signal completion. This may be invoked
+         * from any thread.
+         *
+         */
+        default void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable drawingFinished) {
+            surfaceRedrawNeeded(holder);
+            drawingFinished.run();
+        }
     }
 
     /**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d46910c..018be86 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,6 +33,7 @@
 import android.util.Log;
 
 import com.android.internal.view.BaseIWindow;
+import com.android.internal.view.SurfaceCallbackHelper;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -639,21 +640,13 @@
                             if (callbacks == null) {
                                 callbacks = getSurfaceCallbacks();
                             }
-                            for (SurfaceHolder.Callback c : callbacks) {
-                                if (c instanceof SurfaceHolder.Callback2) {
-                                    ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
-                                            mSurfaceHolder);
-                                }
-                            }
+                            SurfaceCallbackHelper sch =
+                                    new SurfaceCallbackHelper(mSession, mWindow);
+                            sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
                         }
                     }
                 } finally {
                     mIsCreating = false;
-                    if (redrawNeeded) {
-                        if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                + "finishedDrawing");
-                        mSession.finishDrawing(mWindow);
-                    }
                     mSession.performDeferredDestroy(mWindow);
                 }
             } catch (RemoteException ex) {
@@ -876,7 +869,6 @@
     }
 
     private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
-
         private static final String LOG_TAG = "SurfaceHolder";
 
         @Override
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d6db634..b42f769 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -96,6 +96,7 @@
 import com.android.internal.policy.PhoneFallbackEventHandler;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
+import com.android.internal.view.SurfaceCallbackHelper;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -528,11 +529,17 @@
      */
     public void notifyChildRebuilt() {
         if (mView instanceof RootViewSurfaceTaker) {
+            if (mSurfaceHolderCallback != null) {
+                mSurfaceHolder.removeCallback(mSurfaceHolderCallback);
+            }
+
             mSurfaceHolderCallback =
                 ((RootViewSurfaceTaker)mView).willYouTakeTheSurface();
+
             if (mSurfaceHolderCallback != null) {
                 mSurfaceHolder = new TakenSurfaceHolder();
                 mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
+                mSurfaceHolder.addCallback(mSurfaceHolderCallback);
             } else {
                 mSurfaceHolder = null;
             }
@@ -581,6 +588,7 @@
                     if (mSurfaceHolderCallback != null) {
                         mSurfaceHolder = new TakenSurfaceHolder();
                         mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
+                        mSurfaceHolder.addCallback(mSurfaceHolderCallback);
                     }
                 }
 
@@ -1957,7 +1965,6 @@
                         mSurfaceHolder.ungetCallbacks();
 
                         mIsCreating = true;
-                        mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
                         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                         if (callbacks != null) {
                             for (SurfaceHolder.Callback c : callbacks) {
@@ -1967,8 +1974,6 @@
                         surfaceChanged = true;
                     }
                     if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) {
-                        mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
-                                lp.format, mWidth, mHeight);
                         SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                         if (callbacks != null) {
                             for (SurfaceHolder.Callback c : callbacks) {
@@ -1981,7 +1986,6 @@
                 } else if (hadSurface) {
                     mSurfaceHolder.ungetCallbacks();
                     SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
-                    mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
                     if (callbacks != null) {
                         for (SurfaceHolder.Callback c : callbacks) {
                             c.surfaceDestroyed(mSurfaceHolder);
@@ -2646,21 +2650,18 @@
             if (LOCAL_LOGV) {
                 Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
             }
+
             if (mSurfaceHolder != null && mSurface.isValid()) {
-                mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
+                SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow);
                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
-                if (callbacks != null) {
-                    for (SurfaceHolder.Callback c : callbacks) {
-                        if (c instanceof SurfaceHolder.Callback2) {
-                            ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(mSurfaceHolder);
-                        }
-                    }
+
+                sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+            } else {
+                try {
+                    mWindowSession.finishDrawing(mWindow);
+                } catch (RemoteException e) {
                 }
             }
-            try {
-                mWindowSession.finishDrawing(mWindow);
-            } catch (RemoteException e) {
-            }
         }
     }
 
diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java
index b41ef29..32ce0fe 100644
--- a/core/java/com/android/internal/view/BaseSurfaceHolder.java
+++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java
@@ -86,7 +86,7 @@
             mCallbacks.remove(callback);
         }
     }
-    
+
     public SurfaceHolder.Callback[] getCallbacks() {
         if (mHaveGottenCallbacks) {
             return mGottenCallbacks;
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
new file mode 100644
index 0000000..5b6a82c
--- /dev/null
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+public class SurfaceCallbackHelper {
+    IWindowSession mSession;
+    IWindow.Stub mWindow;
+
+    int mFinishDrawingCollected = 0;
+    int mFinishDrawingExpected = 0;
+
+    private Runnable mFinishDrawingRunnable = new Runnable() {
+            @Override
+            public void run() {
+                synchronized (SurfaceCallbackHelper.this) {
+                    mFinishDrawingCollected++;
+                    if (mFinishDrawingCollected < mFinishDrawingExpected) {
+                        return;
+                    }
+                    try {
+                        mSession.finishDrawing(mWindow);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+    };
+
+    public SurfaceCallbackHelper(IWindowSession session,
+            IWindow.Stub window) {
+        mSession = session;
+        mWindow = window;
+    }
+
+    public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
+        if (callbacks == null || callbacks.length == 0) {
+            try {
+                mSession.finishDrawing(mWindow);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+
+        synchronized (this) {
+            mFinishDrawingExpected = callbacks.length;
+            mFinishDrawingCollected = 0;
+        }
+
+        for (SurfaceHolder.Callback c : callbacks) {
+            if (c instanceof SurfaceHolder.Callback2) {
+                ((SurfaceHolder.Callback2) c).surfaceRedrawNeededAsync(
+                        holder, mFinishDrawingRunnable);
+            } else {
+                mFinishDrawingRunnable.run();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 4154ef0..329514c 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -542,16 +542,27 @@
     }
 
     /**
-     * This method is part of the SurfaceHolder.Callback interface, and is
+     * This method is part of the SurfaceHolder.Callback2 interface, and is
      * not normally called or subclassed by clients of GLSurfaceView.
      */
     @Override
-    public void surfaceRedrawNeeded(SurfaceHolder holder) {
+    public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) {
         if (mGLThread != null) {
-            mGLThread.requestRenderAndWait();
+            mGLThread.requestRenderAndNotify(finishDrawing);
         }
     }
 
+    /**
+     * This method is part of the SurfaceHolder.Callback2 interface, and is
+     * not normally called or subclassed by clients of GLSurfaceView.
+     */
+    @Deprecated
+    @Override
+    public void surfaceRedrawNeeded(SurfaceHolder holder) {
+        // Since we are part of the framework we know only surfaceRedrawNeededAsync
+        // will be called.
+    }
+
 
     /**
      * Pause the rendering thread, optionally tearing down the EGL context
@@ -1305,6 +1316,7 @@
                 int w = 0;
                 int h = 0;
                 Runnable event = null;
+                Runnable finishDrawingRunnable = null;
 
                 while (true) {
                     synchronized (sGLThreadManager) {
@@ -1400,6 +1412,11 @@
                                 sGLThreadManager.notifyAll();
                             }
 
+                            if (mFinishDrawingRunnable != null) {
+                                finishDrawingRunnable = mFinishDrawingRunnable;
+                                mFinishDrawingRunnable = null;
+                            }
+
                             // Ready to draw?
                             if (readyToDraw()) {
 
@@ -1453,7 +1470,6 @@
                                     break;
                                 }
                             }
-
                             // By design, this is the only place in a GLThread thread where we wait().
                             if (LOG_THREADS) {
                                 Log.i("GLThread", "waiting tid=" + getId()
@@ -1546,6 +1562,10 @@
                             try {
                                 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
                                 view.mRenderer.onDrawFrame(gl);
+                                if (finishDrawingRunnable != null) {
+                                    finishDrawingRunnable.run();
+                                    finishDrawingRunnable = null;
+                                }
                             } finally {
                                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                             }
@@ -1625,7 +1645,7 @@
             }
         }
 
-        public void requestRenderAndWait() {
+        public void requestRenderAndNotify(Runnable finishDrawing) {
             synchronized(sGLThreadManager) {
                 // If we are already on the GL thread, this means a client callback
                 // has caused reentrancy, for example via updating the SurfaceView parameters.
@@ -1638,17 +1658,9 @@
                 mWantRenderNotification = true;
                 mRequestRender = true;
                 mRenderComplete = false;
+                mFinishDrawingRunnable = finishDrawing;
 
                 sGLThreadManager.notifyAll();
-
-                while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) {
-                    try {
-                        sGLThreadManager.wait();
-                    } catch (InterruptedException ex) {
-                        Thread.currentThread().interrupt();
-                    }
-                }
-
             }
         }
 
@@ -1821,6 +1833,7 @@
         private boolean mRenderComplete;
         private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
         private boolean mSizeChanged = true;
+        private Runnable mFinishDrawingRunnable = null;
 
         // End of member variables protected by the sGLThreadManager monitor.