Skip to content

Commit ba6be8a

Browse files
author
Romain Guy
committed
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
1 parent 88fffb7 commit ba6be8a

File tree

10 files changed

+183
-36
lines changed

10 files changed

+183
-36
lines changed

core/java/android/os/Handler.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,16 @@ public final boolean hasMessages(int what, Object object) {
550550
return mQueue.hasMessages(this, what, object);
551551
}
552552

553+
/**
554+
* Check if there are any pending posts of messages with callback r in
555+
* the message queue.
556+
*
557+
* @hide
558+
*/
559+
public final boolean hasCallbacks(Runnable r) {
560+
return mQueue.hasMessages(this, r, null);
561+
}
562+
553563
// if we can get rid of this method, the handler need not remember its loop
554564
// we could instead export a getMessageQueue() method...
555565
public final Looper getLooper() {
@@ -588,20 +598,20 @@ public void send(Message msg) {
588598
}
589599
}
590600

591-
private final Message getPostMessage(Runnable r) {
601+
private static Message getPostMessage(Runnable r) {
592602
Message m = Message.obtain();
593603
m.callback = r;
594604
return m;
595605
}
596606

597-
private final Message getPostMessage(Runnable r, Object token) {
607+
private static Message getPostMessage(Runnable r, Object token) {
598608
Message m = Message.obtain();
599609
m.obj = token;
600610
m.callback = r;
601611
return m;
602612
}
603613

604-
private final void handleCallback(Message message) {
614+
private static void handleCallback(Message message) {
605615
message.callback.run();
606616
}
607617

core/java/android/os/MessageQueue.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,23 @@ final boolean hasMessages(Handler h, int what, Object object) {
347347
}
348348
}
349349

350+
final boolean hasMessages(Handler h, Runnable r, Object object) {
351+
if (h == null) {
352+
return false;
353+
}
354+
355+
synchronized (this) {
356+
Message p = mMessages;
357+
while (p != null) {
358+
if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
359+
return true;
360+
}
361+
p = p.next;
362+
}
363+
return false;
364+
}
365+
}
366+
350367
final void removeMessages(Handler h, int what, Object object) {
351368
if (h == null) {
352369
return;

core/java/android/view/GLES20Canvas.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,20 @@ public int invokeFunctors(Rect dirty) {
266266

267267
private static native int nInvokeFunctors(int renderer, Rect dirty);
268268

269+
@Override
270+
public void detachFunctor(int functor) {
271+
nDetachFunctor(mRenderer, functor);
272+
}
273+
274+
private static native void nDetachFunctor(int renderer, int functor);
275+
276+
@Override
277+
public void attachFunctor(int functor) {
278+
nAttachFunctor(mRenderer, functor);
279+
}
280+
281+
private static native void nAttachFunctor(int renderer, int functor);
282+
269283
///////////////////////////////////////////////////////////////////////////
270284
// Memory
271285
///////////////////////////////////////////////////////////////////////////

core/java/android/view/HardwareCanvas.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,26 @@ public int callDrawGLFunction(int drawGLFunction) {
107107
public int invokeFunctors(Rect dirty) {
108108
return DisplayList.STATUS_DONE;
109109
}
110+
111+
/**
112+
* Detaches the specified functor from the current functor execution queue.
113+
*
114+
* @param functor The native functor to remove from the execution queue.
115+
*
116+
* @see #invokeFunctors(android.graphics.Rect)
117+
* @see #callDrawGLFunction(int)
118+
* @see #detachFunctor(int)
119+
*/
120+
abstract void detachFunctor(int functor);
121+
122+
/**
123+
* Attaches the specified functor to the current functor execution queue.
124+
*
125+
* @param functor The native functor to add to the execution queue.
126+
*
127+
* @see #invokeFunctors(android.graphics.Rect)
128+
* @see #callDrawGLFunction(int)
129+
* @see #detachFunctor(int)
130+
*/
131+
abstract void attachFunctor(int functor);
110132
}

core/java/android/view/HardwareRenderer.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,27 @@ abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbac
436436
*/
437437
abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture);
438438

439+
/**
440+
* Detaches the specified functor from the current functor execution queue.
441+
*
442+
* @param functor The native functor to remove from the execution queue.
443+
*
444+
* @see HardwareCanvas#callDrawGLFunction(int)
445+
* @see #attachFunctor(android.view.View.AttachInfo, int)
446+
*/
447+
abstract void detachFunctor(int functor);
448+
449+
/**
450+
* Schedules the specified functor in the functors execution queue.
451+
*
452+
* @param attachInfo AttachInfo tied to this renderer.
453+
* @param functor The native functor to insert in the execution queue.
454+
*
455+
* @see HardwareCanvas#callDrawGLFunction(int)
456+
* @see #detachFunctor(int)
457+
*/
458+
abstract void attachFunctor(View.AttachInfo attachInfo, int functor);
459+
439460
/**
440461
* Initializes the hardware renderer for the specified surface and setup the
441462
* renderer for drawing, if needed. This is invoked when the ViewAncestor has
@@ -1202,13 +1223,33 @@ private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
12021223
}
12031224

12041225
if ((status & DisplayList.STATUS_INVOKE) != 0) {
1205-
attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
1206-
mFunctorsRunnable.attachInfo = attachInfo;
1226+
scheduleFunctors(attachInfo);
1227+
}
1228+
}
1229+
1230+
private void scheduleFunctors(View.AttachInfo attachInfo) {
1231+
mFunctorsRunnable.attachInfo = attachInfo;
1232+
if (!attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) {
12071233
// delay the functor callback by a few ms so it isn't polled constantly
12081234
attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
12091235
}
12101236
}
12111237

1238+
@Override
1239+
void detachFunctor(int functor) {
1240+
if (mCanvas != null) {
1241+
mCanvas.detachFunctor(functor);
1242+
}
1243+
}
1244+
1245+
@Override
1246+
void attachFunctor(View.AttachInfo attachInfo, int functor) {
1247+
if (mCanvas != null) {
1248+
mCanvas.attachFunctor(functor);
1249+
scheduleFunctors(attachInfo);
1250+
}
1251+
}
1252+
12121253
/**
12131254
* Ensures the current EGL context is the one we expect.
12141255
*

core/java/android/view/ViewRootImpl.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
import android.util.Log;
6060
import android.util.Slog;
6161
import android.util.TypedValue;
62-
import android.view.KeyCharacterMap.FallbackAction;
6362
import android.view.View.AttachInfo;
6463
import android.view.View.MeasureSpec;
6564
import android.view.accessibility.AccessibilityEvent;
@@ -669,6 +668,18 @@ void destroyHardwareLayers() {
669668
}
670669
}
671670

671+
public void attachFunctor(int functor) {
672+
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
673+
mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
674+
}
675+
}
676+
677+
public void detachFunctor(int functor) {
678+
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
679+
mAttachInfo.mHardwareRenderer.detachFunctor(functor);
680+
}
681+
}
682+
672683
private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
673684
mAttachInfo.mHardwareAccelerated = false;
674685
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -4489,8 +4500,8 @@ private void postSendWindowContentChangedCallback(View source) {
44894500
mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent,
44904501
ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
44914502
} else {
4492-
View newSource = getCommonPredecessor(oldSource, source);
4493-
mSendWindowContentChangedAccessibilityEvent.mSource = newSource;
4503+
mSendWindowContentChangedAccessibilityEvent.mSource =
4504+
getCommonPredecessor(oldSource, source);
44944505
}
44954506
}
44964507

core/java/android/webkit/WebViewClassic.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2469,7 +2469,7 @@ public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback)
24692469

24702470
/**
24712471
* Loads the view data from the input stream. See
2472-
* {@link #saveViewState(OutputStream)} for more information.
2472+
* {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information.
24732473
* @param stream The {@link InputStream} to load from
24742474
*/
24752475
public void loadViewState(InputStream stream) {
@@ -5630,6 +5630,13 @@ public void onDetachedFromWindow() {
56305630

56315631
removeAccessibilityApisFromJavaScript();
56325632
updateHwAccelerated();
5633+
5634+
if (mWebView.isHardwareAccelerated()) {
5635+
int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
5636+
if (drawGLFunction != 0) {
5637+
mWebView.getViewRootImpl().detachFunctor(drawGLFunction);
5638+
}
5639+
}
56335640
}
56345641

56355642
@Override

core/jni/android_view_GLES20Canvas.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,21 @@ static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz)
158158
// ----------------------------------------------------------------------------
159159

160160
static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
161-
OpenGLRenderer* renderer, Functor *functor) {
161+
OpenGLRenderer* renderer, Functor* functor) {
162162
android::uirenderer::Rect dirty;
163163
return renderer->callDrawGLFunction(functor, dirty);
164164
}
165165

166+
static void android_view_GLES20Canvas_detachFunctor(JNIEnv* env,
167+
jobject clazz, OpenGLRenderer* renderer, Functor* functor) {
168+
renderer->detachFunctor(functor);
169+
}
170+
171+
static void android_view_GLES20Canvas_attachFunctor(JNIEnv* env,
172+
jobject clazz, OpenGLRenderer* renderer, Functor* functor) {
173+
renderer->attachFunctor(functor);
174+
}
175+
166176
static jint android_view_GLES20Canvas_invokeFunctors(JNIEnv* env,
167177
jobject clazz, OpenGLRenderer* renderer, jobject dirty) {
168178
android::uirenderer::Rect bounds;
@@ -825,9 +835,9 @@ static JNINativeMethod gMethods[] = {
825835
{ "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
826836

827837
#ifdef USE_OPENGL_RENDERER
828-
{ "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
829-
{ "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
830-
{ "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
838+
{ "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
839+
{ "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
840+
{ "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
831841

832842
{ "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
833843
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
@@ -839,7 +849,9 @@ static JNINativeMethod gMethods[] = {
839849
{ "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
840850

841851
{ "nCallDrawGLFunction", "(II)I", (void*) android_view_GLES20Canvas_callDrawGLFunction },
842-
{ "nInvokeFunctors", "(ILandroid/graphics/Rect;)I",
852+
{ "nDetachFunctor", "(II)V", (void*) android_view_GLES20Canvas_detachFunctor },
853+
{ "nAttachFunctor", "(II)V", (void*) android_view_GLES20Canvas_attachFunctor },
854+
{ "nInvokeFunctors", "(ILandroid/graphics/Rect;)I",
843855
(void*) android_view_GLES20Canvas_invokeFunctors },
844856

845857
{ "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },

libs/hwui/OpenGLRenderer.cpp

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -237,33 +237,43 @@ void OpenGLRenderer::resume() {
237237
glBlendEquation(GL_FUNC_ADD);
238238
}
239239

240+
void OpenGLRenderer::detachFunctor(Functor* functor) {
241+
mFunctors.remove(functor);
242+
}
243+
244+
void OpenGLRenderer::attachFunctor(Functor* functor) {
245+
mFunctors.add(functor);
246+
}
247+
240248
status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
241249
status_t result = DrawGlInfo::kStatusDone;
250+
size_t count = functors.size();
242251

243-
Vector<Functor*> functors(mFunctors);
244-
mFunctors.clear();
252+
if (count > 0) {
253+
SortedVector<Functor*> functors(mFunctors);
254+
mFunctors.clear();
245255

246-
DrawGlInfo info;
247-
info.clipLeft = 0;
248-
info.clipTop = 0;
249-
info.clipRight = 0;
250-
info.clipBottom = 0;
251-
info.isLayer = false;
252-
info.width = 0;
253-
info.height = 0;
254-
memset(info.transform, 0, sizeof(float) * 16);
256+
DrawGlInfo info;
257+
info.clipLeft = 0;
258+
info.clipTop = 0;
259+
info.clipRight = 0;
260+
info.clipBottom = 0;
261+
info.isLayer = false;
262+
info.width = 0;
263+
info.height = 0;
264+
memset(info.transform, 0, sizeof(float) * 16);
255265

256-
size_t count = functors.size();
257-
for (size_t i = 0; i < count; i++) {
258-
Functor* f = functors.itemAt(i);
259-
result |= (*f)(DrawGlInfo::kModeProcess, &info);
266+
for (size_t i = 0; i < count; i++) {
267+
Functor* f = functors.itemAt(i);
268+
result |= (*f)(DrawGlInfo::kModeProcess, &info);
260269

261-
if (result != DrawGlInfo::kStatusDone) {
262-
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
263-
dirty.unionWith(localDirty);
270+
if (result != DrawGlInfo::kStatusDone) {
271+
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
272+
dirty.unionWith(localDirty);
264273

265-
if (result & DrawGlInfo::kStatusInvoke) {
266-
mFunctors.push(f);
274+
if (result & DrawGlInfo::kStatusInvoke) {
275+
mFunctors.add(f);
276+
}
267277
}
268278
}
269279
}
@@ -305,7 +315,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
305315
dirty.unionWith(localDirty);
306316

307317
if (result & DrawGlInfo::kStatusInvoke) {
308-
mFunctors.push(functor);
318+
mFunctors.add(functor);
309319
}
310320
}
311321

libs/hwui/OpenGLRenderer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include <utils/Functor.h>
3131
#include <utils/RefBase.h>
32+
#include <utils/SortedVector.h>
3233
#include <utils/Vector.h>
3334

3435
#include <cutils/compiler.h>
@@ -73,6 +74,8 @@ class OpenGLRenderer {
7374
virtual void resume();
7475

7576
ANDROID_API status_t invokeFunctors(Rect& dirty);
77+
ANDROID_API void detachFunctor(Functor* functor);
78+
ANDROID_API void attachFunctor(Functor* functor);
7679
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
7780

7881
ANDROID_API int getSaveCount() const;
@@ -612,7 +615,7 @@ class OpenGLRenderer {
612615
// List of rectangles to clear after saveLayer() is invoked
613616
Vector<Rect*> mLayers;
614617
// List of functors to invoke after a frame is drawn
615-
Vector<Functor*> mFunctors;
618+
SortedVector<Functor*> mFunctors;
616619

617620
// Indentity matrix
618621
const mat4 mIdentity;

0 commit comments

Comments
 (0)