Throw ISE on too-big-views-with-layers

 Bug: 12971954

Change-Id: I3ef995e91f236014b0a72a90846ef19ce6dc42b1
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 815c4a7..2b94b65 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -49,6 +49,14 @@
 
 static jmethodID gRunnableMethod;
 
+static JNIEnv* getenv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
 class JavaTask : public RenderTask {
 public:
     JavaTask(JNIEnv* env, jobject jrunnable) {
@@ -57,20 +65,13 @@
     }
 
     virtual void run() {
-        env()->CallVoidMethod(mRunnable, gRunnableMethod);
-        env()->DeleteGlobalRef(mRunnable);
+        JNIEnv* env = getenv(mVm);
+        env->CallVoidMethod(mRunnable, gRunnableMethod);
+        env->DeleteGlobalRef(mRunnable);
         delete this;
     };
 
 private:
-    JNIEnv* env() {
-        JNIEnv* env;
-        if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-            return 0;
-        }
-        return env;
-    }
-
     JavaVM* mVm;
     jobject mRunnable;
 };
@@ -122,12 +123,34 @@
     std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
-class RootRenderNode : public RenderNode, public AnimationHook {
+class RenderingException : public MessageHandler {
 public:
-    RootRenderNode() : RenderNode() {
+    RenderingException(JavaVM* vm, const std::string& message)
+            : mVm(vm)
+            , mMessage(message) {
+    }
+
+    virtual void handleMessage(const Message&) {
+        throwException(mVm, mMessage);
+    }
+
+    static void throwException(JavaVM* vm, const std::string& message) {
+        JNIEnv* env = getenv(vm);
+        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
+    }
+
+private:
+    JavaVM* mVm;
+    std::string mMessage;
+};
+
+class RootRenderNode : public RenderNode, AnimationHook, ErrorHandler {
+public:
+    RootRenderNode(JNIEnv* env) : RenderNode() {
         mLooper = Looper::getForThread();
         LOG_ALWAYS_FATAL_IF(!mLooper.get(),
                 "Must create RootRenderNode on a thread with a looper!");
+        env->GetJavaVM(&mVm);
     }
 
     virtual ~RootRenderNode() {}
@@ -137,10 +160,16 @@
         mOnFinishedEvents.push_back(event);
     }
 
+    virtual void onError(const std::string& message) {
+        mLooper->sendMessage(new RenderingException(mVm, message), 0);
+    }
+
     virtual void prepareTree(TreeInfo& info) {
         info.animationHook = this;
+        info.errorHandler = this;
         RenderNode::prepareTree(info);
         info.animationHook = NULL;
+        info.errorHandler = NULL;
 
         // post all the finished stuff
         if (mOnFinishedEvents.size()) {
@@ -160,6 +189,7 @@
 private:
     sp<Looper> mLooper;
     std::vector<OnFinishedEvent> mOnFinishedEvents;
+    JavaVM* mVm;
 };
 
 static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
@@ -178,7 +208,7 @@
 }
 
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
-    RootRenderNode* node = new RootRenderNode();
+    RootRenderNode* node = new RootRenderNode(env);
     node->incStrong(0);
     node->setName("RootRenderNode");
     return reinterpret_cast<jlong>(node);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 05f4edf..3d93383 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -20,6 +20,7 @@
 #include "RenderNode.h"
 
 #include <algorithm>
+#include <string>
 
 #include <SkCanvas.h>
 #include <algorithm>
@@ -158,7 +159,10 @@
         applyLayerPropertiesToLayer(info);
         damageSelf(info);
     } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
-        LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight());
+        if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
+            LayerRenderer::destroyLayer(mLayer);
+            mLayer = 0;
+        }
         damageSelf(info);
     }
 
@@ -166,6 +170,15 @@
     info.damageAccumulator->peekAtDirty(&dirty);
     info.damageAccumulator->popTransform();
 
+    if (!mLayer) {
+        if (info.errorHandler) {
+            std::string msg = "Unable to create layer for ";
+            msg += getName();
+            info.errorHandler->onError(msg);
+        }
+        return;
+    }
+
     if (!dirty.isEmpty()) {
         mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
     }
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 0fc0cef..f67e434 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,6 +16,8 @@
 #ifndef TREEINFO_H
 #define TREEINFO_H
 
+#include <string>
+
 #include <utils/Timers.h>
 
 #include "DamageAccumulator.h"
@@ -35,6 +37,13 @@
     ~AnimationHook() {}
 };
 
+class ErrorHandler {
+public:
+    virtual void onError(const std::string& message) = 0;
+protected:
+    ~ErrorHandler() {}
+};
+
 // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
 class TreeInfo {
     PREVENT_COPY_AND_ASSIGN(TreeInfo);
@@ -65,6 +74,7 @@
         , prepareTextures(mode == MODE_FULL)
         , damageAccumulator(NullDamageAccumulator::instance())
         , renderer(0)
+        , errorHandler(0)
     {}
 
     const TraversalMode mode;
@@ -78,6 +88,7 @@
     // The renderer that will be drawing the next frame. Use this to push any
     // layer updates or similar. May be NULL.
     OpenGLRenderer* renderer;
+    ErrorHandler* errorHandler;
 
     struct Out {
         Out()