diff --git a/rfc/410.0000-flutter-android-public-api-surface.md b/rfc/410.0000-flutter-android-public-api-surface.md new file mode 100644 index 0000000..1da301a --- /dev/null +++ b/rfc/410.0000-flutter-android-public-api-surface.md @@ -0,0 +1,190 @@ +--- +type: rfc +rfc: '410.0000' +title: Flutter Android Embedder Public API Surface and Encapsulation +description: Defines the public Java/Kotlin API surface of the Flutter Android embedder and locks down internal classes, constructors, and methods as package-private. +status: draft +created: 2026-10-30T00:00:00Z +updated: 2026-11-02T00:00:00Z +tags: + - 410-android + - 440-plugins + - 030-breaking-change +authors: + - https://github.com/mboetger # Matt Boetger +--- + +# RFC 410.0000: Flutter Android Embedder Public API Surface and Encapsulation + +## Overview + +> [!IMPORTANT] +> This RFC defines the supported public Java/Kotlin API surface of the Flutter Android embedder (**62** types across **17** packages) and locks down the remaining **116** internal types, internal constructors, and `public` `@VisibleForTesting` members as `package-private`. + +Why: + +- **86% (153 of 178)** of top-level types in [`engine/src/flutter/shell/platform/android/io/flutter/`](https://github.com/flutter/flutter/tree/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter) are `public`, and [`gen_javadoc.py`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/tools/javadoc/gen_javadoc.py) publishes **20 of 23** packages—including `FlutterJNI`, `PlatformViewsController`, `AccessibilityBridge`, `TextInputPlugin`, and `systemchannels.*`—to `api.flutter.dev/javadoc/`. +- Internal classes were marked `public` only because Java's `package-private` visibility does not cross subpackages (`io.flutter.embedding.android` vs. `io.flutter.embedding.engine` vs. `io.flutter.plugin.platform`). +- Of the **173** `@VisibleForTesting` members across `io/flutter/`, **84** are already `package-private`, but **85** were widened to `public` even though unit tests in [`test/io/flutter/`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/test/README.md) live in the same Java packages. (We keep `@VisibleForTesting` on `package-private` members so Android Lint's `VisibleForTests` check still warns if same-package production code calls a test-only method.) +- Leaving internal plumbing `public` blocks the [Android Embedder C-API Migration](https://github.com/flutter/rfc/pull/24), which needs to refactor or remove `FlutterJNI`, `PlatformViewsController`, and internal channels without breaking external callers. + +### Goals + +- Define the supported public API surface for Add-to-App hosts and Android plugins. +- Lock down all **116** internal types and internal members on public classes as `package-private`. +- Migrate the 4 internal API usages in `flutter/packages` to existing public APIs. +- Enforce the boundary in CI (`public_api.txt`), [`gen_javadoc.py`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/tools/javadoc/gen_javadoc.py), and [`test/README.md`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/test/README.md). + +### Non-Goals + +- Redesigning the v2 plugin or Activity/Fragment embedding architecture. +- Changing Dart-to-engine platform channel names or binary wire formats. + +## Background + +### Current vs. Target API Surface + +| Metric | Current | Target | +| :--- | ---: | ---: | +| Top-level `public` types | 153 (86.0%) | **62 (34.8%)** (59 supported + 3 deprecated) | +| Top-level `package-private` types | 25 (14.0%) | **116 (65.2%)** | +| Types published by [`gen_javadoc.py`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/tools/javadoc/gen_javadoc.py) | 143 (20 packages, incl. internal) | **All 62 public types** (17 packages; 0 internal) | +| `@VisibleForTesting public` members | 85 (of 173 total) | **0** (all 173 `package-private`) | + +### Ecosystem Audit + +Scanning `flutter/flutter`, `flutter/packages`, `flutter/samples`, and public GitHub repositories confirms that Add-to-App hosts and plugins only require the 62 public types listed in [Proposal](#proposal). Across all of `flutter/packages`, only **4** accidental references to internal types exist: + +| Plugin & File | Internal API Used | Fix | +| :--- | :--- | :--- | +| `video_player_android` ([`VideoPlayerPlugin.java`](https://github.com/flutter/packages/blob/fc56a0d548439c9776466e25e5567921f43e382a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java#L96-L103)) & `google_maps_flutter_android` ([`Convert.java`](https://github.com/flutter/packages/blob/fc56a0d548439c9776466e25e5567921f43e382a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java#L276-L283)) | `FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(...)` | Use `FlutterPluginBinding.getFlutterAssets().getAssetFilePathByName(...)`. | +| `camera_android` & `camera_android_camerax` ([`DeviceOrientationManager.java`](https://github.com/flutter/packages/blob/fc56a0d548439c9776466e25e5567921f43e382a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java#L21)) | `PlatformChannel.DeviceOrientation` (used only for `.toString()`) | Use a plugin-local enum. | +| `camera_android` ([`NoiseReductionFeature.java`](https://github.com/flutter/packages/blob/fc56a0d548439c9776466e25e5567921f43e382a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java#L13)) | `import io.flutter.BuildConfig;` | Delete unused import. | +| `espresso` ([`FlutterViewAction.java`](https://github.com/flutter/packages/blob/fc56a0d548439c9776466e25e5567921f43e382a/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/action/FlutterViewAction.java#L31)) | `import io.flutter.embedding.engine.FlutterJNI;` | Delete unused import. | + +## Proposal + +### 1. Public API Surface (62 Types) + +A type is kept `public` if and only if it is required by one of the two external embedder workflows: + +1. **Add-to-App Host Integration** ([documented Add-to-App APIs](https://docs.flutter.dev/add-to-app), `flutter/samples/add_to_app`, and [`pure_android_host_apps`](https://github.com/flutter/flutter/tree/66b3ad90177ccbd2963836a5556076b370622fe4/dev/integration_tests/pure_android_host_apps)): Hosting Flutter in an Android `Activity`, `Fragment`, or `View`, initializing/caching `FlutterEngine`s, and executing Dart entrypoints. +2. **Android Plugin Authoring** ([v2 plugin APIs](https://docs.flutter.dev/platform-integration/platform-channels) and all plugins in `flutter/packages`): Registering plugins, binding to Android component lifecycles (`Activity`, `Service`, `BroadcastReceiver`, `ContentProvider`), communicating over platform channels, registering `PlatformView`s or external `TextureRegistry` surfaces, and looking up background isolate callbacks. + +All other **116** types are internal communication between `FlutterView`, `FlutterEngine`, and `libflutter.so` that neither hosts nor plugins implement or call: + +| Category | Public Types | +| :--- | :--- | +| **Host Containers (`io.flutter.embedding.android` — 14)** | `FlutterActivity`, `FlutterFragmentActivity`, `FlutterFragment`, `FlutterView`, `FlutterSurfaceView`, `FlutterTextureView`, `FlutterImageView`, `FlutterActivityLaunchConfigs` (`BackgroundMode`), `RenderMode`, `TransparencyMode`, `FlutterEngineProvider`, `FlutterEngineConfigurator`, `ExclusiveAppComponent`, `FlutterPlayStoreSplitApplication` | +| **Engine & Dart Execution (`io.flutter.embedding.engine.*` — 7)** | `FlutterEngine`, `FlutterEngineCache`, `FlutterEngineGroup` (`Options`), `FlutterEngineGroupCache`, `DartExecutor` (`DartEntrypoint`, `DartCallback`, `IsolateServiceIdListener`), `FlutterLoader` (`Settings`), `GeneratedPluginRegister` | +| **Plugin Lifecycle (`io.flutter.embedding.engine.plugins.*` — 14)** | `FlutterPlugin` (`FlutterPluginBinding`, `FlutterAssets`), `PluginRegistry`, `ActivityAware`, `ActivityPluginBinding` (`OnSaveInstanceStateListener`), `ActivityControlSurface`, `ServiceAware`, `ServicePluginBinding`, `ServiceControlSurface`, `BroadcastReceiverAware`, `BroadcastReceiverPluginBinding`, `BroadcastReceiverControlSurface`, `ContentProviderAware`, `ContentProviderPluginBinding`, `ContentProviderControlSurface` | +| **Channels & Codecs (`io.flutter.plugin.common` — 15)** | `BinaryMessenger` (`TaskQueue`, `TaskQueueOptions`, `BinaryMessageHandler`, `BinaryReply`), `MethodChannel` (`MethodCallHandler`, `Result`), `MethodCall`, `BasicMessageChannel` (`MessageHandler`, `Reply`), `EventChannel` (`StreamHandler`, `EventSink`), `MessageCodec`, `MethodCodec`, `StandardMessageCodec`, `StandardMethodCodec`, `BinaryCodec`, `StringCodec`, `JSONMessageCodec`, `JSONMethodCodec`, `FlutterException`, `PluginRegistry` (activity/permission listener interfaces) | +| **Platform Views, Rendering & Utilities (9)** | `PlatformView`, `PlatformViewFactory`, `PlatformViewRegistry` (`io.flutter.plugin.platform`); `FlutterRenderer` (`ViewportMetrics`, `DisplayFeature`, `DisplayFeatureType`, `DisplayFeatureState`), `FlutterUiDisplayListener`, `RenderSurface` (`io.flutter.embedding.engine.renderer`); `TextureRegistry` (`SurfaceProducer`, `SurfaceLifecycle`, `TextureEntry`, `SurfaceTextureEntry`, `ImageTextureEntry`, `OnFrameConsumedListener`, `OnTrimMemoryListener`), `FlutterCallbackInformation` (`io.flutter.view`); `Log` (`io.flutter`) | +| **Deprecated / Legacy (3)** | `FlutterApplication` (`io.flutter.app`, already `@Deprecated`), `FlutterShellArgs` (`io.flutter.embedding.engine`, already `@Deprecated`), `HiddenLifecycleReference` (`io.flutter.embedding.engine.plugins.lifecycle`, superseded by `ActivityPluginBinding.getActivityLifecycle()`) | + +
+Complete package-by-package breakdown (all 178 types across 23 packages) + +| Package | Total | Currently `public` | Target `public` | Target `package-private` (Internal Types) | +| :--- | ---: | ---: | ---: | :--- | +| `io.flutter` | 4 | 4 | 1 (`Log`) | 3 (`Build`, `BuildConfig`, `FlutterInjector`) | +| `io.flutter.app` | 1 | 1 | 1 (`FlutterApplication`) | 0 | +| `io.flutter.embedding.android` | 28 | 24 | 14 | 14 (`AndroidTouchProcessor`, `ContentSizingFlag`, `FlutterActivityAndFragmentDelegate`, `FlutterMeasureSpec`, `FlutterViewDelegate`, `IntentUtils`, `KeyChannelResponder`, `KeyData`, `KeyEmbedderResponder`, `KeyboardManager`, `KeyboardMap`, `MotionEventTracker`, `SurfaceHolderCallbackCompat`, `WindowInfoRepositoryCallbackAdapterWrapper`) | +| `io.flutter.embedding.engine` | 8 | 7 | 5 | 3 (`FlutterEngineConnectionRegistry`, `FlutterJNI`, `FlutterOverlaySurface`) | +| `io.flutter.embedding.engine.dart` | 4 | 3 | 1 (`DartExecutor`) | 3 (`DartMessenger`, `PlatformMessageHandler`, `PlatformTaskQueue`) | +| `io.flutter.embedding.engine.deferredcomponents` | 2 | 2 | 0 | 2 (`DeferredComponentManager`, `PlayStoreDeferredComponentManager`) | +| `io.flutter.embedding.engine.flags` | 2 | 2 | 0 | 2 (`FlutterEngineFlags`, `FlutterEngineFlagsProvider`) | +| `io.flutter.embedding.engine.image` | 9 | 2 | 0 | 9 (`BitmapMetadataReader`, `ExifMetadataReader`, `FlutterImageDecoder`, `ImageDecoderDefaultImpl`, `ImageDecoderHeifApi36Impl`, `ImageDecoderHeifPre36Impl`, `ImageUtils`, `MediaMetadataReader`, `Metadata`) | +| `io.flutter.embedding.engine.loader` | 4 | 3 | 1 (`FlutterLoader`) | 3 (`ApplicationInfoLoader`, `FlutterApplicationInfo`, `ResourceExtractor`) | +| `io.flutter.embedding.engine.mutatorsstack` | 2 | 2 | 0 | 2 (`FlutterMutatorView`, `FlutterMutatorsStack`) | +| `io.flutter.embedding.engine.plugins` | 2 | 2 | 2 | 0 | +| `io.flutter.embedding.engine.plugins.activity` | 3 | 3 | 3 | 0 | +| `io.flutter.embedding.engine.plugins.broadcastreceiver` | 3 | 3 | 3 | 0 | +| `io.flutter.embedding.engine.plugins.contentprovider` | 3 | 3 | 3 | 0 | +| `io.flutter.embedding.engine.plugins.lifecycle` | 1 | 1 | 1 (`HiddenLifecycleReference`) | 0 | +| `io.flutter.embedding.engine.plugins.service` | 3 | 3 | 3 | 0 | +| `io.flutter.embedding.engine.plugins.util` | 1 | 1 | 1 (`GeneratedPluginRegister`) | 0 | +| `io.flutter.embedding.engine.renderer` | 6 | 5 | 3 | 3 (`FlutterUiResizeListener`, `SurfaceTextureSurfaceProducer`, `SurfaceTextureWrapper`) | +| `io.flutter.embedding.engine.systemchannels` | 22 | 22 | 0 | 22 (All 22 channel classes) | +| `io.flutter.plugin.common` | 17 | 17 | 15 | 2 (`ErrorLogResult`, `JSONUtil`) | +| `io.flutter.plugin.editing` | 8 | 5 | 0 | 8 (`FlutterTextUtils`, `ImeSyncDeferringInsetsCallback`, `InputConnectionAdaptor`, `ListenableEditingState`, `ScribePlugin`, `SpellCheckPlugin`, `TextEditingDelta`, `TextInputPlugin`) | +| `io.flutter.plugin.localization` | 1 | 1 | 0 | 1 (`LocalizationPlugin`) | +| `io.flutter.plugin.mouse` | 1 | 1 | 0 | 1 (`MouseCursorPlugin`) | +| `io.flutter.plugin.platform` | 22 | 16 | 3 (`PlatformView`, `PlatformViewFactory`, `PlatformViewRegistry`) | 19 (`AccessibilityEventsDelegate`, `ImageReaderPlatformViewRenderTarget`, `PlatformOverlayView`, `PlatformPlugin`, `PlatformViewGestureTracker`, `PlatformViewRegistryImpl`, `PlatformViewRenderTarget`, `PlatformViewWrapper`, `PlatformViewsAccessibilityDelegate`, `PlatformViewsController`, `PlatformViewsController2`, `PlatformViewsControllerDelegator`, `SingleViewFakeWindowViewGroup`, `SingleViewPresentation`, `SingleViewWindowManager`, `SurfaceProducerPlatformViewRenderTarget`, `SurfaceTexturePlatformViewRenderTarget`, `VirtualDisplayController`, `WindowManagerHandler`) | +| `io.flutter.plugin.text` | 1 | 1 | 0 | 1 (`ProcessTextPlugin`) | +| `io.flutter.plugin.view` | 1 | 1 | 0 | 1 (`SensitiveContentPlugin`) | +| `io.flutter.util` | 5 | 5 | 0 | 5 (`HandlerCompat`, `PathUtils`, `Preconditions`, `TraceSection`, `ViewUtils`) | +| `io.flutter.view` | 14 | 13 | 2 (`TextureRegistry`, `FlutterCallbackInformation`) | 12 (`AccessibilityBridge`, `VsyncWaiter`, and 10 accessibility role configurators) | + +
+ +### 2. Decoupling Public Classes from Internal Types + +To allow internal types to become `package-private` without `javac` signature errors on public classes: + +| Public Class | Change | +| :--- | :--- | +| [`FlutterView`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java#L106-L108) | Replace `implements MouseCursorPlugin.MouseCursorViewDelegate, KeyboardManager.ViewDelegate` with private inner delegates. Make hybrid-composition helpers (`convertToImageView`, `revertImageView`, `attachOverlaySurfaceToRender`, `acquireLatestImageViewFrame`, `createImageView`) and `public` `@VisibleForTesting` members `package-private`. | +| [`FlutterEngine`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L108) | Make the 6 constructors accepting `FlutterJNI`/`PlatformViewsController*`, the 16 internal `get*Channel()` getters, and `getPlatformViewsController*()` `package-private`. Add `getPlatformViewRegistry()` and direct host methods (`notifyAppIsResumed()`, `notifyAppIsInactive()`, `notifyAppIsPaused()`, `notifyAppIsDetached()`, `notifyMemoryPressureWarning()`, `setInitialRoute(String)`, `pushRoute(String)`, `popRoute()`, `getRestorationData()`, `setRestorationData(byte[])`) so custom `FlutterView` hosts do not need `systemchannels.*`. | +| [`FlutterRenderer`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java#L65) & [`DartExecutor`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java#L48) | Make `FlutterRenderer(FlutterJNI)`, `DartExecutor(FlutterJNI, AssetManager)`, `onAttachedToJNI()`, `onDetachedFromJNI()`, `setSemanticsEnabled()`, `dispatchSemanticsAction()`, and `public` `@VisibleForTesting` methods `package-private`. | +| [`TextureRegistry`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java#L27) | Move internal engine consumption interfaces (`GLTextureConsumer`, `ImageConsumer`) out of `TextureRegistry` into `package-private` renderer interfaces. | +| [`ActivityPluginBinding`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityPluginBinding.java#L18) | Add `default androidx.lifecycle.Lifecycle getActivityLifecycle()` directly, deprecate `HiddenLifecycleReference`, and make its constructor `package-private`. | + +### 3. Phased Rollout + +```mermaid +flowchart LR + P1["Phase 1: Immediate Lockdown
• Fix 4 flutter/packages leaks
• 28 zero-caller classes → package-private
• 85 public @VisibleForTesting → package-private
• gen_javadoc.py -public & public_api.txt CI check"] --> P2["Phase 2: Deprecate Cross-Package Internals
• Add FlutterEngine & ActivityPluginBinding methods
• Annotate 63 cross-package classes & internal
members @Deprecated + @RestrictTo(LIBRARY_GROUP)"] + P2 --> P3["Phase 3: Bytecode Encapsulation
• Co-locate 63 internal classes into owning packages
• Drop 'public' modifier (116 package-private types)"] +``` + +#### Phase 1: Immediate Non-Breaking Lockdown & CI Guard + +1. Fix the 4 internal API usages in `flutter/packages`. +2. Drop `public` in-place from the **28** internal top-level types that already have zero cross-package callers, and from the **85** `public` `@VisibleForTesting` members (keeping the `@VisibleForTesting` annotation on the `package-private` declarations, and documenting in [`test/README.md`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/shell/platform/android/test/README.md) that `@VisibleForTesting` members must never be `public`). +3. Replace the implemented internal interfaces on `FlutterView` with private inner delegates. +4. Update [`gen_javadoc.py`](https://github.com/flutter/flutter/blob/66b3ad90177ccbd2963836a5556076b370622fe4/engine/src/flutter/tools/javadoc/gen_javadoc.py) to pass `-public` and list the 17 public packages (removing internal-only `flags`, `mutatorsstack`, `systemchannels`, `editing`, `util` and adding `io.flutter` and `io.flutter.embedding.engine.plugins.util`) so all 62 public types are documented and 0 internal types are published. +5. Add a bytecode signature golden test (`engine/src/flutter/shell/platform/android/api/public_api.txt`) that fails CI if any unapproved `public` or `protected` symbol is added to `flutter.jar`. + +
+28 zero-caller classes made package-private in Phase 1 + +- `io.flutter.embedding.android`: `FlutterViewDelegate`, `KeyChannelResponder`, `KeyData`, `KeyEmbedderResponder`, `KeyboardMap`, `SurfaceHolderCallbackCompat`, `WindowInfoRepositoryCallbackAdapterWrapper` +- `io.flutter.embedding.engine.dart`: `PlatformTaskQueue` +- `io.flutter.embedding.engine.flags`: `FlutterEngineFlagsProvider` +- `io.flutter.embedding.engine.image`: `BitmapMetadataReader` +- `io.flutter.plugin.common`: `ErrorLogResult`, `JSONUtil` +- `io.flutter.plugin.platform`: `ImageReaderPlatformViewRenderTarget`, `PlatformOverlayView`, `PlatformViewRegistryImpl`, `PlatformViewRenderTarget`, `PlatformViewWrapper`, `SurfaceProducerPlatformViewRenderTarget`, `SurfaceTexturePlatformViewRenderTarget` +- `io.flutter.view`: `AccessibilityNodeConfigurator`, `AccessibilityStringBuilder`, `BaseRoleConfigurator`, `ClassNameRoleConfigurator`, `ComboBoxRoleConfigurator`, `MenuRoleConfigurator`, `ProgressBarRoleConfigurator`, `RoleConfiguratorFactory`, `SliderRoleConfigurator` + +
+ +#### Phase 2: Deprecate Cross-Package Internals (1 Stable Release) + +1. Land the replacement host methods on `FlutterEngine` and `getActivityLifecycle()` on `ActivityPluginBinding`. +2. Annotate the remaining **63** cross-package internal classes and internal members on `FlutterEngine`, `FlutterView`, `FlutterRenderer`, and `DartExecutor` with `@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)` and `@Deprecated(since = "Flutter X.Y", forRemoval = true)`. +3. Publish a breaking-change migration guide on `docs.flutter.dev`. + +#### Phase 3: Package Co-location & `package-private` Lockdown + +After one stable release, co-locate the **63** cross-package internal classes into the package that owns them and drop the `public` modifier: + +
+Package co-location map for the 63 Phase 3 classes + +| Target Package | Co-located `package-private` Classes | +| :--- | :--- | +| `io.flutter.embedding.android` | `TextInputPlugin`, `InputConnectionAdaptor`, `ListenableEditingState`, `FlutterTextUtils`, `ImeSyncDeferringInsetsCallback`, `ScribePlugin`, `SpellCheckPlugin`, `TextEditingDelta`, `MouseCursorPlugin`, `SensitiveContentPlugin`, `PlatformPlugin`, `AndroidTouchProcessor`, `KeyboardManager`, `MotionEventTracker` | +| `io.flutter.embedding.engine` | `FlutterJNI`, `FlutterOverlaySurface`, `FlutterEngineFlags`, `FlutterImageDecoder` (+ 8 `image.*` helpers), `ApplicationInfoLoader`, `FlutterApplicationInfo`, `DeferredComponentManager`, `PlayStoreDeferredComponentManager`, `LocalizationPlugin`, `ProcessTextPlugin`, `VsyncWaiter`, all 22 `systemchannels.*` classes, `PlatformViewsController`, `PlatformViewsController2`, `PlatformViewsControllerDelegator`, `PlatformViewGestureTracker`, `PlatformViewsAccessibilityDelegate`, `FlutterMutatorView`, `FlutterMutatorsStack`, `AccessibilityBridge`, `AccessibilityViewEmbedder`, and internal utilities (`Build`, `BuildConfig`, `FlutterInjector`, `HandlerCompat`, `PathUtils`, `Preconditions`, `TraceSection`, `ViewUtils`) | + +> [!NOTE] +> JNI `env->FindClass` and `env->RegisterNatives` ignore Java visibility modifiers, and `FlutterJNI` already resides in `io.flutter.embedding.engine`. Co-locating its collaborators into `io.flutter.embedding.engine` allows `FlutterJNI` to become `package-private` without changing any C++ JNI descriptors. + +
+ +## Alternatives Considered + +- **`@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)` without `package-private`:** `@RestrictTo` is only an Android Lint check; bytecode remains `ACC_PUBLIC`. Callers can bypass it with `@SuppressLint("RestrictedApi")` or when Lint is disabled, which still prevents removing or renaming internal classes like `FlutterJNI` during the C-API migration. +- **Java Platform Module System (`module-info.java`):** D8/R8 DEX compilation and the Android Runtime (ART) verifier ignore JPMS module boundaries, and Android Gradle builds put `flutter.jar` on the classpath rather than the module path. +- **Kotlin `internal` visibility:** Kotlin `internal` declarations compile to `public` in JVM/DEX bytecode, remaining accessible to external Java callers unless they are `package-private`. +- **Build-time bytecode repackaging (JarJar / R8):** Rewriting access flags in bytecode without matching source packages causes `IllegalAccessError` across subpackages at runtime and diverges stack traces from source files.