diff --git a/Documentation/guides/building-apps/build-properties.md b/Documentation/guides/building-apps/build-properties.md
index 7743863813b..afe06d94074 100644
--- a/Documentation/guides/building-apps/build-properties.md
+++ b/Documentation/guides/building-apps/build-properties.md
@@ -1303,6 +1303,20 @@ This is only used when building `system` applications.
Support for this property was added in Xamarin.Android 11.3.
+## AndroidStripILAfterAOT
+
+A bool property that specifies whether or not the *method bodies* of AOT compiled methods will be removed.
+
+The default value is `false`, and the method bodies of AOT compiled methods will *not* be removed.
+
+When set to `true`, [`$(AndroidEnableProfiledAot)`](#androidenableprofiledaot) is set to `false` by default.
+This means that in Release configuration builds -- in which
+[`$(RunAOTCompilation)`](#runaotcompilation) is `true` by default -- AOT is enabled for *everything*.
+This can result in increased app sizes. This behavior can be overridden by explicitly setting
+`$(AndroidEnableProfiledAot)` to `true` within your project file.
+
+Support for this property was added in .NET 8.
+
## AndroidSupportedAbis
A string property that contains a
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
index db621d58ecf..7e2c2fd97f8 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets
@@ -123,11 +123,25 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
LLVMPath="$(_LLVMPath)"
LdName="$(_LdName)"
LdFlags="$(_LdFlags)"
+ CollectTrimmingEligibleMethods="$(AndroidStripILAfterAOT)"
+ TrimmingEligibleMethodsOutputDirectory="$(IntermediateOutputPath)tokens"
WorkingDirectory="$(MSBuildProjectDirectory)"
AotArguments="$(AndroidAotAdditionalArguments)">
+
+
+
+
true
<_AndroidXA1030 Condition=" '$(RunAOTCompilation)' == 'true' and '$(PublishTrimmed)' == 'false' ">true
$(RunAOTCompilation)
- true
+ true
diff --git a/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj b/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj
index 8f6b1ecd0b8..70616562284 100644
--- a/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj
+++ b/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj
@@ -37,6 +37,9 @@
+
+ $(MicrosoftAndroidSdkOutDir)Xamarin.Android.Cecil.dll
+
diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
index fc60f88a1b7..1672b36b7cb 100644
--- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
+++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
@@ -6,6 +6,7 @@
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
+using Mono.Cecil;
using NUnit.Framework;
using Xamarin.ProjectTools;
@@ -1075,5 +1076,43 @@ public void SupportDesugaringStaticInterfaceMethods ()
);
}
+ [Test]
+ public void EnableAndroidStripILAfterAOT ([Values (false, true)] bool profiledAOT)
+ {
+ var proj = new XamarinAndroidApplicationProject {
+ ProjectName = nameof (EnableAndroidStripILAfterAOT),
+ RootNamespace = nameof (EnableAndroidStripILAfterAOT),
+ IsRelease = true,
+ EnableDefaultItems = true,
+ };
+ proj.SetProperty("AndroidStripILAfterAOT", "true");
+ proj.SetProperty("AndroidEnableProfiledAot", profiledAOT.ToString ());
+ // So we can use Mono.Cecil to open assemblies directly
+ proj.SetProperty ("AndroidEnableAssemblyCompression", "false");
+
+ var builder = CreateApkBuilder ();
+ Assert.IsTrue (builder.Build (proj), "`dotnet build` should succeed");
+
+ var apk = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk");
+ FileAssert.Exists (apk);
+ var helper = new ArchiveAssemblyHelper (apk);
+ Assert.IsTrue (helper.Exists ($"assemblies/{proj.ProjectName}.dll"), $"{proj.ProjectName}.dll should exist in apk!");
+ using (var stream = helper.ReadEntry ($"assemblies/{proj.ProjectName}.dll")) {
+ stream.Position = 0;
+ using var assembly = AssemblyDefinition.ReadAssembly (stream);
+ var type = assembly.MainModule.GetType ($"{proj.RootNamespace}.MainActivity");
+ var method = type.Methods.FirstOrDefault (p => p.Name == "OnCreate");
+ Assert.IsNotNull (method, $"{proj.RootNamespace}.MainActivity.OnCreate should exist!");
+ Assert.IsTrue (!method.HasBody || method.Body.Instructions.Count == 0, $"{proj.RootNamespace}.MainActivity.OnCreate should have no body!");
+ }
+
+ RunProjectAndAssert (proj, builder);
+
+ WaitForPermissionActivity (Path.Combine (Root, builder.ProjectDirectory, "permission-logcat.log"));
+ bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity",
+ Path.Combine (Root, builder.ProjectDirectory, "logcat.log"), 30);
+ Assert.IsTrue(didLaunch, "Activity should have started.");
+ }
+
}
}