Skip to content

[beta] Expose engineId on native plugin bindings and registrars on Android, iOS, and macOS - #195991

Closed
flutteractionsbot wants to merge 1 commit into
flutter:flutter-3.50-candidate.0from
flutteractionsbot:cp-beta-76395508545ff366c171bf5ab39fc893a9ca3e71
Closed

flutteractionsbot wants to merge 1 commit into
flutter:flutter-3.50-candidate.0from
flutteractionsbot:cp-beta-76395508545ff366c171bf5ab39fc893a9ca3e71

Conversation

@flutteractionsbot

@flutteractionsbot flutteractionsbot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

This pull request is created by automatic cherry pick workflow
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

Issue Link:

#195876

Impact Description:

Native plugins using JNI/FFI (such as plugins generated with Pigeon's native interop support) register directly in native memory rather than on a per-engine BinaryMessenger, and previously had no non-deprecated public API on FlutterPluginBinding (Android) or FlutterPluginRegistrar (iOS/macOS) to obtain the engine identifier during plugin attachment and detachment. In production apps running multiple FlutterEngine instances in the same process (such as background engines alongside the main UI engine), one engine attaching or detaching can overwrite or unregister another engine's JNI/FFI host API instance (#194920). Exposing engineId / engineIdentifier on the native plugin bindings and registrars unblocks per-engine scoping of native FFI/JNI plugin registrations.

Changelog Description:

flutter/195876 Exposes the engine identifier on FlutterPluginBinding (Android) and FlutterPluginRegistrar (iOS and macOS) so native FFI/JNI plugins can scope per-engine registrations in multi-engine apps.

Workaround:

On Android, plugins had to call the deprecated FlutterPluginBinding.getFlutterEngine().getEngineId(). On iOS and macOS, there was no public API workaround on FlutterPluginRegistrar (engineIdentifier was only declared on internal headers, and the weak engine reference on the registrar is already nil during detachFromEngineForRegistrar:).

Risk:

What is the risk level of this cherry-pick?

  • Low
  • Medium
  • High

Test Coverage:

Are you confident that your fix is well-tested by automated tests?

  • Yes
  • No

Validation Steps:

  1. On Android, verify FlutterPluginBinding.getEngineId() returns the associated FlutterEngine.getEngineId().
  2. On iOS, verify registrar.engineIdentifier on FlutterPluginRegistrar and FlutterApplicationRegistrar matches the owning FlutterEngine.engineIdentifier both during registration and during detachFromEngineForRegistrar: after the engine is deallocated.
  3. On macOS, verify registrar.engineIdentifier on FlutterPluginRegistrar matches the owning FlutterEngine.engineIdentifier.

…iOS, and macOS (flutter#195881)

Exposes the engine identifier (corresponding to
`PlatformDispatcher.instance.engineId` in Dart) directly on native
plugin bindings and registrars across Android, iOS, and macOS so that
FFI/JNI plugins can associate and clean up per-engine native state
during plugin registration and detachment without access to the
underlying engine instance:

- **Android**: Adds `FlutterPluginBinding.getEngineId()` returning
`flutterEngine.getEngineId()`.
- **iOS**: Adds `@property(readonly) int64_t engineIdentifier` to
`FlutterBaseRegistrar` (inherited by `FlutterPluginRegistrar` and
`FlutterApplicationRegistrar`), cached at registrar initialization so it
remains valid during `detachFromEngineForRegistrar:` when the weak
engine reference is already `nil`.
- **macOS**: Adds `@property(readonly) int64_t engineIdentifier` to
`FlutterPluginRegistrar`, cached at registrar initialization.

work towards flutter#195876 (linux and
windows will follow in another pr)

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
@flutteractionsbot
flutteractionsbot requested a review from a team as a code owner November 4, 2026 18:51
@flutteractionsbot flutteractionsbot added the cp: review Cherry-picks in the review queue label Nov 4, 2026
@flutteractionsbot
flutteractionsbot requested review from a team as code owners November 4, 2026 18:51
@flutteractionsbot

Copy link
Copy Markdown
Contributor Author

@tarrinneal please fill out the PR description above, afterwards the release team will review this request.

@flutteractionsbot
flutteractionsbot requested review from mboetger and removed request for a team November 4, 2026 18:51
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Nov 4, 2026
@flutter-dashboard

Copy link
Copy Markdown

This pull request was opened from and to a release candidate branch. This should only be done as part of the official Flutter release process. If you are attempting to make a regular contribution to the Flutter project, please close this PR and follow the instructions at Tree Hygiene for detailed instructions on contributing to Flutter.

Reviewers: Use caution before merging pull requests to release branches. Ensure the proper procedure has been followed.

@github-actions github-actions Bot added platform-android Android applications specifically platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. platform-macos Building on or for macOS specifically a: desktop Running on desktop team-android Owned by Android platform team team-ios Owned by iOS platform team team-macos Owned by the macOS platform team labels Nov 4, 2026
@tarrinneal tarrinneal changed the title [CP-beta]Expose engineId on native plugin bindings and registrars on Android, iOS, and macOS [beta] Expose engineId on native plugin bindings and registrars on Android, iOS, and macOS Nov 4, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request exposes the unique identifier of the FlutterEngine (engineId or engineIdentifier) to plugin registrars and bindings across Android, iOS, and macOS platforms, and adds corresponding unit tests. The review feedback points out that adding engineIdentifier as a required property to the public FlutterBaseRegistrar (iOS) and FlutterPluginRegistrar (macOS) protocols is a breaking change for third-party implementations. It is recommended to mark these properties as @optional to maintain backward compatibility.

Comment on lines +291 to +297

/**
* The unique identifier for the `FlutterEngine` associated with this registrar.
*
* This corresponds to `PlatformDispatcher.instance.engineId` in Dart.
*/
@property(nonatomic, readonly) int64_t engineIdentifier;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Adding a new required property to the public FlutterBaseRegistrar protocol is a breaking change for any third-party plugins or applications that implement custom or mock registrars in their unit tests. To maintain backward compatibility, please mark this property as @optional and restore the @required directive for the subsequent properties/methods.

@optional

/**
 * The unique identifier for the FlutterEngine associated with this registrar.
 *
 * This corresponds to PlatformDispatcher.instance.engineId in Dart.
 */
@property(nonatomic, readonly) int64_t engineIdentifier;

@required
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The possible difference is that this is a minor release cherry pick though.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make it optional in the cherry pick and keep required in master?

Comment on lines 29 to +35

/**
* The unique identifier for the `FlutterEngine` associated with this registrar.
*
* This corresponds to `PlatformDispatcher.instance.engineId` in Dart.
*/
@property(nonatomic, readonly) int64_t engineIdentifier;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Adding a new required property to the public FlutterPluginRegistrar protocol is a breaking change for any third-party plugins or applications that implement custom or mock registrars in their unit tests. To maintain backward compatibility, please mark this property as @optional and restore the @required directive for the subsequent properties/methods.

@optional

/**
 * The unique identifier for the FlutterEngine associated with this registrar.
 *
 * This corresponds to PlatformDispatcher.instance.engineId in Dart.
 */
@property(nonatomic, readonly) int64_t engineIdentifier;

@required
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

@zanderso zanderso left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This CP is under discussion today.

@zanderso

zanderso commented Nov 5, 2026

Copy link
Copy Markdown
Member

I'm going to go ahead and close this. A breaking change to support feature work is not suitable for a cherry-pick: https://github.com/flutter/flutter/blob/master/docs/releases/Flutter-Cherrypick-Process.md

A breaking change should also follow our breaking change processes: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes

In terms of making this breaking on master and non-breaking on stable by making the new property @optional, we typically only allow stable and master to diverge when it's required for our infrastructure to function properly, but AFAIK there's no precedent for doing it in other cases.

@zanderso zanderso closed this Nov 5, 2026
@flutteractionsbot
flutteractionsbot deleted the cp-beta-76395508545ff366c171bf5ab39fc893a9ca3e71 branch November 5, 2026 16:32
@stuartmorgan

Copy link
Copy Markdown
Contributor

FWIW I can't remember any case where additions to the plugin API surface were considered a breaking change before. There's no plausible use case for non-test code to implement these surfaces, and things that only break the tests of plugin developers (and of that very small subset of people, only those that have native unit tests, which anecdotally is a significantly smaller subset, and of those only those who unit test the parts that require mocking this surface) have an extremely small impact since there's no ecosystem-level effect (unlike changes that break package code that is consumed by clients).

In the past we just adapted to the changes in Flutter's own rollers, and didn't consider it formally breaking.

@zanderso

zanderso commented Nov 6, 2026

Copy link
Copy Markdown
Member

Thanks @stuartmorgan, I apologize that it wasn't clear to me from the discussion that the change would only affect rarely-written unit tests and not production code. A quick decision was needed and in the presence of tests broken internally, we made a conservative choice. If this change is safer than our coarse-grained signals indicated, then we should consider it for a hotfix release in this cycle. cc @tarrinneal

auto-submit Bot pushed a commit that referenced this pull request Nov 9, 2026
…droid, iOS, and macOS (#196145)

### Issue Link:
#195876

### Impact Description:
Native plugins using JNI/FFI (such as plugins generated with Pigeon's native interop support) register directly in native memory rather than on a per-engine `BinaryMessenger`, and have no non-deprecated public API on `FlutterPluginBinding` (Android) or `FlutterPluginRegistrar` (iOS/macOS) to obtain the engine identifier during plugin attachment and detachment. In apps running multiple `FlutterEngine` instances in the same process (such as background engines alongside the main UI engine), one engine attaching or detaching can overwrite or unregister another engine's JNI/FFI host API instance (#194920). This blocks shipping multi-engine-safe FFI/JNI plugins (flutter/packages#13359) against the 3.50 stable release.

**Scope of the API break:** This supersedes #195991, which was closed as a breaking change. As [@stuartmorgan noted](#195991 (comment)), the `@required` `engineIdentifier` property on `FlutterBaseRegistrar` (iOS) and `FlutterPluginRegistrar` (macOS) only affects hand-written native unit-test fakes that conform to those protocols directly. Production plugin and app code only consumes registrars provided by the engine and is unaffected. Following that, [@zanderso commented](#195991 (comment)) that if the change is safer than the earlier signals indicated, it should be considered for a hotfix release in this cycle. Affected test fakes can add `var engineIdentifier: Int64 = 0` (Swift) or `@synthesize engineIdentifier;` (Objective-C), which compiles both before and after this change. First-party fakes were already updated in flutter/packages#13574. On Android, `FlutterPluginBinding` is a concrete class, so adding `getEngineId()` does not affect mocks.

### Changelog Description:
[flutter/195876] When running multiple `FlutterEngine`s on Android, iOS, or macOS, FFI/JNI plugins could not determine which engine a `FlutterPluginBinding` or `FlutterPluginRegistrar` belonged to.

### Workaround:
On Android, plugins can call the deprecated `FlutterPluginBinding.getFlutterEngine().getEngineId()`. On iOS and macOS, there is no public API workaround (`engineIdentifier` is only declared in internal headers, and the registrar's weak engine reference is already `nil` during `detachFromEngineForRegistrar:`).

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:
1. On Android, verify `FlutterPluginBinding.getEngineId()` returns the associated `FlutterEngine.getEngineId()`.
2. On iOS, verify `registrar.engineIdentifier` on `FlutterPluginRegistrar` and `FlutterApplicationRegistrar` matches the owning `FlutterEngine.engineIdentifier`, both during registration and during `detachFromEngineForRegistrar:`.
3. On macOS, verify `registrar.engineIdentifier` on `FlutterPluginRegistrar` matches the owning `FlutterEngine.engineIdentifier`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop CICD Run CI/CD cp: review Cherry-picks in the review queue engine flutter/engine related. See also e: labels. platform-android Android applications specifically platform-ios iOS applications specifically platform-macos Building on or for macOS specifically team-android Owned by Android platform team team-ios Owned by iOS platform team team-macos Owned by the macOS platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants