Skip to content

[Mono.Android] Prevent premature JNI handle collection - #12315

Open
jonathanpeppers wants to merge 4 commits into
mainfrom
jonathanpeppers-fix-jni-handle-lifetimes
Open

[Mono.Android] Prevent premature JNI handle collection#12315
jonathanpeppers wants to merge 4 commits into
mainfrom
jonathanpeppers-fix-jni-handle-lifetimes

Conversation

@jonathanpeppers

Copy link
Copy Markdown
Member

Summary

  • keep managed JNI handle owners alive until native calls consume borrowed handles
  • return stable local references for temporary managed-to-Java wrappers
  • cover JNIEnv.ToLocalJniHandle() with concurrent GC pressure
  • consolidate legacy JavaObject.GetHandle() conversion through JavaConvert

Fixes #5405

Testing

  • Static diff validation completed
  • Device runtime test not run because this worktree does not have a local .NET for Android SDK

Keep managed JNI handle owners alive until native calls have consumed their handles, and return stable local references for temporary wrappers.

Add concurrent collection coverage for ToLocalJniHandle.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e6105414-1c2c-4696-8d01-6507bda3e4be
Copilot AI review requested due to automatic review settings August 7, 2026 18:11

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

This PR addresses a JNI lifetime hazard where managed handle owners can be collected after a .Handle/borrowed-handle access but before the corresponding native/JNI operation consumes it, leading to invalid handles. The change set hardens several managed→JNI call sites with GC.KeepAlive(...), updates conversions to prefer stable local references, and adds a regression test that stresses JNIEnv.ToLocalJniHandle() under concurrent GC/finalization.

Changes:

  • Add GC.KeepAlive(...) at a number of JNI boundary call sites to prevent premature collection of managed handle owners.
  • Route legacy Android.Runtime.JavaObject.GetHandle(object) through JavaConvert.ToLocalJniHandle(object) and adjust JavaConvert’s JavaObject wrapping to use ToLocalJniHandle.
  • Add a GC-pressure regression test for JNIEnv.ToLocalJniHandle().

Reviewed changes

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

Show a summary per file
File Description
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs Adds concurrent-GC regression coverage for JNIEnv.ToLocalJniHandle() using a finalizable handle owner.
src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs Keeps certificate object alive across JNI handle consumption.
src/Mono.Android/Java.Interop/Runtime.cs Ensures IJavaObject passed to IsGCUserPeer stays alive through handle usage.
src/Mono.Android/Java.Interop/JavaObjectExtensions.cs Keeps cast source instance alive across GetObject(..., Handle, ...) conversions.
src/Mono.Android/Java.Interop/JavaConvert.cs Makes temporary Android.Runtime.JavaObject wrapper conversion return a stable local ref via JNIEnv.ToLocalJniHandle.
src/Mono.Android/Android.Widget/TextView.cs Keeps listener/receiver alive across CallVoidMethod in event hookup.
src/Mono.Android/Android.Widget/ArrayAdapter.cs Adds GC.KeepAlive(context) after constructor/static JNI calls and keeps comparator/this alive in JNI calls.
src/Mono.Android/Android.Widget/AdapterViewAnimator.cs Adds GC.KeepAlive(this/value) around adapter getter/setter JNI calls.
src/Mono.Android/Android.Widget/AdapterView.cs Keeps constructor parameters alive across StartCreateInstance/FinishCreateInstance calls.
src/Mono.Android/Android.Widget/AbsListView.cs Keeps adapter/receiver alive across SetAdapter JNI calls.
src/Mono.Android/Android.Runtime/XmlReaderPullParser.cs Keeps parser-reader wrappers alive across NewLocalRef(handle) creation.
src/Mono.Android/Android.Runtime/JNIEnv.cs Keeps IJavaObject alive across local-ref creation; keeps array elements alive during copy/set into Java arrays.
src/Mono.Android/Android.Runtime/JavaObject.cs Consolidates legacy GetHandle(object) conversion through JavaConvert.ToLocalJniHandle.
src/Mono.Android/Android.Runtime/AndroidEnvironment.cs Keeps throwable alive across JNIEnv.Throw(Handle).

Comment thread src/Mono.Android/Android.Widget/ArrayAdapter.cs Outdated
Comment thread src/Mono.Android/Android.Runtime/JNIEnv.cs
Use an explicit marshaling failure when ArrayAdapter creation returns null.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e6105414-1c2c-4696-8d01-6507bda3e4be
Use a derived Java peer because Java.Lang.Object does not expose a public constructor.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e6105414-1c2c-4696-8d01-6507bda3e4be
Derive the finalizable owner directly from Java.Lang.Object so Android packaging accepts it as an IJavaObject.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e6105414-1c2c-4696-8d01-6507bda3e4be
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix possible premature collection

2 participants