Repository navigation
[Android] Replay predictive back state for retained engines - #192286
auto-submit[bot] merged 6 commits into
Conversation
Retain the latest SystemNavigator.setFrameworkHandlesBack value on the engine-scoped PlatformChannel and replay it when a new platform handler attaches. Initialize the PlatformPlugin delegate before registering the handler so synchronous replay reaches the new Activity. Add regression coverage for handler replacement, messages received without a host, and constructor-time replay.
There was a problem hiding this comment.
Code Review
This pull request updates PlatformChannel to retain and replay the frameworkHandlesBack state when PlatformMessageHandler is attached or detached, and updates PlatformPlugin to set the message handler after initializing the delegate. Corresponding unit tests were added to verify this behavior. The review feedback suggests safely checking if arguments is an instance of Boolean before casting to prevent potential NullPointerException or ClassCastException when handling SystemNavigator.setFrameworkHandlesBack.
| frameworkHandlesBack = (boolean) arguments; | ||
| if (platformMessageHandler != null) { | ||
| platformMessageHandler.setFrameworkHandlesBack(frameworkHandlesBack); | ||
| result.success(null); | ||
| break; | ||
| } |
There was a problem hiding this comment.
Directly casting arguments to boolean can throw a NullPointerException if arguments is null (due to unboxing), or a ClassCastException if it is of an unexpected type. To ensure robust defensive programming, we should safely check if arguments is an instance of Boolean before casting.
| frameworkHandlesBack = (boolean) arguments; | |
| if (platformMessageHandler != null) { | |
| platformMessageHandler.setFrameworkHandlesBack(frameworkHandlesBack); | |
| result.success(null); | |
| break; | |
| } | |
| if (arguments instanceof Boolean) { | |
| frameworkHandlesBack = (Boolean) arguments; | |
| } else { | |
| frameworkHandlesBack = false; | |
| } | |
| if (platformMessageHandler != null) { | |
| platformMessageHandler.setFrameworkHandlesBack(frameworkHandlesBack); | |
| } |
|
Additional physical-device validation on Flutter 3.47.2 stable (
Full verification details: #192284 (comment) |
gmackall
left a comment
There was a problem hiding this comment.
Thanks for the fix and the device validation. This works for FlutterActivity, but I think FlutterFragment (and so FlutterFragmentActivity, which always sets shouldAutomaticallyHandleOnBackPressed(true)) is still broken.
FlutterFragment.onAttach calls delegate.onAttach(context) first. With this PR that constructs PlatformPlugin, which replays setFrameworkHandlesBack(true) and calls onBackPressedCallback.setEnabled(true). Then onAttach calls onBackPressedCallback.setEnabled(false), which overwrites the replayed value. So a FlutterFragmentActivity.withCachedEngine(...) replacement host still sends Back to the system unless there's a savedInstanceState bundle.
Could you set the default before attaching, so the replay wins? Replacing this block with something like:
boolean autoBack = getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false);
if (autoBack) {
// When Android handles a back gesture, it pops an Activity or goes back
// ... (keep the existing comment)
onBackPressedCallback.setEnabled(false);
}
delegate.onAttach(context);
if (autoBack) {
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
}Sync with 928815a to remove stale builder definitions. The six PR Java files remain unchanged. Validated the upstream CI YAML test cases in an isolated Dart harness (1,538 passing), Java formatting, and diff whitespace.
Roll Flutter from fcfb4f203eae to 934720261be0 (38 revisions) flutter/flutter@fcfb4f2...9347202 2026-11-03 [email protected] Roll Skia from 5bceaa033999 to c733ad3e37cb (5 revisions) (flutter/flutter#195872) 2026-11-03 [email protected] Roll Skia from 34d2bf479385 to 5bceaa033999 (9 revisions) (flutter/flutter#195864) 2026-11-03 [email protected] fix: prefer pointer events over semantics tap for mouse clicks on web (flutter/flutter#191529) 2026-11-03 [email protected] [VPAT] Expose a semantics text field value while it is not being edited (flutter/flutter#195445) 2026-11-03 [email protected] [Impeller] Make a GL context current before constructing an AiksContext without a surface (flutter/flutter#195629) 2026-11-03 [email protected] [flutter_tools] Eliminate globals.dart from project.dart (flutter/flutter#194066) 2026-11-03 [email protected] [tool] Complete context runner elimination and migrate AppContext usages to ToolContext (flutter/flutter#190797) 2026-11-03 [email protected] [web] Unify font collections between CanvasKit and Skwasm (flutter/flutter#195539) 2026-11-03 [email protected] [Android] Replay predictive back state for retained engines (flutter/flutter#192286) 2026-11-03 [email protected] [vulkan]: Adds depth/stencil synchronization for subpasses (flutter/flutter#195689) 2026-11-03 [email protected] [web] Honor requested upscaling in instantiateImageCodecWithSize and ImageDescriptor.instantiateCodec (flutter/flutter#195836) 2026-11-03 [email protected] Roll Skia from 866b61ca92c9 to 34d2bf479385 (9 revisions) (flutter/flutter#195848) 2026-11-03 [email protected] Roll Fuchsia Test Scripts from xl1LWBuppzu-BnOzV... to B6zarykZp6PKWRueg... (flutter/flutter#195847) 2026-11-03 [email protected] Remove unused ffi (flutter/flutter#195710) 2026-11-02 [email protected] Revert: [Android] Let the raster thread use every core of a chip without little cores (flutter/flutter#195838) 2026-11-02 [email protected] Run 10 impeller iOS benchmarks as one devicelab task (flutter/flutter#195661) 2026-11-02 [email protected] Use named uniforms instead of setFloat in StretchEffect (flutter/flutter#195372) 2026-11-02 [email protected] [flutter_tools] Report when the browser disconnects during a web test run (flutter/flutter#192198) 2026-11-02 [email protected] fix: Resolve memory leak in image_list example (flutter/flutter#189526) 2026-11-02 [email protected] Only run the common local engine configurations in presubmit (flutter/flutter#195688) 2026-11-02 [email protected] Update gcloud_version to version [email protected] (flutter/flutter#195769) 2026-11-02 [email protected] [Impeller] Order the Vulkan copy-texture-to-texture layout transition after the copy (flutter/flutter#195814) 2026-11-02 [email protected] [Impeller] Order the Vulkan ResizeTexture layout transition after its blit (flutter/flutter#195721) 2026-11-02 [email protected] [web] Fix CanvasKit fragment shader uniforms being freed or mutated after recording (flutter/flutter#195718) 2026-11-02 [email protected] Roll Fuchsia Linux SDK from MqypI6RNyVZ_FLUE-... to T_-6-6X1lDMghhAvX... (flutter/flutter#195809) 2026-11-02 [email protected] [Android] Log why Impeller falls back to OpenGLES (flutter/flutter#194764) 2026-11-02 [email protected] Roll Skia from 3f35fb6e591b to 866b61ca92c9 (11 revisions) (flutter/flutter#195802) 2026-11-02 [email protected] Roll Skia from 3f35fb6e591b to 866b61ca92c9 (11 revisions) (flutter/flutter#195802) 2026-11-02 [email protected] Only run the common local engine configurations in presubmit (flutter/flutter#195688) 2026-11-02 [email protected] Balance tool_integration_tests subshards by expected duration (flutter/flutter#195628) 2026-11-02 [email protected] [web_paragraph] Map code units without a cluster to a cluster (flutter/flutter#195384) 2026-11-02 [email protected] [web] Hide synthetic autofill inputs from assistive technology (flutter/flutter#195618) 2026-11-02 [email protected] Roll Packages from 3742a4e to 3f34a6f (8 revisions) (flutter/flutter#195795) 2026-11-02 [email protected] [web] Unskip web tests that pass on CanvasKit and Skwasm: platform_view_test, route_notification_messages_test (flutter/flutter#195605) 2026-11-02 [email protected] Roll Skia from e5ad3868b8db to 3f35fb6e591b (1 revision) (flutter/flutter#195789) 2026-11-02 [email protected] Roll Skia from f5215e74aa16 to e5ad3868b8db (3 revisions) (flutter/flutter#195779) 2026-11-02 [email protected] Roll Skia from e43478a2f9f8 to f5215e74aa16 (1 revision) (flutter/flutter#195776) 2026-11-01 [email protected] Roll Fuchsia Linux SDK from kW0A0xxNkqf38ljm4... to MqypI6RNyVZ_FLUE-... (flutter/flutter#195768) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose ...
PlatformChannelcurrently forwardsSystemNavigator.setFrameworkHandlesBackonly to the Android host that is attached at that moment. When an app retains aFlutterEngineacross Activity instances, the old Activity unregisters its back callback during teardown, but the retained route does not resend its unchanged back state to the replacement Activity. System and predictive Back can then bypass Flutter.This change keeps the most recent framework back state on the engine-scoped
PlatformChanneland replays it whenever a newPlatformMessageHandlerattaches. It also records the state if the framework message arrives while no host is attached.Because replay is synchronous during
PlatformPluginconstruction, the plugin delegate is now assigned before registering the message handler. This ensures the replay reaches the new Activity.Fixes #192284.
Testing:
PlatformChannelTestcoverage for replacing a handler and receiving the message with no handler attached.PlatformPluginTestcoverage for constructor-time replay to the delegate.PlatformChannelTestandPlatformPluginTest; all passed.google-java-format 1.7andgit diff --check.provideFlutterEngine(), the first Activity was removed, and a new Activity attached to the same PID, engine, isolate, andPopScope(canPop: false)route.frameworkHandlesBackstate; system Back destroyed it without invokingPopScope.frameworkHandlesBack=true; system Back and a completed predictive-back gesture both invokedPopScopewithout destroying the Activity. A cancelled predictive-back gesture left the route unchanged.Fragment follow-up (2026-10-08)
Initialize the automatic Back callback before attaching the delegate, so the cached engine's synchronous state replay is preserved. Register the callback after attachment. A regression test covers attach-time replay on Android API 33 and 34, including normal and predictive Back dispatch.
Validation for this follow-up:
This follow-up was assisted by Codex. Contributor review is required before requesting re-review under the project's AI contribution guidelines.
CI refresh (2026-10-30)
Merged upstream master
928815a06ein5317a0754dto refresh the stale CI builder configuration that caused theMac_ios new_gallery_skia_ios__transition_perfvalidation failure. Both CI YAML files now match that upstream revision exactly. The six Java files changed by this PR are byte-for-byte unchanged from the preceding PR head.Validation for this refresh:
dev/bots/test/ci_yaml_validation_test.dartpassed in an isolated Dart harness using the current framework and engine CI files. The only test-source adjustment was replacing the shared test-helper import withpackage:test/test.dart; this is not a full Cocoon or engine presubmit run.google-java-format 1.7checked all six PR Java files;git diff --checkpassed.This refresh was assisted by OpenAI Codex. No re-review request is included in this update.
Pre-launch Checklist
///).The contributor review and CLA requirements are complete. The relevant local tests are passing; the final test checkbox is left open pending presubmit CI.