Fix onTrimMemory for HardwareRenderer

 Also fixes detachFunctor possibly drawing after return

 Bug: 15189843
 Bug: 15990672

Change-Id: I64c48cb674c461a8eeaba407b697e09f72c98ce3
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index e803ec3..89105ea 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -195,17 +195,28 @@
 
 void RenderNode::prepareTreeImpl(TreeInfo& info) {
     info.damageAccumulator->pushTransform(this);
-    if (info.mode == TreeInfo::MODE_FULL) {
+
+    switch (info.mode) {
+    case TreeInfo::MODE_FULL:
         pushStagingPropertiesChanges(info);
         mAnimatorManager.animate(info);
-    } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) {
+        break;
+    case TreeInfo::MODE_MAYBE_DETACHING:
         pushStagingPropertiesChanges(info);
-    } else if (info.mode == TreeInfo::MODE_RT_ONLY) {
+        break;
+    case TreeInfo::MODE_RT_ONLY:
         mAnimatorManager.animate(info);
+        break;
+    case TreeInfo::MODE_DESTROY_RESOURCES:
+        // This will also release the hardware layer if we have one as
+        // isRenderable() will return false, thus causing pushLayerUpdate
+        // to recycle the hardware layer
+        setStagingDisplayList(NULL);
+        break;
     }
 
     prepareLayer(info);
-    if (info.mode == TreeInfo::MODE_FULL) {
+    if (info.mode == TreeInfo::MODE_FULL || info.mode == TreeInfo::MODE_DESTROY_RESOURCES) {
         pushStagingDisplayListChanges(info);
     }
     prepareSubTree(info, mDisplayListData);
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 083100e..9746ac53 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -65,7 +65,8 @@
         // but don't evaluate animators and such as if it isn't detached as a
         // MODE_FULL will follow shortly.
         MODE_MAYBE_DETACHING,
-        // TODO: TRIM_MEMORY?
+        // Destroy all hardware resources, including DisplayListData, in the tree.
+        MODE_DESTROY_RESOURCES,
     };
 
     explicit TreeInfo(TraversalMode mode, RenderState& renderState)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9c3cf44..524ee62dd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -30,6 +30,9 @@
 #include "../OpenGLRenderer.h"
 #include "../Stencil.h"
 
+#define TRIM_MEMORY_COMPLETE 80
+#define TRIM_MEMORY_UI_HIDDEN 20
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
@@ -156,6 +159,10 @@
     }
 }
 
+void CanvasContext::stopDrawing() {
+    mRenderThread.removeFrameCallback(this);
+}
+
 void CanvasContext::notifyFramePending() {
     ATRACE_CALL();
     mRenderThread.pushBackFrameCallback(this);
@@ -216,8 +223,6 @@
     profiler().startFrame();
 
     TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
-    info.prepareTextures = false;
-
     prepareTree(info);
     if (info.out.canDrawThisFrame) {
         draw();
@@ -241,10 +246,25 @@
     return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
 }
 
-void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
+void CanvasContext::destroyHardwareResources() {
+    stopDrawing();
     if (mEglManager.hasEglContext()) {
         requireGlContext();
-        Caches::getInstance().flush(flushMode);
+        TreeInfo info(TreeInfo::MODE_DESTROY_RESOURCES, mRenderThread.renderState());
+        mRootRenderNode->prepareTree(info);
+        Caches::getInstance().flush(Caches::kFlushMode_Layers);
+    }
+}
+
+void CanvasContext::trimMemory(RenderThread& thread, int level) {
+    // No context means nothing to free
+    if (!thread.eglManager().hasEglContext()) return;
+
+    if (level >= TRIM_MEMORY_COMPLETE) {
+        Caches::getInstance().flush(Caches::kFlushMode_Full);
+        thread.eglManager().destroy();
+    } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
+        Caches::getInstance().flush(Caches::kFlushMode_Moderate);
     }
 }
 
@@ -264,11 +284,7 @@
 }
 
 void CanvasContext::requireGlContext() {
-    if (mEglSurface != EGL_NO_SURFACE) {
-        makeCurrent();
-    } else {
-        mEglManager.usePBufferSurface();
-    }
+    mEglManager.requireGlContext();
 }
 
 void CanvasContext::setTextureAtlas(RenderThread& thread,
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index dbfb3d2..1bab1b1 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -66,7 +66,8 @@
 
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
-    void flushCaches(Caches::FlushMode flushMode);
+    void destroyHardwareResources();
+    static void trimMemory(RenderThread& thread, int level);
 
     static void invokeFunctor(RenderThread& thread, Functor* functor);
 
@@ -78,6 +79,7 @@
     ANDROID_API static void setTextureAtlas(RenderThread& thread,
             const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
 
+    void stopDrawing();
     void notifyFramePending();
 
     DrawProfiler& profiler() { return mProfiler; }
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index e1ee620..6cd3d0b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -312,18 +312,30 @@
     postAndWait(task);
 }
 
-CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
-    args->context->flushCaches(args->flushMode);
+CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) {
+    args->context->destroyHardwareResources();
     return NULL;
 }
 
-void RenderProxy::flushCaches(Caches::FlushMode flushMode) {
-    SETUP_TASK(flushCaches);
+void RenderProxy::destroyHardwareResources() {
+    SETUP_TASK(destroyHardwareResources);
     args->context = mContext;
-    args->flushMode = flushMode;
     post(task);
 }
 
+CREATE_BRIDGE2(timMemory, RenderThread* thread, int level) {
+    CanvasContext::trimMemory(*args->thread, args->level);
+    return NULL;
+}
+
+void RenderProxy::trimMemory(int level) {
+    RenderThread& thread = RenderThread::getInstance();
+    SETUP_TASK(timMemory);
+    args->thread = &thread;
+    args->level = level;
+    thread.queue(task);
+}
+
 CREATE_BRIDGE0(fence) {
     // Intentionally empty
     return NULL;
@@ -334,6 +346,17 @@
     postAndWait(task);
 }
 
+CREATE_BRIDGE1(stopDrawing, CanvasContext* context) {
+    args->context->stopDrawing();
+    return NULL;
+}
+
+void RenderProxy::stopDrawing() {
+    SETUP_TASK(stopDrawing);
+    args->context = mContext;
+    postAndWait(task);
+}
+
 CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
     args->context->notifyFramePending();
     return NULL;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index c7358e6..0027403 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -85,9 +85,11 @@
     ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer);
 
-    ANDROID_API void flushCaches(Caches::FlushMode flushMode);
+    ANDROID_API void destroyHardwareResources();
+    ANDROID_API static void trimMemory(int level);
 
     ANDROID_API void fence();
+    ANDROID_API void stopDrawing();
     ANDROID_API void notifyFramePending();
 
     ANDROID_API void dumpProfileInfo(int fd);