Skip to content

ANR on Android 16: main thread blocks in Shell::OnPlatformViewDestroyed while raster thread hangs in Surface::dequeueBuffer during backgrounding (Impeller GLES) #190599

Closed

Description

@bariskodas

Steps to reproduce

Not reproducible on demand — this is a recurring production ANR reported by Google Play Console (user-perceived ANR clusters) for a Flutter paint-by-number game. It fires when the app is sent to background: one dump is under Activity.performStop, the other under View.dispatchWindowVisibilityChanged — both reach FlutterSurfaceView → surfaceDestroyed.

Both occurrences are Android 16 (SDK 36) tablets with Adreno GPUs (Samsung Galaxy Tab A9+, Honor Pad X8a), rendering with Impeller on the OpenGLES backend (GPUSurfaceGLImpeller visible in the raster stack). Three Play Console clusters share this exact signature (8 users / 8 events in the last 28 days for v352 of the app).

Expected results

Backgrounding the app tears down the surface without blocking the Android main thread indefinitely.

Actual results

The main thread blocks in Shell::OnPlatformViewDestroyed waiting on fml::AutoResetWaitableEvent, while the raster thread is mid-frame inside Surface::dequeueBuffer, waiting for a free buffer slot on a window that is being hidden — with Android 16's BLAST buffer queue that slot is never released, so neither thread ever makes progress → "Input dispatching timed out" ANR.

Main thread (identical in both dumps):

"main" tid=1 Native
  #02 pc ... libc.so (pthread_cond_wait+76)
  #03 pc ... libapp/libflutter (std::__fl::condition_variable::wait(std::__fl::unique_lock<std::__fl::mutex>&))
  #04 pc ... (fml::AutoResetWaitableEvent::Wait())
  #05 pc ... (flutter::Shell::OnPlatformViewDestroyed())
  #06 pc ... (flutter::PlatformViewAndroid::NotifyDestroyed())
  #07 pc ... (flutter::SurfaceDestroyed(_JNIEnv*, _jobject*, long))
  at io.flutter.embedding.engine.FlutterJNI.onSurfaceDestroyed (FlutterJNI.java:669)
  at io.flutter.embedding.engine.renderer.FlutterRenderer.stopRenderingToSurface (FlutterRenderer.java:1170)
  at io.flutter.embedding.android.FlutterSurfaceView$1.surfaceDestroyed (FlutterSurfaceView.java:81)
  -- dump A: at android.view.SurfaceView.onWindowVisibilityChanged / ViewRootImpl.performTraversals
  -- dump B: at android.view.SurfaceView.setWindowStopped / android.app.Activity.performStop (Activity.java:9947)

Raster thread, dump A (Galaxy Tab A9+, Android 16):

"1.raster" tid=108 Native
  #02 pc ... libc.so (pthread_cond_clockwait+180)
  #03 pc ... libgui.so (android::BufferQueueProducer::waitForFreeSlotThenRelock const+840)
  #04 pc ... libgui.so (android::BufferQueueProducer::dequeueBuffer+564)
  #05 pc ... libgui.so (android::Surface::dequeueBuffer+436)
  #06-#12 vendor Adreno EGL (eglSubDriverAndroid.so / libGLESv2_adreno.so)
  #13 pc ... (impeller::RenderPassGLES::OnEncodeCommands ... $_0::operator())
  #14 pc ... (impeller::ReactorGLES::React())
  #15 pc ... (impeller::Canvas::EndReplay())
  #16 pc ... (impeller::RenderToTarget(...))
  #17 pc ... (flutter::GPUSurfaceGLImpeller::AcquireFrame(...)::$_1 ... operator())
  #18 pc ... (flutter::SurfaceFrame::Submit())
  #19 pc ... (flutter::Rasterizer::DrawToSurfacesUnsafe(...))
  #23 pc ... (flutter::Shell::OnAnimatorDraw ...)

Raster thread, dump B (Honor Pad X8a, Android 16) — identical shape, with the SDK-36 BLAST queue visible:

"1.raster" tid=112 Native
  #01 pc ... libgui.so (android::BBQBufferQueueProducer::waitForBufferRelease const+380)
  #02 pc ... libgui.so (android::BufferQueueProducer::dequeueBuffer+1172)
  #03 pc ... libgui.so (android::Surface::dequeueBuffer+1256)
  #04-#07 vendor Adreno EGL
  ... (same impeller::RenderPassGLES / ReactorGLES / Rasterizer frames as dump A)

Engine BuildId in both dumps: 0d216f3f630e1105257f8a3f74e22c9b43c22a51.

Possibly the same family as #107351 and #170196, but this variant is specifically the destroy handshake racing a mid-frame dequeueBuffer on a hidden window, which Android 16's buffer accounting seems to make persistent rather than transient.

