From 5a942828555d99e82ab48a512d2afc209063d20d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:53:26 +0000 Subject: [PATCH 1/2] Bump external/Java.Interop from `85919bb` to `7b018fe` Bumps [external/Java.Interop](https://github.com/dotnet/java-interop) from `85919bb` to `7b018fe`. - [Commits](https://github.com/dotnet/java-interop/compare/85919bb9bbda8baefec483b89b7b8981104b086e...7b018fee2a42d2390fc8cdb5c94a5b23f0f73d9b) --- updated-dependencies: - dependency-name: external/Java.Interop dependency-version: 7b018fee2a42d2390fc8cdb5c94a5b23f0f73d9b dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- external/Java.Interop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/Java.Interop b/external/Java.Interop index 85919bb9bbd..7b018fee2a4 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit 85919bb9bbda8baefec483b89b7b8981104b086e +Subproject commit 7b018fee2a42d2390fc8cdb5c94a5b23f0f73d9b From ee93cf9beaca7065f992d996dc6e4278f1d4d63c Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 16 Apr 2026 09:58:15 -0500 Subject: [PATCH 2/2] Add regression test for global ref leak in LayoutInflater.Inflate Fixes: https://github.com/dotnet/android/issues/11101 Fixes: https://github.com/dotnet/android/issues/10989 The fix is in Java.Interop (ConstructPeer + Dispose for Invalid refs), included via the submodule bump. This adds an on-device regression test that inflates 100 custom views and asserts the JNI global reference count does not grow significantly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Android.Widget/CustomWidgetTests.cs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs index 78d62576a0e..7b549f0061f 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Widget/CustomWidgetTests.cs @@ -1,4 +1,5 @@ -using Android.App; +using System; +using Android.App; using Android.Content; using Android.Util; using Android.Views; @@ -44,6 +45,47 @@ public void UpperAndLowerCaseCustomWidget_FromLibrary_ShouldNotThrowInflateExcep inflater.Inflate (Resource.Layout.upper_lower_custom, null); }, "Regression test for widgets with uppercase and lowercase namespace (bug #23880) failed."); } + + // https://github.com/dotnet/android/issues/11101 + [Test] + public void InflateCustomView_ShouldNotLeakGlobalRefs () + { + var inflater = (LayoutInflater) Application.Context.GetSystemService (Context.LayoutInflaterService); + Assert.IsNotNull (inflater); + + // Warm up: inflate once to populate caches and type mappings, + // and let any background thread activity from previous tests settle. + inflater.Inflate (Resource.Layout.lowercase_custom, null); + CollectGarbage (times: 3); + + int grefBefore = Java.Interop.Runtime.GlobalReferenceCount; + + // Use a large number of inflations so that a real leak (3+ global refs + // per inflate) produces a delta far above any background noise from + // Android system services, GC bridge processing, or finalizer threads. + const int inflateCount = 100; + for (int i = 0; i < inflateCount; i++) { + inflater.Inflate (Resource.Layout.lowercase_custom, null); + } + + CollectGarbage (times: 3); + + int grefAfter = Java.Interop.Runtime.GlobalReferenceCount; + int delta = grefAfter - grefBefore; + + // A real leak would produce delta >= 300 (3 leaked refs per inflate). + // Use a generous threshold to tolerate background noise on real devices. + Assert.IsTrue (delta <= 100, + $"Global reference leak detected: {delta} extra global refs after inflating/GC'ing {inflateCount} custom views. Before={grefBefore}, After={grefAfter}"); + + static void CollectGarbage (int times) + { + for (int i = 0; i < times; i++) { + GC.Collect (); + GC.WaitForPendingFinalizers (); + } + } + } } public class CustomButton : Button