From 4e23ed892b6808bfc7c07e11c42d6960c25cdb67 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 7 Aug 2026 12:51:36 -0500 Subject: [PATCH 1/4] [java-runtime] Improve native library load diagnostics Native library startup failures currently identify only that loading failed, which leaves crash reports without enough information to diagnose corrupt or incomplete installations. Route MonoVM, CoreCLR, and NativeAOT startup loads through a shared helper. When loading fails, report the requested library, supported ABIs, extracted file state, APK and split contents, and the original linker error. Suggest reinstallation when the library is absent from every inspected location. Add device coverage which deliberately removes the startup library from CoreCLR and NativeAOT packages and verifies the resulting diagnostic. Fixes #5149 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dffd648-59fe-4171-b126-1597a61cd438 --- .../Resources/JavaInteropRuntime.java | 6 +- .../Resources/NativeAotRuntimeProvider.java | 1 + .../java/mono/android/MonoPackageManager.java | 10 +- .../mono/android/NativeLibraryHelper.java | 149 ++++++++++++++++++ .../mono/android/clr/MonoPackageManager.java | 2 +- .../Tests/NativeLibraryLoadTests.cs | 98 ++++++++++++ 6 files changed, 258 insertions(+), 8 deletions(-) create mode 100644 src/java-runtime/java/mono/android/NativeLibraryHelper.java create mode 100644 tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs diff --git a/src/Xamarin.Android.Build.Tasks/Resources/JavaInteropRuntime.java b/src/Xamarin.Android.Build.Tasks/Resources/JavaInteropRuntime.java index 6ee0fd3f41c..c2f33c45925 100644 --- a/src/Xamarin.Android.Build.Tasks/Resources/JavaInteropRuntime.java +++ b/src/Xamarin.Android.Build.Tasks/Resources/JavaInteropRuntime.java @@ -1,11 +1,13 @@ package net.dot.jni.nativeaot; import android.util.Log; +import android.content.Context; +import mono.NativeLibraryHelper; public class JavaInteropRuntime { - static { + public static void loadLibrary(Context context) { Log.d("JavaInteropRuntime", "Loading @MAIN_ASSEMBLY_NAME@.so..."); - System.loadLibrary("@MAIN_ASSEMBLY_NAME@"); + NativeLibraryHelper.loadLibrary("@MAIN_ASSEMBLY_NAME@", context); } private JavaInteropRuntime() { diff --git a/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java b/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java index 90f392160e0..c1feb566c93 100644 --- a/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java +++ b/src/Xamarin.Android.Build.Tasks/Resources/NativeAotRuntimeProvider.java @@ -36,6 +36,7 @@ public void attachInfo(android.content.Context context, android.content.pm.Provi String cacheDir = context.getCacheDir().getAbsolutePath(); // Initialize .NET runtime + JavaInteropRuntime.loadLibrary(context); JavaInteropRuntime.init(loader, language, filesDir, cacheDir); // NOTE: only required for custom applications ApplicationRegistration.registerApplications(); diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index 6531bd2da3d..38927fda9d9 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -99,19 +99,19 @@ public static void LoadApplication (Context context) // below, leading to an error locating the Mono runtime // if (BuildConfig.Debug) { - System.loadLibrary ("xamarin-debug-app-helper"); + NativeLibraryHelper.loadLibrary ("xamarin-debug-app-helper", runtimePackage, apks); DebugRuntime.init (apks, runtimeDir, appDirs, haveSplitApks); } else { - System.loadLibrary("monosgen-2.0"); + NativeLibraryHelper.loadLibrary ("monosgen-2.0", runtimePackage, apks); } - System.loadLibrary("xamarin-app"); + NativeLibraryHelper.loadLibrary ("xamarin-app", runtimePackage, apks); if (!BuildConfig.DotNetRuntime) { // .net5+ APKs don't contain `libmono-native.so` - System.loadLibrary("mono-native"); + NativeLibraryHelper.loadLibrary ("mono-native", runtimePackage, apks); } - System.loadLibrary("monodroid"); + NativeLibraryHelper.loadLibrary ("monodroid", runtimePackage, apks); Runtime.initInternal ( language, diff --git a/src/java-runtime/java/mono/android/NativeLibraryHelper.java b/src/java-runtime/java/mono/android/NativeLibraryHelper.java new file mode 100644 index 00000000000..917ba240b4f --- /dev/null +++ b/src/java-runtime/java/mono/android/NativeLibraryHelper.java @@ -0,0 +1,149 @@ +package mono; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.os.Build; +import android.util.Log; + +public final class NativeLibraryHelper { + static final String TAG = "monodroid"; + + private NativeLibraryHelper () + { + } + + public static void loadLibrary (String libraryName, Context context) + { + ApplicationInfo applicationInfo = context.getApplicationInfo (); + String[] splitApks = applicationInfo.splitSourceDirs; + String[] apks; + if (splitApks != null && splitApks.length > 0) { + apks = new String [splitApks.length + 1]; + apks [0] = applicationInfo.sourceDir; + System.arraycopy (splitApks, 0, apks, 1, splitApks.length); + } else { + apks = new String [] { applicationInfo.sourceDir }; + } + + loadLibrary (libraryName, applicationInfo, apks); + } + + static void loadLibrary (String libraryName, ApplicationInfo applicationInfo, String[] apks) + { + try { + System.loadLibrary (libraryName); + } catch (UnsatisfiedLinkError cause) { + String diagnosticMessage = getDiagnosticMessage (libraryName, applicationInfo, apks, cause); + Log.e (TAG, diagnosticMessage, cause); + + UnsatisfiedLinkError error = new UnsatisfiedLinkError (diagnosticMessage); + error.initCause (cause); + throw error; + } catch (SecurityException cause) { + String diagnosticMessage = getDiagnosticMessage (libraryName, applicationInfo, apks, cause); + Log.e (TAG, diagnosticMessage, cause); + throw new SecurityException (diagnosticMessage, cause); + } + } + + static String getDiagnosticMessage (String libraryName, ApplicationInfo applicationInfo, String[] apks, Throwable cause) + { + String mappedLibraryName = System.mapLibraryName (libraryName); + StringBuilder message = new StringBuilder (); + message.append ("Failed to load native library '").append (mappedLibraryName).append ("'."); + message.append (" Supported ABIs: ").append (Arrays.toString (Build.SUPPORTED_ABIS)).append ("."); + + boolean foundLibrary = appendNativeLibraryDirectoryDiagnostics (message, applicationInfo.nativeLibraryDir, mappedLibraryName); + foundLibrary |= appendApkDiagnostics (message, apks, mappedLibraryName); + + if (!foundLibrary) { + message.append (" The library was not found in the native library directory or any application APK that could be inspected."); + message.append (" The application installation may be corrupt; reinstalling the application may fix this error."); + } + + String causeMessage = cause.getMessage (); + if (causeMessage != null && causeMessage.length () > 0) { + message.append (" Original error: ").append (causeMessage); + } + + return message.toString (); + } + + static boolean appendNativeLibraryDirectoryDiagnostics (StringBuilder message, String nativeLibraryDir, String mappedLibraryName) + { + message.append (" Native library directory: "); + if (nativeLibraryDir == null) { + message.append ("."); + return false; + } + + File directory = new File (nativeLibraryDir); + File library = new File (directory, mappedLibraryName); + boolean libraryExists = library.isFile (); + message.append ('\'').append (nativeLibraryDir).append ('\''); + message.append (" (directory exists: ").append (directory.isDirectory ()); + message.append (", library exists: ").append (libraryExists); + if (libraryExists) { + message.append (", library size: ").append (library.length ()); + message.append (", library readable: ").append (library.canRead ()); + } + message.append (")."); + return libraryExists; + } + + static boolean appendApkDiagnostics (StringBuilder message, String[] apks, String mappedLibraryName) + { + boolean foundLibrary = false; + message.append (" APKs:"); + if (apks == null || apks.length == 0) { + message.append (" ."); + return false; + } + + for (String apk : apks) { + message.append (" '").append (apk).append ("'"); + if (apk == null) { + message.append (" (invalid path);"); + continue; + } + + File apkFile = new File (apk); + if (!apkFile.isFile ()) { + message.append (" (file exists: false);"); + continue; + } + + ArrayList entries = new ArrayList (); + try (ZipFile zip = new ZipFile (apkFile)) { + for (String abi : Build.SUPPORTED_ABIS) { + String entryName = "lib/" + abi + "/" + mappedLibraryName; + ZipEntry entry = zip.getEntry (entryName); + if (entry == null) + continue; + + foundLibrary = true; + String storage = entry.getMethod () == ZipEntry.STORED ? "stored" : "compressed"; + entries.add (abi + ", " + storage + ", size " + entry.getSize ()); + } + if (entries.size () == 0) + message.append (" (contains no matching native libraries);"); + else + message.append (" (contains: ").append (entries).append (");"); + } catch (IOException | SecurityException e) { + message.append (" (could not inspect: ").append (e.getClass ().getSimpleName ()); + String errorMessage = e.getMessage (); + if (errorMessage != null && errorMessage.length () > 0) + message.append (": ").append (errorMessage); + message.append (");"); + } + } + + return foundLibrary; + } +} diff --git a/src/java-runtime/java/mono/android/clr/MonoPackageManager.java b/src/java-runtime/java/mono/android/clr/MonoPackageManager.java index 5592cc2b68e..dca751a9a77 100644 --- a/src/java-runtime/java/mono/android/clr/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/clr/MonoPackageManager.java @@ -70,7 +70,7 @@ public static void LoadApplication (Context context) String[] appDirs = new String[] {filesDir, cacheDir, dataDir, codeCacheDir}; boolean haveSplitApks = runtimePackage.splitSourceDirs != null && runtimePackage.splitSourceDirs.length > 0; - System.loadLibrary("monodroid"); + NativeLibraryHelper.loadLibrary ("monodroid", runtimePackage, apks); Runtime.initInternal ( language, diff --git a/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs b/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs new file mode 100644 index 00000000000..9a14e34efeb --- /dev/null +++ b/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using NUnit.Framework; +using Xamarin.Android.Tasks; +using Xamarin.ProjectTools; + +namespace Xamarin.Android.Build.Tests +{ + [TestFixture] + [Category ("UsesDevice")] + public class NativeLibraryLoadTests : DeviceTest + { + [Test] + public void MissingNativeLibraryHasUsefulErrorMessage ([Values (AndroidRuntime.CoreCLR, AndroidRuntime.NativeAOT)] AndroidRuntime runtime) + { + bool isRelease = runtime == AndroidRuntime.NativeAOT; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + + var proj = new XamarinAndroidApplicationProject ( + packageName: PackageUtils.MakePackageName (runtime, "missingnativelibrary") + ) { + IsRelease = isRelease, + ProjectName = "MissingNativeLibrary", + }; + proj.SetRuntime (runtime); + proj.SetRuntimeIdentifiers (new [] { DeviceAbi }); + proj.SetDefaultTargetDevice (); + + string libraryName; + string removeLibraryItems; + if (runtime == AndroidRuntime.NativeAOT) { + libraryName = $"lib{proj.ProjectName}.so"; + removeLibraryItems = $@" + + <_ApplicationSharedLibrary Remove=""@(_ApplicationSharedLibrary)"" + Condition="" '%(_ApplicationSharedLibrary.ArchiveFileName)' == '{libraryName}' or '%(_ApplicationSharedLibrary.FileName)%(_ApplicationSharedLibrary.Extension)' == '{proj.ProjectName}.so' "" />"; + } else { + libraryName = "libmonodroid.so"; + removeLibraryItems = @" + + <_ApplicationSharedLibrary Remove=""@(_ApplicationSharedLibrary)"" + Condition="" '%(_ApplicationSharedLibrary.ArchiveFileName)' == 'libmonodroid.so' or '%(_ApplicationSharedLibrary.FileName)%(_ApplicationSharedLibrary.Extension)' == 'libmonodroid.so' "" />"; + } + + proj.Imports.Add (new Import (() => "Directory.Build.targets") { + TextContent = () => $""" + + + + {removeLibraryItems} + + + +""" + }); + + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Install (proj), "Project should have installed."); + + string outputDirectory = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath); + string[] apks = Directory.GetFiles (outputDirectory, $"{proj.PackageName}-Signed.apk", SearchOption.AllDirectories); + Assert.IsNotEmpty (apks, "The signed APK should exist."); + using (var apk = ZipHelper.OpenZip (apks [0])) { + Assert.IsFalse (apk.ContainsEntry ($"lib/{DeviceAbi}/{libraryName}"), + $"{libraryName} should have been removed from the APK."); + } + + var expectedMessages = new HashSet { + $"Failed to load native library '{libraryName}'.", + "Supported ABIs:", + "Native library directory:", + "library exists: false", + "APKs:", + "contains no matching native libraries", + "The application installation may be corrupt; reinstalling the application may fix this error.", + }; + string logcatPath = Path.Combine (Root, builder.ProjectDirectory, "native-library-load.log"); + bool foundDiagnostic = MonitorAdbLogcat ( + line => { + expectedMessages.RemoveWhere (message => line.Contains (message, StringComparison.Ordinal)); + return expectedMessages.Count == 0; + }, + logcatPath, + timeout: 45, + onMonitoringStarted: () => AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity") + ); + + Assert.IsTrue (foundDiagnostic, + $"The native library diagnostic was incomplete. Missing: {string.Join (", ", expectedMessages)}"); + } + } +} From 20375ca5d640f9362dae48023d09de31b139ce2b Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 7 Aug 2026 13:32:53 -0500 Subject: [PATCH 2/4] [tests] Use file-scoped namespace in native library test Follow the convention for newly added C# files. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dffd648-59fe-4171-b126-1597a61cd438 --- .../Tests/NativeLibraryLoadTests.cs | 125 +++++++++--------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs b/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs index 9a14e34efeb..3f0221eb9bc 100644 --- a/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs @@ -6,50 +6,50 @@ using Xamarin.Android.Tasks; using Xamarin.ProjectTools; -namespace Xamarin.Android.Build.Tests +namespace Xamarin.Android.Build.Tests; + +[TestFixture] +[Category ("UsesDevice")] +public class NativeLibraryLoadTests : DeviceTest { - [TestFixture] - [Category ("UsesDevice")] - public class NativeLibraryLoadTests : DeviceTest + [Test] + public void MissingNativeLibraryHasUsefulErrorMessage ([Values (AndroidRuntime.CoreCLR, AndroidRuntime.NativeAOT)] AndroidRuntime runtime) { - [Test] - public void MissingNativeLibraryHasUsefulErrorMessage ([Values (AndroidRuntime.CoreCLR, AndroidRuntime.NativeAOT)] AndroidRuntime runtime) - { - bool isRelease = runtime == AndroidRuntime.NativeAOT; - if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { - return; - } + bool isRelease = runtime == AndroidRuntime.NativeAOT; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } - var proj = new XamarinAndroidApplicationProject ( - packageName: PackageUtils.MakePackageName (runtime, "missingnativelibrary") - ) { - IsRelease = isRelease, - ProjectName = "MissingNativeLibrary", - }; - proj.SetRuntime (runtime); - proj.SetRuntimeIdentifiers (new [] { DeviceAbi }); - proj.SetDefaultTargetDevice (); + var proj = new XamarinAndroidApplicationProject ( + packageName: PackageUtils.MakePackageName (runtime, "missingnativelibrary") + ) { + IsRelease = isRelease, + ProjectName = "MissingNativeLibrary", + }; + proj.SetRuntime (runtime); + proj.SetRuntimeIdentifiers (new [] { DeviceAbi }); + proj.SetDefaultTargetDevice (); - string libraryName; - string removeLibraryItems; - if (runtime == AndroidRuntime.NativeAOT) { - libraryName = $"lib{proj.ProjectName}.so"; - removeLibraryItems = $@" + string libraryName; + string removeLibraryItems; + if (runtime == AndroidRuntime.NativeAOT) { + libraryName = $"lib{proj.ProjectName}.so"; + removeLibraryItems = $@" <_ApplicationSharedLibrary Remove=""@(_ApplicationSharedLibrary)"" Condition="" '%(_ApplicationSharedLibrary.ArchiveFileName)' == '{libraryName}' or '%(_ApplicationSharedLibrary.FileName)%(_ApplicationSharedLibrary.Extension)' == '{proj.ProjectName}.so' "" />"; - } else { - libraryName = "libmonodroid.so"; - removeLibraryItems = @" + } else { + libraryName = "libmonodroid.so"; + removeLibraryItems = @" <_ApplicationSharedLibrary Remove=""@(_ApplicationSharedLibrary)"" Condition="" '%(_ApplicationSharedLibrary.ArchiveFileName)' == 'libmonodroid.so' or '%(_ApplicationSharedLibrary.FileName)%(_ApplicationSharedLibrary.Extension)' == 'libmonodroid.so' "" />"; - } + } - proj.Imports.Add (new Import (() => "Directory.Build.targets") { - TextContent = () => $""" + proj.Imports.Add (new Import (() => "Directory.Build.targets") { + TextContent = () => $""" @@ -58,41 +58,40 @@ public void MissingNativeLibraryHasUsefulErrorMessage ([Values (AndroidRuntime.C """ - }); + }); - using var builder = CreateApkBuilder (); - Assert.IsTrue (builder.Install (proj), "Project should have installed."); + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Install (proj), "Project should have installed."); - string outputDirectory = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath); - string[] apks = Directory.GetFiles (outputDirectory, $"{proj.PackageName}-Signed.apk", SearchOption.AllDirectories); - Assert.IsNotEmpty (apks, "The signed APK should exist."); - using (var apk = ZipHelper.OpenZip (apks [0])) { - Assert.IsFalse (apk.ContainsEntry ($"lib/{DeviceAbi}/{libraryName}"), - $"{libraryName} should have been removed from the APK."); - } + string outputDirectory = Path.Combine (Root, builder.ProjectDirectory, proj.OutputPath); + string[] apks = Directory.GetFiles (outputDirectory, $"{proj.PackageName}-Signed.apk", SearchOption.AllDirectories); + Assert.IsNotEmpty (apks, "The signed APK should exist."); + using (var apk = ZipHelper.OpenZip (apks [0])) { + Assert.IsFalse (apk.ContainsEntry ($"lib/{DeviceAbi}/{libraryName}"), + $"{libraryName} should have been removed from the APK."); + } - var expectedMessages = new HashSet { - $"Failed to load native library '{libraryName}'.", - "Supported ABIs:", - "Native library directory:", - "library exists: false", - "APKs:", - "contains no matching native libraries", - "The application installation may be corrupt; reinstalling the application may fix this error.", - }; - string logcatPath = Path.Combine (Root, builder.ProjectDirectory, "native-library-load.log"); - bool foundDiagnostic = MonitorAdbLogcat ( - line => { - expectedMessages.RemoveWhere (message => line.Contains (message, StringComparison.Ordinal)); - return expectedMessages.Count == 0; - }, - logcatPath, - timeout: 45, - onMonitoringStarted: () => AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity") - ); + var expectedMessages = new HashSet { + $"Failed to load native library '{libraryName}'.", + "Supported ABIs:", + "Native library directory:", + "library exists: false", + "APKs:", + "contains no matching native libraries", + "The application installation may be corrupt; reinstalling the application may fix this error.", + }; + string logcatPath = Path.Combine (Root, builder.ProjectDirectory, "native-library-load.log"); + bool foundDiagnostic = MonitorAdbLogcat ( + line => { + expectedMessages.RemoveWhere (message => line.Contains (message, StringComparison.Ordinal)); + return expectedMessages.Count == 0; + }, + logcatPath, + timeout: 45, + onMonitoringStarted: () => AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity") + ); - Assert.IsTrue (foundDiagnostic, - $"The native library diagnostic was incomplete. Missing: {string.Join (", ", expectedMessages)}"); - } + Assert.IsTrue (foundDiagnostic, + $"The native library diagnostic was incomplete. Missing: {string.Join (", ", expectedMessages)}"); } } From ed6eb7e84dfcc55c16c2bed370bd66b3e3f1cbea Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 7 Aug 2026 15:52:23 -0500 Subject: [PATCH 3/4] [tests] Match NativeAOT packaged library filename The NativeAOT application shared library already has the lib prefix in its FrameworkNativeLibrary identity. Match that filename so the failure test actually removes the library from the APK. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dffd648-59fe-4171-b126-1597a61cd438 --- .../MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs b/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs index 3f0221eb9bc..8693b89361e 100644 --- a/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/NativeLibraryLoadTests.cs @@ -36,9 +36,9 @@ public void MissingNativeLibraryHasUsefulErrorMessage ([Values (AndroidRuntime.C libraryName = $"lib{proj.ProjectName}.so"; removeLibraryItems = $@" + Condition="" '%(FrameworkNativeLibrary.ArchiveFileName)' == '{libraryName}' or '%(FrameworkNativeLibrary.FileName)%(FrameworkNativeLibrary.Extension)' == '{libraryName}' "" /> <_ApplicationSharedLibrary Remove=""@(_ApplicationSharedLibrary)"" - Condition="" '%(_ApplicationSharedLibrary.ArchiveFileName)' == '{libraryName}' or '%(_ApplicationSharedLibrary.FileName)%(_ApplicationSharedLibrary.Extension)' == '{proj.ProjectName}.so' "" />"; + Condition="" '%(_ApplicationSharedLibrary.ArchiveFileName)' == '{libraryName}' or '%(_ApplicationSharedLibrary.FileName)%(_ApplicationSharedLibrary.Extension)' == '{libraryName}' "" />"; } else { libraryName = "libmonodroid.so"; removeLibraryItems = @" From 204cebbe4a43520513103d2e8581a37a21aedff4 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Aug 2026 13:56:14 -0500 Subject: [PATCH 4/4] Update CoreCLR APK size baseline Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8dffd648-59fe-4171-b126-1597a61cd438 --- ...ReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc index 8453587c3ba..30f0c810c73 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc @@ -5,7 +5,7 @@ "Size": 6652 }, "classes.dex": { - "Size": 3240220 + "Size": 3242876 }, "kotlin/annotation/annotation.kotlin_builtins": { "Size": 928 @@ -29,31 +29,31 @@ "Size": 2396 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 11217040 + "Size": 11415872 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 2761136 + "Size": 2788736 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 4839560 + "Size": 4841976 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1184912 + "Size": 1184800 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72432 }, "lib/arm64-v8a/libSystem.IO.Compression.Native.so": { - "Size": 1258776 + "Size": 1259088 }, "lib/arm64-v8a/libSystem.Native.so": { - "Size": 99776 + "Size": 99792 }, "lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": { - "Size": 169768 + "Size": 171584 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 119656 + "Size": 119776 }, "META-INF/androidx.activity_activity.version": { "Size": 6