SurfaceView: Force surface disconnection before reuse.

Prior to N, if a client received the surfaceDestroyed callback
from their SurfaceView they could trust that the surface would not be
reused. Now that is not true in multiple scenarios. If a client fails
to shut down its EGL context then, we could end up in a situation where,
the client will never disconnect. Then when we reuse the same
underlying IGraphicBufferProducer for surfaceCreated next
the app will crash with a double connect error. There is no valid
use of the surface inbetween surfaceDestroyed and surfaceCreated,
so we just force the disconnection after surfaceDestroyed.

Bug: 30236166
Change-Id: I2e3e4b3176492dc0c2d46a59e0b5a781bf9bc356
This commit is contained in:
Robert Carr
2016-09-07 14:12:44 -07:00
parent 735b9eca0f
commit 387838be95
3 changed files with 29 additions and 0 deletions

View File

@@ -59,6 +59,7 @@ public class Surface implements Parcelable {
private static native long nativeGetNextFrameNumber(long nativeObject);
private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
private static native void nativeSetBuffersTransform(long nativeObject, long transform);
private static native int nativeForceScopedDisconnect(long nativeObject);
public static final Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@@ -538,6 +539,16 @@ public class Surface implements Parcelable {
}
}
void forceScopedDisconnect() {
synchronized (mLock) {
checkNotReleasedLocked();
int err = nativeForceScopedDisconnect(mNativeObject);
if (err != 0) {
throw new RuntimeException("Failed to disconnect Surface instance (bad object?)");
}
}
}
/**
* Returns whether or not this Surface is backed by a single-buffered SurfaceTexture
* @hide

View File

@@ -586,6 +586,18 @@ public class SurfaceView extends View {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
// Since Android N the same surface may be reused and given to us
// again by the system server at a later point. However
// as we didn't do this in previous releases, clients weren't
// necessarily required to clean up properly in
// surfaceDestroyed. This leads to problems for example when
// clients don't destroy their EGL context, and try
// and create a new one on the same surface following reuse.
// Since there is no valid use of the surface in-between
// surfaceDestroyed and surfaceCreated, we force a disconnect,
// so the next connect will always work if we end up reusing
// the surface.
mSurface.forceScopedDisconnect();
}
}

View File

@@ -487,6 +487,11 @@ static jint nativeSetScalingMode(JNIEnv *env, jclass clazz, jlong nativeObject,
return surface->setScalingMode(scalingMode);
}
static jint nativeForceScopedDisconnect(JNIEnv *env, jclass clazz, jlong nativeObject) {
Surface* surface = reinterpret_cast<Surface*>(nativeObject);
return surface->disconnect(-1, IGraphicBufferProducer::DisconnectMode::AllLocal);
}
namespace uirenderer {
using namespace android::uirenderer::renderthread;
@@ -564,6 +569,7 @@ static const JNINativeMethod gSurfaceMethods[] = {
{"nativeGetHeight", "(J)I", (void*)nativeGetHeight },
{"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber },
{"nativeSetScalingMode", "(JI)I", (void*)nativeSetScalingMode },
{"nativeForceScopedDisconnect", "(J)I", (void*)nativeForceScopedDisconnect},
// HWUI context
{"nHwuiCreate", "(JJ)J", (void*) hwui::create },