Have an actual fallback if the surface is lost

Bug: 17516789

This will force a relayout/reinitialize pass if the Surface
is lost mid-render instead of crashing on the next frame

Change-Id: If08bfa16f740728fa7c05904fa11e26f07b81e2e
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 00a8884..5579c13 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -66,6 +66,8 @@
     private static final int SYNC_OK = 0;
     // Needs a ViewRoot invalidate
     private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0;
+    // Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
+    private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
 
     private static final String[] VISUALIZERS = {
         PROFILE_PROPERTY_VISUALIZE_BARS,
@@ -336,6 +338,12 @@
 
         int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
                 recordDuration, view.getResources().getDisplayMetrics().density);
+        if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
+            setEnabled(false);
+            // Invalidate since we failed to draw. This should fetch a Surface
+            // if it is still needed or do nothing if we are no longer drawing
+            attachInfo.mViewRootImpl.invalidate();
+        }
         if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
             attachInfo.mViewRootImpl.invalidate();
         }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b499dd0..6c3637d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -166,6 +166,11 @@
         freePrefetechedLayers();
     }
 
+    if (CC_UNLIKELY(!mNativeWindow.get())) {
+        info.out.canDrawThisFrame = false;
+        return;
+    }
+
     int runningBehind = 0;
     // TODO: This query is moderately expensive, investigate adding some sort
     // of fast-path based off when we last called eglSwapBuffers() as well as
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index e20564b..435244e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -68,6 +68,8 @@
     bool initialize(ANativeWindow* window);
     void updateSurface(ANativeWindow* window);
     void pauseSurface(ANativeWindow* window);
+    bool hasSurface() { return mNativeWindow.get(); }
+
     void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setOpaque(bool opaque);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index dd34e09..97b31a9 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -132,6 +132,12 @@
     mLayers.clear();
     mContext->prepareTree(info);
 
+    // This is after the prepareTree so that any pending operations
+    // (RenderNode tree state, prefetched layers, etc...) will be flushed.
+    if (CC_UNLIKELY(!mContext->hasSurface())) {
+        mSyncResult |= kSync_LostSurfaceRewardIfFound;
+    }
+
     if (info.out.hasAnimations) {
         if (info.out.requiresUiRedraw) {
             mSyncResult |= kSync_UIRedrawRequired;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 243cc5d..28f6cb2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -42,6 +42,7 @@
 enum SyncResult {
     kSync_OK = 0,
     kSync_UIRedrawRequired = 1 << 0,
+    kSync_LostSurfaceRewardIfFound = 1 << 1,
 };
 
 /*