diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 96b1abb62dbf4..5c89e2215a9f7 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1200,8 +1200,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } mTransformHint = viewRoot.getBufferTransformHint(); mBlastSurfaceControl.setTransformHint(mTransformHint); + mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */); mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat); + mBlastBufferQueue.setTransactionHangCallback(ViewRootImpl.sTransactionHangCallback); } private void onDrawFinished() { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 27ce711558573..3ebe7436fdaf9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -851,6 +851,28 @@ public final class ViewRootImpl implements ViewParent, */ private Bundle mRelayoutBundle = new Bundle(); + private static volatile boolean sAnrReported = false; + static BLASTBufferQueue.TransactionHangCallback sTransactionHangCallback = + new BLASTBufferQueue.TransactionHangCallback() { + @Override + public void onTransactionHang(boolean isGPUHang) { + if (isGPUHang && !sAnrReported) { + sAnrReported = true; + try { + ActivityManager.getService().appNotResponding( + "Buffer processing hung up due to stuck fence. Indicates GPU hang"); + } catch (RemoteException e) { + // We asked the system to crash us, but the system + // already crashed. Unfortunately things may be + // out of control. + } + } else { + // TODO: Do something with this later. For now we just ANR + // in dequeue buffer later like we always have. + } + } + }; + private String mTag = TAG; public ViewRootImpl(Context context, Display display) { @@ -2086,6 +2108,7 @@ public final class ViewRootImpl implements ViewParent, } mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format); + mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback); Surface blastSurface = mBlastBufferQueue.createSurface(); // Only call transferFrom if the surface has changed to prevent inc the generation ID and // causing EGL resources to be recreated. diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp index c0f7b41d116d1..4af28ea243611 100644 --- a/core/jni/android_graphics_BLASTBufferQueue.cpp +++ b/core/jni/android_graphics_BLASTBufferQueue.cpp @@ -47,6 +47,43 @@ static JNIEnv* getenv(JavaVM* vm) { return env; } + struct { + jmethodID onTransactionHang; +} gTransactionHangCallback; + +class TransactionHangCallbackWrapper : public LightRefBase { +public: + explicit TransactionHangCallbackWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); + mTransactionHangObject = env->NewGlobalRef(jobject); + LOG_ALWAYS_FATAL_IF(!mTransactionHangObject, "Failed to make global ref"); + } + + ~TransactionHangCallbackWrapper() { + if (mTransactionHangObject) { + getenv()->DeleteGlobalRef(mTransactionHangObject); + mTransactionHangObject = nullptr; + } + } + + void onTransactionHang(bool isGpuHang) { + if (mTransactionHangObject) { + getenv()->CallVoidMethod(mTransactionHangObject, + gTransactionHangCallback.onTransactionHang, isGpuHang); + } + } + +private: + JavaVM* mVm; + jobject mTransactionHangObject; + + JNIEnv* getenv() { + JNIEnv* env; + mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + return env; + } +}; + static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jboolean updateDestinationFrame) { ScopedUtfChars name(env, jName); @@ -141,6 +178,20 @@ static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlo sp queue = reinterpret_cast(ptr); return queue->isSameSurfaceControl(reinterpret_cast(surfaceControl)); } + +static void nativeSetTransactionHangCallback(JNIEnv* env, jclass clazz, jlong ptr, + jobject transactionHangCallback) { + sp queue = reinterpret_cast(ptr); + if (transactionHangCallback == nullptr) { + queue->setTransactionHangCallback(nullptr); + } else { + sp wrapper = + new TransactionHangCallbackWrapper{env, transactionHangCallback}; + queue->setTransactionHangCallback([wrapper](bool isGpuHang) { + wrapper->onTransactionHang(isGpuHang); + }); + } +} static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) { @@ -163,7 +214,10 @@ static const JNINativeMethod gMethods[] = { {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum}, {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions}, {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl}, - {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions} + {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions}, + {"nativeSetTransactionHangCallback", + "(JLandroid/graphics/BLASTBufferQueue$TransactionHangCallback;)V", + (void*)nativeSetTransactionHangCallback}, // clang-format on }; @@ -180,6 +234,11 @@ int register_android_graphics_BLASTBufferQueue(JNIEnv* env) { jclass consumer = FindClassOrDie(env, "java/util/function/Consumer"); gTransactionConsumer.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V"); + jclass transactionHangClass = + FindClassOrDie(env, "android/graphics/BLASTBufferQueue$TransactionHangCallback"); + gTransactionHangCallback.onTransactionHang = + GetMethodIDOrDie(env, transactionHangClass, "onTransactionHang", "(Z)V"); + return 0; } diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 4b723d1569c9a..1c41d06a3da20 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -43,6 +43,12 @@ public final class BLASTBufferQueue { private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr); private static native SurfaceControl.Transaction nativeGatherPendingTransactions(long ptr, long frameNumber); + private static native void nativeSetTransactionHangCallback(long ptr, + TransactionHangCallback callback); + + public interface TransactionHangCallback { + void onTransactionHang(boolean isGpuHang); + } /** Create a new connection with the surface flinger. */ public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height, @@ -184,4 +190,8 @@ public final class BLASTBufferQueue { public SurfaceControl.Transaction gatherPendingTransactions(long frameNumber) { return nativeGatherPendingTransactions(mNativeObject, frameNumber); } + + public void setTransactionHangCallback(TransactionHangCallback hangCallback) { + nativeSetTransactionHangCallback(mNativeObject, hangCallback); + } }