Skip to content

Fix Content Sizing Race Condition#182326

Merged
auto-submit[bot] merged 15 commits into
flutter:masterfrom
mboetger:fix-race-condition
Mar 18, 2026
Merged

Fix Content Sizing Race Condition#182326
auto-submit[bot] merged 15 commits into
flutter:masterfrom
mboetger:fix-race-condition

Conversation

@mboetger

@mboetger mboetger commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

Only call MaybeResizeSurfaceView if the expected frame_size is not the same size as what is returned from the Dart layout. Also, remove the gating function that prevents viewport metrics from being sent on a resize.

The AtomicBoolean that is gating calls back into the engine has to be removed. This is necessary because the buffer for the SurfaceView is only updated once the surface finishes drawing to the screen. Since the frame can be submitted before the buffer is updated (but after the resize call happens), the UI rejects the frame because the buffer dimensions do not match. Therefore, the callback with the updated viewport metrics is necessary for the frame to be re-tried.

I believe this AtomicBoolean is ultimately the cause of the initial flake. Since rotations are actually changing the size, the AtomicBoolean would have been set to not send the viewport metrics back when necessary.

The call from the engine should remain non-blocking to prevent deadlocks.

Fixes: #182242

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@github-actions github-actions Bot added platform-android Android applications specifically engine flutter/engine related. See also e: labels. team-android Owned by Android platform team labels Feb 12, 2026
@mboetger

Copy link
Copy Markdown
Contributor Author

Local testing shows this also fixes the flake (if the listener is always added regardless of if content sizing is enabled - though this PR does NOT remove that condition check).

@rwrz

rwrz commented Mar 6, 2026

Copy link
Copy Markdown

Thanks for this fix! I can confirm it resolves two issues I was hitting in my project (a video_player fork with PiP and background playback support).

  1. Picture-in-Picture viewport metrics never updating

When an Activity enters PiP mode, MediaQuery.sizeOf(context) continued to report the full-screen dimensions (e.g. Size(392.7, 825.5)) instead of the PiP window dimensions (e.g. Size(216.4, 121.8)). This wasn't a
timing/race issue — even after calling setState or triggering a hot reload, the stale size persisted. LayoutBuilder constraints were also wrong.

Impact: Any widget using MediaQuery or parent constraints to adapt its layout in PiP mode would render at the wrong size. For example, a video centered in a Scaffold would position itself in the middle of an 825dp-tall
layout — completely off-screen in the tiny PiP window (~121dp visible). The result was a black screen.

Workaround I had to implement: Passing the PiP window dimensions directly from Activity.onPictureInPictureModeChanged(boolean, Configuration) via newConfig.screenWidthDp/screenHeightDp through a platform channel event,
bypassing MediaQuery entirely.

  1. Device orientation changes (portrait ↔ landscape)

When rotating the device (e.g. going landscape for a fullscreen video experience), the viewport metrics were also stale — the layout would briefly render with the old orientation's dimensions before snapping to the
correct size. This PR fixes both cases since they share the same root cause: the viewport metric callbacks being blocked.

Testing

  • Flutter: checked out this PR branch via gh pr checkout 182326 + flutter precache
  • Device: Android 14
  • Activity: FlutterActivity subclass with android:supportsPictureInPicture="true"
  • Both PiP and orientation changes now report correct MediaQuery sizes immediately.

To test, I did:

cd $(dirname $(which flutter))/..                                                                                                                                                        
git fetch origin pull/182326/head:pr-182326                                                                                                                                                                                  
git checkout pr-182326
flutter precache

@mboetger

Copy link
Copy Markdown
Contributor Author

@ rwrz - Interesting. Thanks for confirming the fix. Just to be clear, you are using the io.flutter.embedding.android.EnableContentSizing flag to enable the functionality and seeing this behavior?

@rwrz

rwrz commented Mar 11, 2026

Copy link
Copy Markdown

@ rwrz - Interesting. Thanks for confirming the fix. Just to be clear, you are using the io.flutter.embedding.android.EnableContentSizing flag to enable the functionality and seeing this behavior?

Sorry. Not really. Probably I misinterpreted the fix and got the results I needed by luck or another commit. =/
I was facing the problem using stable (3.41.4) and it was fixed by using your branch.

@mboetger

mboetger commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

Sorry. Not really. Probably I misinterpreted the fix and got the results I needed by luck or another commit. =/ I was facing the problem using stable (3.41.4) and it was fixed by using your branch.