Happy to attach the full Play Console ANR thread dumps if useful.

Code sample

N/A — production ANR; app uses a standard FlutterActivity (FlutterSurfaceView), no platform views on the affected screens.

Flutter version

Flutter 3.41.9 (stable, via FVM); flutter build apk --release, Impeller default-enabled (GLES backend on the affected devices).

Activity

  1. bariskodas commented on Aug 5, 2026

    @bariskodas
    Author

    Additional data point — a third production dump shows the same stall on the resize path (Shell::OnPlatformViewSurfaceChanged handshake), not just destroy:

    Main thread (Honor Pad X8a, Android 16 / SDK 36 — same device model and same Adreno EGL driver build as dump B above):

    "main" tid=1 Native
      #02 pc ... libc.so (pthread_cond_wait+72)
      #03 pc ... (std::__fl::condition_variable::wait)
      #04 pc ... (fml::AutoResetWaitableEvent::Wait())
      #05 pc ... (flutter::SurfaceChanged(_JNIEnv*, _jobject*, long, int, int))
      at io.flutter.embedding.engine.FlutterJNI.onSurfaceChanged (FlutterJNI.java:652)
      at io.flutter.embedding.android.FlutterSurfaceView$1.surfaceChanged (FlutterSurfaceView.java:71)
      at android.view.SurfaceView.updateSurface (SurfaceView.java:1577)
      at android.view.SurfaceView.setFrame (SurfaceView.java:729)   <- resize during a layout pass
      ...
    

    Raster thread — byte-for-byte the same state as the destroy dumps:

    "1.raster" tid=101 Native
      #01 pc ... libgui.so (android::BBQBufferQueueProducer::waitForBufferRelease const+380)
      #02 pc ... libgui.so (android::BufferQueueProducer::dequeueBuffer+1172)
      #03 pc ... libgui.so (android::Surface::dequeueBuffer+1256)
      #04-#10 vendor Adreno EGL
      #11 pc ... (impeller::RenderPassGLES::OnEncodeCommands ... operator())
      ...
    

    So any of the AutoResetWaitableEvent surface handshakes (changed and destroyed) can deadlock against a raster thread that is mid-frame in dequeueBuffer on an SDK-36 BLAST queue.

  2. bariskodas commented on Aug 5, 2026

    @bariskodas
    Author

    Third independent production occurrence of the destroy-variant (Play Console, v3.5.2, Honor Pad X8a / Android 16 / Impeller GLES — same device model as the earlier dumps, 23 days ago). Both halves of the deadlock are again visible simultaneously in one dump:

    "main":
      std::__fl::condition_variable::wait
      fml::AutoResetWaitableEvent::Wait()
      flutter::Shell::OnPlatformViewDestroyed()
      flutter::PlatformViewAndroid::NotifyDestroyed()
      flutter::SurfaceDestroyed (JNI)
      ← android.view.SurfaceView.notifySurfaceDestroyed ← Activity.performStop
    
    "1.raster":
      libgui BBQBufferQueueProducer::waitForBufferRelease
      BufferQueueProducer::dequeueBuffer
      Surface::dequeueBuffer
      ← Adreno EGL ← impeller::RenderPassGLES … flutter::Rasterizer::Draw
    

    Main is latched on the raster thread acknowledging surface teardown; raster is blocked dequeuing a buffer on the window being hidden, which is never released. Consistent recurrence on SDK 36 budget tablets with Impeller GLES.

  3. saurabh-mirajkar commented on Aug 6, 2026

    @saurabh-mirajkar
    Contributor

    @bariskodas Thanks for the detailed report. The stack traces are helpful, but the issue is still based on production ANR reports without a reproducible case.

    This also appears related to #169585, which reports a similar production ANR.

    Since you're currently using Flutter 3.41.9, could you first verify whether this still occurs on the latest stable 3.44.8? If it does, please let us know whether the stack traces remain the same after upgrading.

    If you're able to identify a reproducible sample or reliable reproduction steps, that would make the issue actionable for the team to investigate.

  4. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    needs repro infoAutomated crash report whose cause isn't yet known
    and removed
    in triagePresently being triaged by the triage team
    on Aug 6, 2026
  5. github-actions commented on Aug 27, 2026

    @github-actions

    Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now.

    If you find this problem please file a new issue with the same description, what happens, logs and the output of 'flutter doctor -v'. All system setups can be slightly different so it's always better to open new issues and reference the related ones.

    Thanks for your contribution.

  6. github-actions commented on Sep 10, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  7. locked as resolved and limited conversation to collaborators on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs repro infoAutomated crash report whose cause isn't yet knownwaiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions