From 1013a801ae1cd5fc64909ea5dbe18509eb2a55d0 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 22 May 2026 15:32:38 -0700 Subject: [PATCH 1/4] Skip UCO constructor generation for abstract types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Abstract types are never directly instantiated from Java — the ACW constructor's getClass() guard prevents activation. Generating UCO constructor wrappers for them is dead code and fails when the managed constructor is protected (which is legitimate for abstract types). This fixes XAGTT7009 errors when using the trimmable type map with abstract types like MAUI's CellAdapter that have protected constructors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m --- .../Generator/ModelBuilder.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs index ee5d087206a..86ff851d804 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs @@ -356,6 +356,13 @@ static void BuildUcoConstructors (JavaPeerInfo peer, JavaPeerProxyData proxy) return; } + // Abstract types are never directly instantiated from Java — the ACW + // constructor's getClass() guard prevents activation. Skip generating + // UCO constructor wrappers for them. + if (peer.IsAbstract) { + return; + } + foreach (var ctor in peer.JavaConstructors) { if (ctor.SuperArgumentsString != null && !ctor.HasMatchingManagedCtor) { throw new InvalidOperationException ( From a9b0ba339f7688c2b0c41da1b900877f4c9ab36a Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 22 May 2026 15:35:01 -0700 Subject: [PATCH 2/4] Add test: abstract type with protected ctor builds with trimmable typemap Verifies that abstract Java peer types with protected constructors do not cause XAGTT7009 when using _AndroidTypeMapImplementation=trimmable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m --- .../TrimmableTypeMapBuildTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs index 48cb40876d6..cd697690cab 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs @@ -394,5 +394,32 @@ class ExportShapes : Java.Lang.Object { "assembly or the user's [Export] source. Offending warning lines:\n " + string.Join ("\n ", offending)); } + + [Test] + public void Build_WithTrimmableTypeMap_AbstractTypeWithProtectedCtor_Succeeds () + { + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; + proj.SetRuntime (AndroidRuntime.NativeAOT); + proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable"); + proj.Sources.Add (new BuildItem.Source ("AbstractProvider.cs") { + TextContent = () => @" +namespace UnnamedProject { + public abstract class AbstractProvider : Java.Lang.Object { + protected AbstractProvider (Android.Content.Context context) { } + public abstract string GetData (); + } + + public class ConcreteProvider : AbstractProvider { + public ConcreteProvider (Android.Content.Context context) : base (context) { } + public override string GetData () => ""hello""; + } +}" + }); + + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded — abstract types with protected ctors should not cause XAGTT7009."); + } } } From 672cd57b2a059137803807053e7b39163b71ca2d Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 22 May 2026 15:42:56 -0700 Subject: [PATCH 3/4] Add unit test: abstract type skips UCO constructor generation Verifies that BuildUcoConstructors produces no UCO constructors for abstract types, even when the Java constructor has SuperArgumentsString and no matching public managed constructor exists. This complements the existing Build_ExportConstructorWithoutMatchingManagedCtor_Throws test which covers the concrete type case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m --- .../Generator/TypeMapModelBuilderTests.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs index 06498daa478..5eb8a16f30c 100644 --- a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs +++ b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs @@ -1369,6 +1369,26 @@ public void Build_ExportConstructorWithoutMatchingManagedCtor_Throws () Assert.Contains ("no matching user-visible managed constructor", ex.Message); Assert.Contains ("MyApp.MissingCtor", ex.Message); } + + [Fact] + public void Build_AbstractTypeWithProtectedCtor_NoUcoConstructors () + { + var peer = MakeAcwPeer ("my/app/AbstractAdapter", "MyApp.AbstractAdapter", "App") with { + IsAbstract = true, + JavaConstructors = new List { + new JavaConstructorInfo { + ConstructorIndex = 0, + JniSignature = "(Landroid/content/Context;)V", + HasMatchingManagedCtor = false, + SuperArgumentsString = "p0", + }, + }, + }; + var model = BuildModel (new [] { peer }); + var proxy = model.ProxyTypes.FirstOrDefault (p => p.TypeName.Contains ("AbstractAdapter")); + Assert.NotNull (proxy); + Assert.Empty (proxy.UcoConstructors); + } } public class NativeRegistrations From 613273cb24b5a8707d59f53e4a2f246d9dea8a91 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 22 May 2026 16:11:31 -0700 Subject: [PATCH 4/4] Add IgnoreUnsupportedConfiguration guard to NativeAOT integration test Addresses Copilot review feedback: the test hard-codes NativeAOT without the IgnoreUnsupportedConfiguration guard, which can cause failures in environments where NativeAOT builds are skipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Assisted-by: Claude:claude-opus-4.6-1m --- .../Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs index cd697690cab..93195ece435 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs @@ -398,6 +398,10 @@ class ExportShapes : Java.Lang.Object { [Test] public void Build_WithTrimmableTypeMap_AbstractTypeWithProtectedCtor_Succeeds () { + if (IgnoreUnsupportedConfiguration (AndroidRuntime.NativeAOT, release: true)) { + return; + } + var proj = new XamarinAndroidApplicationProject { IsRelease = true, };