Prevent WebView from crashing when detached from the window
Bug #6365056
WebView enqueues a functor in the hardware renderer to handle
animations and this functor is called at a later time by the
hardware renderer. However, the functor was not removed from
the queue when WebView was removed from the window. This could
cause the hardware renderer to attempt to execute an invalid
functor and lead to a crash.
Change-Id: I9d38e80f3fdc5e29d4d0cdfa1e893c251a954508
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 610b3550..0d562e4 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -550,6 +550,16 @@
return mQueue.hasMessages(this, what, object);
}
+ /**
+ * Check if there are any pending posts of messages with callback r in
+ * the message queue.
+ *
+ * @hide
+ */
+ public final boolean hasCallbacks(Runnable r) {
+ return mQueue.hasMessages(this, r, null);
+ }
+
// if we can get rid of this method, the handler need not remember its loop
// we could instead export a getMessageQueue() method...
public final Looper getLooper() {
@@ -588,20 +598,20 @@
}
}
- private final Message getPostMessage(Runnable r) {
+ private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
- private final Message getPostMessage(Runnable r, Object token) {
+ private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
- private final void handleCallback(Message message) {
+ private static void handleCallback(Message message) {
message.callback.run();
}
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 64027ef..5ad60ec 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -347,6 +347,23 @@
}
}
+ final boolean hasMessages(Handler h, Runnable r, Object object) {
+ if (h == null) {
+ return false;
+ }
+
+ synchronized (this) {
+ Message p = mMessages;
+ while (p != null) {
+ if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
+ return true;
+ }
+ p = p.next;
+ }
+ return false;
+ }
+ }
+
final void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 7736f57..0bb5f9f 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -266,6 +266,20 @@
private static native int nInvokeFunctors(int renderer, Rect dirty);
+ @Override
+ public void detachFunctor(int functor) {
+ nDetachFunctor(mRenderer, functor);
+ }
+
+ private static native void nDetachFunctor(int renderer, int functor);
+
+ @Override
+ public void attachFunctor(int functor) {
+ nAttachFunctor(mRenderer, functor);
+ }
+
+ private static native void nAttachFunctor(int renderer, int functor);
+
///////////////////////////////////////////////////////////////////////////
// Memory
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 2f4cd36..ee2dd59 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -107,4 +107,26 @@
public int invokeFunctors(Rect dirty) {
return DisplayList.STATUS_DONE;
}
+
+ /**
+ * Detaches the specified functor from the current functor execution queue.
+ *
+ * @param functor The native functor to remove from the execution queue.
+ *
+ * @see #invokeFunctors(android.graphics.Rect)
+ * @see #callDrawGLFunction(int)
+ * @see #detachFunctor(int)
+ */
+ abstract void detachFunctor(int functor);
+
+ /**
+ * Attaches the specified functor to the current functor execution queue.
+ *
+ * @param functor The native functor to add to the execution queue.
+ *
+ * @see #invokeFunctors(android.graphics.Rect)
+ * @see #callDrawGLFunction(int)
+ * @see #detachFunctor(int)
+ */
+ abstract void attachFunctor(int functor);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 1ec754a..b9295c3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -437,6 +437,27 @@
abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture);
/**
+ * Detaches the specified functor from the current functor execution queue.
+ *
+ * @param functor The native functor to remove from the execution queue.
+ *
+ * @see HardwareCanvas#callDrawGLFunction(int)
+ * @see #attachFunctor(android.view.View.AttachInfo, int)
+ */
+ abstract void detachFunctor(int functor);
+
+ /**
+ * Schedules the specified functor in the functors execution queue.
+ *
+ * @param attachInfo AttachInfo tied to this renderer.
+ * @param functor The native functor to insert in the execution queue.
+ *
+ * @see HardwareCanvas#callDrawGLFunction(int)
+ * @see #detachFunctor(int)
+ */
+ abstract void attachFunctor(View.AttachInfo attachInfo, int functor);
+
+ /**
* Initializes the hardware renderer for the specified surface and setup the
* renderer for drawing, if needed. This is invoked when the ViewAncestor has
* potentially lost the hardware renderer. The hardware renderer should be
@@ -1202,13 +1223,33 @@
}
if ((status & DisplayList.STATUS_INVOKE) != 0) {
- attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
- mFunctorsRunnable.attachInfo = attachInfo;
+ scheduleFunctors(attachInfo);
+ }
+ }
+
+ private void scheduleFunctors(View.AttachInfo attachInfo) {
+ mFunctorsRunnable.attachInfo = attachInfo;
+ if (!attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) {
// delay the functor callback by a few ms so it isn't polled constantly
attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
}
}
+ @Override
+ void detachFunctor(int functor) {
+ if (mCanvas != null) {
+ mCanvas.detachFunctor(functor);
+ }
+ }
+
+ @Override
+ void attachFunctor(View.AttachInfo attachInfo, int functor) {
+ if (mCanvas != null) {
+ mCanvas.attachFunctor(functor);
+ scheduleFunctors(attachInfo);
+ }
+ }
+
/**
* Ensures the current EGL context is the one we expect.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e3681df..59f0917 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -59,7 +59,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
-import android.view.KeyCharacterMap.FallbackAction;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
@@ -669,6 +668,18 @@
}
}
+ public void attachFunctor(int functor) {
+ if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
+ mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
+ }
+ }
+
+ public void detachFunctor(int functor) {
+ if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
+ mAttachInfo.mHardwareRenderer.detachFunctor(functor);
+ }
+ }
+
private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -4489,8 +4500,8 @@
mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
} else {
- View newSource = getCommonPredecessor(oldSource, source);
- mSendWindowContentChangedAccessibilityEvent.mSource = newSource;
+ mSendWindowContentChangedAccessibilityEvent.mSource =
+ getCommonPredecessor(oldSource, source);
}
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 3bd9960..1aa0a52 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -2469,7 +2469,7 @@
/**
* Loads the view data from the input stream. See
- * {@link #saveViewState(OutputStream)} for more information.
+ * {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information.
* @param stream The {@link InputStream} to load from
*/
public void loadViewState(InputStream stream) {
@@ -5630,6 +5630,13 @@
removeAccessibilityApisFromJavaScript();
updateHwAccelerated();
+
+ if (mWebView.isHardwareAccelerated()) {
+ int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
+ if (drawGLFunction != 0) {
+ mWebView.getViewRootImpl().detachFunctor(drawGLFunction);
+ }
+ }
}
@Override
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index d4d60d3..60929ac 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -158,11 +158,21 @@
// ----------------------------------------------------------------------------
static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
- OpenGLRenderer* renderer, Functor *functor) {
+ OpenGLRenderer* renderer, Functor* functor) {
android::uirenderer::Rect dirty;
return renderer->callDrawGLFunction(functor, dirty);
}
+static void android_view_GLES20Canvas_detachFunctor(JNIEnv* env,
+ jobject clazz, OpenGLRenderer* renderer, Functor* functor) {
+ renderer->detachFunctor(functor);
+}
+
+static void android_view_GLES20Canvas_attachFunctor(JNIEnv* env,
+ jobject clazz, OpenGLRenderer* renderer, Functor* functor) {
+ renderer->attachFunctor(functor);
+}
+
static jint android_view_GLES20Canvas_invokeFunctors(JNIEnv* env,
jobject clazz, OpenGLRenderer* renderer, jobject dirty) {
android::uirenderer::Rect bounds;
@@ -825,9 +835,9 @@
{ "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
#ifdef USE_OPENGL_RENDERER
- { "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
- { "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
- { "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
+ { "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
+ { "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
+ { "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
{ "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
@@ -839,7 +849,9 @@
{ "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
{ "nCallDrawGLFunction", "(II)I", (void*) android_view_GLES20Canvas_callDrawGLFunction },
- { "nInvokeFunctors", "(ILandroid/graphics/Rect;)I",
+ { "nDetachFunctor", "(II)V", (void*) android_view_GLES20Canvas_detachFunctor },
+ { "nAttachFunctor", "(II)V", (void*) android_view_GLES20Canvas_attachFunctor },
+ { "nInvokeFunctors", "(ILandroid/graphics/Rect;)I",
(void*) android_view_GLES20Canvas_invokeFunctors },
{ "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5edaa46..9f337ff0 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -237,33 +237,43 @@
glBlendEquation(GL_FUNC_ADD);
}
+void OpenGLRenderer::detachFunctor(Functor* functor) {
+ mFunctors.remove(functor);
+}
+
+void OpenGLRenderer::attachFunctor(Functor* functor) {
+ mFunctors.add(functor);
+}
+
status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
status_t result = DrawGlInfo::kStatusDone;
-
- Vector<Functor*> functors(mFunctors);
- mFunctors.clear();
-
- DrawGlInfo info;
- info.clipLeft = 0;
- info.clipTop = 0;
- info.clipRight = 0;
- info.clipBottom = 0;
- info.isLayer = false;
- info.width = 0;
- info.height = 0;
- memset(info.transform, 0, sizeof(float) * 16);
-
size_t count = functors.size();
- for (size_t i = 0; i < count; i++) {
- Functor* f = functors.itemAt(i);
- result |= (*f)(DrawGlInfo::kModeProcess, &info);
- if (result != DrawGlInfo::kStatusDone) {
- Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
- dirty.unionWith(localDirty);
+ if (count > 0) {
+ SortedVector<Functor*> functors(mFunctors);
+ mFunctors.clear();
- if (result & DrawGlInfo::kStatusInvoke) {
- mFunctors.push(f);
+ DrawGlInfo info;
+ info.clipLeft = 0;
+ info.clipTop = 0;
+ info.clipRight = 0;
+ info.clipBottom = 0;
+ info.isLayer = false;
+ info.width = 0;
+ info.height = 0;
+ memset(info.transform, 0, sizeof(float) * 16);
+
+ for (size_t i = 0; i < count; i++) {
+ Functor* f = functors.itemAt(i);
+ result |= (*f)(DrawGlInfo::kModeProcess, &info);
+
+ if (result != DrawGlInfo::kStatusDone) {
+ Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
+ dirty.unionWith(localDirty);
+
+ if (result & DrawGlInfo::kStatusInvoke) {
+ mFunctors.add(f);
+ }
}
}
}
@@ -305,7 +315,7 @@
dirty.unionWith(localDirty);
if (result & DrawGlInfo::kStatusInvoke) {
- mFunctors.push(functor);
+ mFunctors.add(functor);
}
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 141e22b..18a6923 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -29,6 +29,7 @@
#include <utils/Functor.h>
#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
#include <utils/Vector.h>
#include <cutils/compiler.h>
@@ -73,6 +74,8 @@
virtual void resume();
ANDROID_API status_t invokeFunctors(Rect& dirty);
+ ANDROID_API void detachFunctor(Functor* functor);
+ ANDROID_API void attachFunctor(Functor* functor);
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
ANDROID_API int getSaveCount() const;
@@ -612,7 +615,7 @@
// List of rectangles to clear after saveLayer() is invoked
Vector<Rect*> mLayers;
// List of functors to invoke after a frame is drawn
- Vector<Functor*> mFunctors;
+ SortedVector<Functor*> mFunctors;
// Indentity matrix
const mat4 mIdentity;