@rwrz - even more interesting. How did you decide to use my branch? Were you seeing this line in the logs: "Resize was in response to the engine resizing the view. Not sending viewport metrics."?

@mboetger

Copy link
Copy Markdown
Contributor Author

@rwrz I guess it's possible another PR fixed the issue somewhere in between stable and HEAD for my PR. I would be extra curious if you cherrypicked just my fix into the stable branch and saw the fix.

@mboetger mboetger marked this pull request as ready for review March 11, 2026 23:16
@mboetger mboetger requested a review from a team as a code owner March 11, 2026 23:16

@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 addresses a race condition in content sizing by making the MaybeResizeSurfaceView call blocking. This is achieved using fml::AutoResetWaitableEvent in C++ to synchronize with the platform thread. On the Java side, a corresponding change posts the layout update to the message loop to prevent deadlocks. The change also removes the shouldSendViewportMetrics AtomicBoolean which is no longer necessary, simplifying the logic. The changes look correct and effectively address the race condition. I've added a couple of suggestions to improve the robustness of the lambda captures in the C++ code.

mboetger and others added 2 commits March 11, 2026 16:20
…er/external_view_embedder_2.cc

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…er/external_view_embedder.cc

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@mboetger mboetger marked this pull request as draft March 11, 2026 23:22
@mboetger mboetger added the CICD Run CI/CD label Mar 13, 2026
@mboetger

Copy link
Copy Markdown
Contributor Author

I am working on integrations tests specifically for content sizing. Rotations were definitely affected and need to be tested thoroughly. #179673 proved that - and that fact that I was able to reproduce the flake without platform views being active (just the rotation) highlighted that. I will add an integration test that enables content sizing based on that test - without platform views - just with rotations in a follow up PR.

@rwrz - I believe you were running into problems created by content sizing, but I believe this PR would have fixed your issue - and maybe that was the other commit that fixed your issue. If you are able to provide a small code sample that shows the issue you were having parent constraints I would gladly create an integration test for that as well.

Ultimately, it's clear the AtomicBoolean was the problem - but I also believe calling MaybeResizeSurfaceView when unnecessary was contributing to the issue outside of the content sizing use case.

engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 20, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 20, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 21, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 21, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 21, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 22, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 22, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 25, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 25, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 25, 2026
mboetger added a commit to mboetger/flutter that referenced this pull request Mar 26, 2026
Only call MaybeResizeSurfaceView if the expected frame_size is not the
same size as what is returned from the Dart layout. Also, remove the
gating function that prevents viewport metrics from being sent on a
resize.

The AtomicBoolean that is gating calls back into the engine has to be
removed. This is necessary because the buffer for the SurfaceView is
only updated once the surface finishes drawing to the screen. Since the
frame can be submitted before the buffer is updated (but after the
resize call happens), the UI rejects the frame because the buffer
dimensions do not match. Therefore, the callback with the updated
viewport metrics is necessary for the frame to be re-tried.

I believe this AtomicBoolean is ultimately the cause of the initial
flake. Since rotations are actually changing the size, the AtomicBoolean
would have been set to not send the viewport metrics back when
necessary.

The call from the engine should remain non-blocking to prevent
deadlocks.

Fixes: flutter#182242

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [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 documentation (doc comments with `///`).
- [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.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
ahmedsameha1 pushed a commit to ahmedsameha1/flutter that referenced this pull request Apr 14, 2026
Only call MaybeResizeSurfaceView if the expected frame_size is not the
same size as what is returned from the Dart layout. Also, remove the
gating function that prevents viewport metrics from being sent on a
resize.

The AtomicBoolean that is gating calls back into the engine has to be
removed. This is necessary because the buffer for the SurfaceView is
only updated once the surface finishes drawing to the screen. Since the
frame can be submitted before the buffer is updated (but after the
resize call happens), the UI rejects the frame because the buffer
dimensions do not match. Therefore, the callback with the updated
viewport metrics is necessary for the frame to be re-tried.

I believe this AtomicBoolean is ultimately the cause of the initial
flake. Since rotations are actually changing the size, the AtomicBoolean
would have been set to not send the viewport metrics back when
necessary.

The call from the engine should remain non-blocking to prevent
deadlocks.

Fixes: flutter#182242

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [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 documentation (doc comments with `///`).
- [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.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-android Android applications specifically team-android Owned by Android platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate Content Sizing Race Condition

3 participants