Skip to content

[in_app_purchase_android] Uncatchable SIGABRT: reconnect issues startConnection synchronously from the onBillingServiceDisconnected platform-message upcall #191831

Open
flutter/packages
#12639

Description

@eszter-varga

Steps to reproduce

Re-filing of #188088, which was closed by the bot for missing repro info
while the triage question was unanswered. The requested information is
included up front this time; a PR with the fix and a regression test
accompanies this issue.

Hard to force deterministically — it needs the Play Billing service to flap
(e.g. Play Store self-updating, or a device in a bad state) right around the
moment the app is foregrounded, while BillingClientManager is connected.

  1. App is backgrounded; the Play Billing service binding is lost.
  2. App is foregrounded; something probes billing (e.g.
    InAppPurchase.instance.isAvailable() / restorePurchases()).
  3. The platform fires onBillingServiceDisconnected.
  4. BillingClientManager._connect immediately calls
    BillingClient.startConnection synchronously from within that callback.

Answering the triage question from #188088 (“does it reproduce with the
latest stable Flutter and the latest in_app_purchase_android release?”):

  • The defective code path is unchanged in the latest release (0.5.2,
    2026-07-20) and on main — re-verified at code level on 2026-08-26:
    _connect() still wraps startConnection in Future<void>.sync(...),
    which never yields before the platform send, so the send still runs
    synchronously on the disconnect upcall. There is no
    Future.delayed/Timer anywhere in billing_client_manager.dart.
  • The production crashes were captured by Sentry on 0.4.0+8 (2026-06-10 and
    2026-06-11, identical stacks). Because the trigger is a Play-service flap,
    it cannot be reproduced on demand on a healthy device — but the crashing
    code is byte-identical in 0.5.2, so the version bump cannot have fixed it.
  • Related [in_app_purchase][android] BillingClient connection is not restored after it's lost #110663 is a different issue: that was about the connection not
    being restored
    ; this one is about how the reconnect is issued (a
    synchronous, re-entrant platform send inside the JNI upcall).

Expected results

A transient disconnect leads to a (possibly failed) reconnect that surfaces as
a Dart PlatformException, never a process abort.

Actual results

The process aborts with an uncatchable SIGABRT.

Crash (abridged):

SIGABRT: Abort
  ... fml::KillProcess
  ... flutter::PlatformViewAndroidJNIImpl::FlutterViewHandlePlatformMessage
        (platform_view_android_jni_impl.cc:1348)   # FML_CHECK(CheckException(env))
  ...
  InAppPurchaseApi.startConnection (messages.g.dart)
  BillingClient.startConnection (billing_client_wrapper.dart)
  BillingClientManager._connect (billing_client_manager.dart)
  Future.sync (future.dart)
  BillingClientManager._connect (billing_client_manager.dart)
  HostBillingClientCallbackHandler.onBillingServiceDisconnected (billing_client_wrapper.dart)
  InAppPurchaseCallbackApi.setUp.<…> (messages.g.dart)
  PlatformDispatcher._dispatchPlatformMessage (platform_dispatcher.dart)

Analysis

onBillingServiceDisconnected is delivered as an inbound platform
message, handled on the merged UI/platform thread inside a JNI upcall.
_connect then sends startConnection — an outbound platform message —
synchronously from that same stack (Future.sync runs the closure with no
event-loop yield). When the billing service is flapping, the Java side has a
pending exception at the moment the engine runs
FML_CHECK(fml::jni::CheckException(env)) in
FlutterViewHandlePlatformMessage, so it aborts. This is uncatchable from
Dart
(see #144954).

Proposed fix

Yield one macrotask before the platform send so it never runs synchronously on
the disconnect upcall:

_readyFuture = Future<void>.sync(() async {
  await Future<void>.delayed(Duration.zero); // escape the JNI upcall
  if (_isDisposed) { _isConnecting = false; return; }
  await client.startConnection(...);
  _isConnecting = false;
});

A microtask is not sufficient (microtasks drain before control returns to
the engine); a zero-duration timer is. A PR implementing exactly this (plus a
regression test) is attached.

Related (separate): the same _connect reconnects with no cap, so a
sustained service flap storms bindService
(IllegalStateException: Too many bind requests(999+)) — also an uncatchable
abort. Worth bounding (Dart-side backoff, or enabling Billing 8's
enableAutoServiceReconnection() in BillingClientFactoryImpl), but
separable from the fix above.

Environment

  • in_app_purchase_android: crash captured on 0.4.0+8; code-verified
    unfixed on 0.5.2 (latest release) and main as of 2026-08-26.
  • Flutter: 3.44.4 (stable), Dart 3.12.2
  • Android: production fleet (the two Sentry-captured occurrences above, both
    ~22–23 s after foregrounding, with identical stacks).
flutter doctor -v
[✓] Flutter (Channel stable, 3.44.4, on Debian GNU/Linux 12 (bookworm) 6.1.0-37-amd64, locale en_US.UTF-8) [216ms]
    • Flutter version 3.44.4 on channel stable at /home/elinorapricot/devtools/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ad70ec4617 (9 weeks ago), 2026-06-24 11:07:06 -0700
    • Engine revision a10d8ac38d
    • Dart version 3.12.2
    • DevTools version 2.57.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration

[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0) [13.6s]
    • Android SDK at /home/elinorapricot/Android/Sdk/
    • Emulator version 36.5.11.0 (build_id 15261927) (CL:N/A)
    • Platform android-36.1, build-tools 36.1.0
    • Java binary at: /home/elinorapricot/Tools/android-studio/jbr/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11609105)
    • All Android licenses accepted.

[✓] Chrome - develop for the web [72ms]
    • CHROME_EXECUTABLE = /usr/bin/chromium

[✓] Linux toolchain - develop for Linux desktop [1,879ms]
    • Debian clang version 14.0.6
    • cmake version 3.25.1
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    • GL_EXT_framebuffer_blit: no
    • GL_EXT_texture_format_BGRA8888: no

[✓] Connected device (3 available) [1,932ms]
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 16 (API 36) (emulator)
    • Linux (desktop)              • linux         • linux-x64      • Debian GNU/Linux 12 (bookworm) 6.1.0-37-amd64
    • Chrome (web)                 • chrome        • web-javascript • Chromium 140.0.7339.127 built on Debian GNU/Linux 12 (bookworm)

[✓] Network resources [1,087ms]
    • All expected network resources are available.

• No issues found!

Activity

  1. suryaKommana2662 commented on Aug 27, 2026

    @suryaKommana2662
    Contributor

    @eszter-varga Thanks for the detailed report and the additional reproduction information. I was not able to reproduce the crash locally, as the reported behavior depends on the Play Billing service disconnecting/flapping during the reconnect sequence. The provided crash stack, analysis, and linked PR provide useful information. Routing this to team-android for further investigation.

  2. added
    team-androidOwned by Android platform team
    fyi-ecosystemFor the attention of Ecosystem team
    packageflutter/packages repository. See also p: labels.
    platform-androidAndroid applications specifically
    and removed
    in triagePresently being triaged by the triage team
    on Aug 27, 2026
  3. added
    triaged-androidTriaged by Android platform team
    P2Important issues not at the top of the work list
    on Sep 1, 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

    P2Important issues not at the top of the work listp: in_app_purchasePlugin for in-app purchasepackageflutter/packages repository. See also p: labels.platform-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