Skip to content

[Android] ImageReaderSurfaceProducer.waitOnFence leaks one sync_file FD per frame (image.getFence() never closed) → freeze at FD limit (API 33+) #188161

Description

@maCStacker

Steps to reproduce

Display a continuously rendering external texture (e.g. a WebRTC video stream via flutter_webrtc ≥ 0.12.12, which uses the new TextureRegistry.SurfaceProducer API) in fullscreen on an Android 13+ device, and leave it running.

Expected results

File-descriptor count stays stable; the stream keeps rendering indefinitely.

Actual results

The process leaks one anon:sync_file file descriptor per rendered frame and hits the 32768 RLIMIT_NOFILE ceiling in ~12 min (~2700 fd/min on the test device). After that all new fd operations fail ("Too many open files"), rendering freezes and the app is effectively dead.

Root cause: in the engine, ImageReaderSurfaceProducer.waitOnFence(Image) (and the twin in ImageTextureRegistryEntry) calls image.getFence() and awaitForever() but never fence.close(). SyncFence is AutoCloseable and owns a sync_file fd that the receiver must close. Across the Android embedding there are 2x
getFence() and 0x fence.close(). The fence-wait path is gated on SDK_INT >= 33, so it affects Android 13+. Present in stable 3.41.7 and current master.

Suggested fix — close the fence after awaiting (try-with-resources):
try (SyncFence fence = image.getFence()) {
fence.awaitForever();
} catch (IOException e) { /* drop */ }

Code sample

Code sample // Flutter engine source: FlutterRenderer.java -> ImageReaderSurfaceProducer.waitOnFence // (stable 3.41.7 and master) @RequiresApi(API_LEVELS.API_33) private void waitOnFence(Image image) { try { SyncFence fence = image.getFence(); fence.awaitForever(); } catch (IOException e) { // Drop. } } // fence (a SyncFence wrapping a sync_file FD) is never close()d.

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
Samsung SM-P619, Android 14. Per-minute fd breakdown from /proc/self/fd:
  anon:sync_file 2842 -> 32129 over ~12 min, while socket/file/etc. stay flat.
  Then: fcntl(F_DUPFD_CLOEXEC) failed ... Too many open files.
  Forcing the legacy SurfaceTexture path (debugForceSurfaceProducerGlTextures=true) did NOT reliably avoid it.
  Related: #143777 (closed not planned), #84702, flutter-webrtc/flutter-webrtc#1974.

Flutter Doctor output

Doctor output
  [✓] Flutter (Channel stable, 3.41.7, on Linux Mint 22.3 6.17.0-23-generic, locale de_DE.UTF-8)
      • Flutter version 3.41.7 on channel stable at /home/macyo/development/flutter
      • Framework revision cc0734ac71 (2026-04-15), Engine revision 59aa584fdf
      • Dart version 3.11.5, DevTools version 2.54.2
  [✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
      • Platform android-36, build-tools 35.0.0
      • Java version OpenJDK Runtime Environment (build 17.0.18)
      • All Android licenses accepted.
  [✓] Chrome - develop for the web
  [✓] Linux toolchain - develop for Linux desktop

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority issues at the top of the work listengineflutter/engine related. See also e: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-androidAndroid applications specificallyteam-androidOwned by Android platform teamtriaged-androidTriaged by Android platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions