Skip to content

[net11.0][Android] Shell: Fix fragment teardown crash (port of #37048) - #37121

Closed
davidnguyen-tech wants to merge 1 commit into
dotnet:net11.0from
davidnguyen-tech:port/37048-shell-fragment-teardown-net11
Closed

[net11.0][Android] Shell: Fix fragment teardown crash (port of #37048)#37121
davidnguyen-tech wants to merge 1 commit into
dotnet:net11.0from
davidnguyen-tech:port/37048-shell-fragment-teardown-net11

Conversation

@davidnguyen-tech

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description

Ports #37048 ("[Android] Shell: Fix fragment teardown crash") from release/11.0.1xx-preview7 to net11.0.

#37048 fixed the crash on the preview7 branch only. net11.0 never received it, so the Android Controls device tests have been crashing there continuously since 2026-07-28 and are still crashing today.

Root cause (unchanged from #37048): ShellItemHandlerAdapter.Dispose() and ShellSectionHandlerAdapter.Dispose() disposed the wrapper fragment's managed peer while AndroidX FragmentManager still owned the Java fragment. FragmentManager later invoked OnDestroyView on a fragment whose managed peer was already gone, so the native-to-managed callback failed.

The failure is deterministic — Helix retried the work item 3× on a fresh emulator each time and dead-lettered it — and it takes down the whole test process, so no results file is written at all:

No results file found in any of the following formats: xunit, junit, trx
Packing 0 test reports to '.../__test_report.json'

Because there is no TRX, AzDO reports failedTests: 0 and the crash is invisible in test-result counts; it is only visible via the Helix work-item exit code. In build 1539159, 653 passing tests were observed in logcat and then lost.

Both runtimes are affected, with different symptoms:

Mono

android.runtime.JavaProxyThrowable: [System.NotSupportedException]:
  Unable to activate instance of type Microsoft.Maui.Controls.Handlers.ShellItemWrapperFragment
  from native handle 0x7ffd1e5ccd28 (key_handle 0x7381ddd).
	at Java.Interop.TypeManager.CreateInstance
	at Android.Runtime.AndroidValueManager.CreatePeer
	at AndroidX.Fragment.App.Fragment.n_OnDestroyView
	at crc649ff77a65592e7d55.ShellItemWrapperFragment.onDestroyView(ShellItemWrapperFragment.java:42)
	at androidx.fragment.app.FragmentManager.execPendingActions

CoreCLR — the same failed peer resolution instead recurses until the stack is exhausted (n_OnDestroyView ×5745 / OnDestroyView() ×5744, every frame the base AndroidX.Fragment.App.Fragment type):

Fatal error.
Got a SIGSEGV while executing native code.
   at Microsoft.Android.Runtime.JavaMarshalRegisteredPeers.PeekPeer(Java.Interop.JniObjectReference)
   at AndroidX.Fragment.App.Fragment.n_OnDestroyView(IntPtr, IntPtr)
   at AndroidX.Fragment.App.Fragment.OnDestroyView()
   ... repeats ...

Changes

Straight cherry-pick of 77f337115f0448568353e856387c937ebb50364b, applied cleanly with no conflicts. The resulting file contents are byte-identical to release/11.0.1xx-preview7 in both changed regions.

  • ShellItemHandlerAdapter.Dispose() no longer disposes its FragmentManager-owned wrapper fragment.
  • ShellSectionHandlerAdapter.Dispose() applies the same ownership correction.

Destroyed?.Invoke, DisconnectHandler() and clearing _wrapperFragment are all preserved, so handler cleanup and event semantics are unchanged.

Validation

Evidence that this change is what separates passing from crashing runs. Helix work-item exit code for com.microsoft.maui.controls.devicetests-Signed (-1/80 = process crash, no results; 1 = ordinary test failures with results written):

Build Branch / PR base Has #37048 Controls exit
1529878 · 07-28 16:22Z net11.0 1 — last good
1529973 · 07-28 17:41Z net11.0 -1 first bad
1539159 · 08-04 13:32Z net11.0 -1
1540353 · 08-05 03:09Z darc-net11.0 -1
1540605 · 08-05 09:31Z PR → net11.0 -1 most recent
1539938 · 08-04 21:44Z preview7 0
1539989 · 08-05 04:06Z preview7 0

In every failing run the last test to pass is SearchHandlerRendersCorrectly, with the crash following ~1s later during Shell fragment teardown. main is unaffected — the wrapper fragment classes do not exist there.

This PR's own maui-pr-devicetests run is the real check: the Android CoreCLR and Mono Controls legs should go green and, importantly, should report test results rather than dying with no TRX.

Follow-up (not addressed here, deliberately)

All three new Shell wrapper fragments still lack the (IntPtr, JniHandleOwnership) JNI activation constructor — on net11.0 and on preview7:

  • ShellItemWrapperFragment, ShellSectionWrapperFragment, ShellContentNavigationFragment

The pre-existing analogues have it (NavigationViewFragment.cs:29, MauiNavHostFragment.cs:18). This PR removes the trigger, but any other way of losing the peer (GC, process-death restore, another disposal path) would land back in the same catastrophic CoreCLR recursion instead of a readable error. Kept out of this PR to keep it a clean, low-risk port.

Related: #34758 (introduced the premature dispose), #36133 (added ShellItemWrapperFragment.OnDestroyView()), #36108, #36264.

Port of dotnet#37048 from release/11.0.1xx-preview7 to net11.0.

ShellItemHandlerAdapter.Dispose() and ShellSectionHandlerAdapter.Dispose()
disposed the wrapper fragment's managed peer while AndroidX FragmentManager
still owned the Java fragment. FragmentManager later invoked OnDestroyView on
a fragment whose managed peer was already gone, crashing the native-to-managed
callback.

Removes the premature _wrapperFragment?.Dispose() call in both adapters.
Destroyed?.Invoke, DisconnectHandler() and clearing _wrapperFragment are
preserved, so handler cleanup and event semantics are unchanged.

(cherry picked from commit 77f3371)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 751493a4-4b7c-4eaf-88a9-fcc68ba397af
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 37121

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 37121"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions github-actions Bot added area-controls-shell Shell Navigation, Routes, Tabs, Flyout platform/android labels Aug 5, 2026
@davidnguyen-tech
davidnguyen-tech marked this pull request as ready for review August 5, 2026 14:47
Copilot AI review requested due to automatic review settings August 5, 2026 14:47
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI 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.

Pull request overview

Ports the Android Shell fragment-teardown fix to net11.0 by correcting fragment ownership during adapter disposal so AndroidX FragmentManager can complete lifecycle callbacks without the managed fragment peer being prematurely disposed (preventing deterministic device-test process crashes during teardown).

Changes:

  • Remove _wrapperFragment?.Dispose() from ShellItemHandlerAdapter.Dispose() and retain only handler disconnect + _wrapperFragment = null.
  • Remove _wrapperFragment?.Dispose() from ShellSectionHandlerAdapter.Dispose() and retain only handler disconnect + _wrapperFragment = null.
  • Add inline comments documenting why the wrapper fragment must not be disposed by the adapter (FragmentManager-owned lifecycle).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/Controls/src/Core/Handlers/Shell/ShellItemHandler.Android.cs Stops disposing the FragmentManager-owned ShellItemWrapperFragment during adapter teardown to avoid post-dispose native→managed lifecycle callbacks.
src/Controls/src/Core/Handlers/Shell/ShellSectionHandler.Android.cs Applies the same fragment-ownership correction for ShellSectionWrapperFragment during adapter teardown.

@kubaflo

kubaflo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Closing because all the preview7 fixes will be ported here #37119

@kubaflo kubaflo closed this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-shell Shell Navigation, Routes, Tabs, Flyout platform/android

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants