diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets index b749937120c..c33c2104cbc 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets @@ -23,5 +23,6 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets. + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index 969d09519aa..6f434688cc3 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -12,7 +12,6 @@ Xamarin.Android.Net.AndroidClientHandler true false - false false false diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets new file mode 100644 index 00000000000..80123bd04f2 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.RuntimeConfig.targets @@ -0,0 +1,50 @@ + + + + + + + + $([System.IO.Path]::GetDirectoryName($(RuntimeConfigParserTasksAssemblyPath)))/net6.0/$([System.IO.Path]::GetFileName($(RuntimeConfigParserTasksAssemblyPath))) + <_BinaryRuntimeConfigPath>$(IntermediateOutputPath)$(ProjectRuntimeConfigFileName).bin + + + + + <_RuntimeConfigReservedProperties Include="TRUSTED_PLATFORM_ASSEMBLIES"/> + <_RuntimeConfigReservedProperties Include="APP_PATHS"/> + <_RuntimeConfigReservedProperties Include="APP_NI_PATHS"/> + <_RuntimeConfigReservedProperties Include="NATIVE_DLL_SEARCH_DIRECTORIES"/> + <_RuntimeConfigReservedProperties Include="PLATFORM_RESOURCE_ROOTS"/> + <_RuntimeConfigReservedProperties Include="PINVOKE_OVERRIDE"/> + + + + + + + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs index 2ef9b1770f5..ce44c06404c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs @@ -89,6 +89,8 @@ public class BuildApk : AndroidTask public string CheckedBuild { get; set; } + public string RuntimeConfigBinFilePath { get; set; } + [Required] public string ProjectFullPath { get; set; } @@ -190,6 +192,10 @@ void ExecuteWithAbi (string [] supportedAbis, string apkInputPath, string apkOut } } + if (!String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath)) { + AddFileToArchiveIfNewer (apk, RuntimeConfigBinFilePath, $"{AssembliesPath}rc.bin", compressionMethod: UncompressedMethod); + } + int count = 0; foreach (var file in files) { var item = Path.Combine (file.archivePath.Replace (Path.DirectorySeparatorChar, '/')); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index 872f7c171c8..054ced26455 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -59,6 +59,7 @@ public class GeneratePackageManagerJava : AndroidTask [Required] public bool InstantRunEnabled { get; set; } + public string RuntimeConfigBinFilePath { get; set; } public string BoundExceptionType { get; set; } public string PackageNamingPolicy { get; set; } @@ -261,6 +262,7 @@ void AddEnvironment () throw new InvalidOperationException ($"Unsupported BoundExceptionType value '{BoundExceptionType}'"); } + bool haveRuntimeConfigBlob = !String.IsNullOrEmpty (RuntimeConfigBinFilePath) && File.Exists (RuntimeConfigBinFilePath); var appConfState = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal (ApplicationConfigTaskState.RegisterTaskObjectKey, RegisteredTaskObjectLifetime.Build); foreach (string abi in SupportedAbis) { NativeAssemblerTargetProvider asmTargetProvider = GetAssemblyTargetProvider (abi); @@ -279,6 +281,7 @@ void AddEnvironment () BoundExceptionType = boundExceptionType, InstantRunEnabled = InstantRunEnabled, JniAddNativeMethodRegistrationAttributePresent = appConfState != null ? appConfState.JniAddNativeMethodRegistrationAttributePresent : false, + HaveRuntimeConfigBlob = haveRuntimeConfigBlob, }; using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index 972e2832852..262b7479428 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -86,6 +86,7 @@ public void CheckIncludedAssemblies () new [] { "Java.Interop.dll", "Mono.Android.dll", + "rc.bin", "System.Private.CoreLib.dll", "System.Runtime.dll", "System.Linq.dll", diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs index 68ff5179448..478aade9493 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs @@ -25,13 +25,14 @@ public sealed class ApplicationConfig public bool broken_exception_transitions; public bool instant_run_enabled; public bool jni_add_native_method_registration_attribute_present; + public bool have_runtime_config_blob; public byte bound_stream_io_exception_type; public uint package_naming_policy; public uint environment_variable_count; public uint system_property_count; public string android_package_name; }; - const uint ApplicationConfigFieldCount = 12; + const uint ApplicationConfigFieldCount = 13; static readonly object ndkInitLock = new object (); static readonly char[] readElfFieldSeparator = new [] { ' ', '\t' }; @@ -150,27 +151,32 @@ static ApplicationConfig ReadApplicationConfig (string envFile) ret.jni_add_native_method_registration_attribute_present = ConvertFieldToBool ("jni_add_native_method_registration_attribute_present", envFile, i, field [1]); break; - case 7: // bound_stream_io_exception_type: byte / .byte + case 7: // have_runtime_config_blob: bool / .byte + AssertFieldType (envFile, ".byte", field [0], i); + ret.have_runtime_config_blob = ConvertFieldToBool ("have_runtime_config_blob", envFile, i, field [1]); + break; + + case 8: // bound_stream_io_exception_type: byte / .byte AssertFieldType (envFile, ".byte", field [0], i); ret.bound_stream_io_exception_type = ConvertFieldToByte ("bound_stream_io_exception_type", envFile, i, field [1]); break; - case 8: // package_naming_policy: uint32_t / .word | .long + case 9: // package_naming_policy: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile}:{i}': {field [0]}"); ret.package_naming_policy = ConvertFieldToUInt32 ("package_naming_policy", envFile, i, field [1]); break; - case 9: // environment_variable_count: uint32_t / .word | .long + case 10: // environment_variable_count: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile}:{i}': {field [0]}"); ret.environment_variable_count = ConvertFieldToUInt32 ("environment_variable_count", envFile, i, field [1]); break; - case 10: // system_property_count: uint32_t / .word | .long + case 11: // system_property_count: uint32_t / .word | .long Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile}:{i}': {field [0]}"); ret.system_property_count = ConvertFieldToUInt32 ("system_property_count", envFile, i, field [1]); break; - case 11: // android_package_name: string / [pointer type] + case 12: // android_package_name: string / [pointer type] Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile}:{i}': {field [0]}"); pointers.Add (field [1].Trim ()); break; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index 74e8c000c2b..ddf5df41a01 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -412,6 +412,7 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease) "es", $"{proj.ProjectName}.dll", $"{proj.ProjectName}.pdb", + $"{proj.ProjectName}.runtimeconfig.json", $"{proj.ProjectName}.xml", }; CollectionAssert.AreEqual (expectedFiles, files, $"Expected: {string.Join (";", expectedFiles)}\n Found: {string.Join (";", files)}"); @@ -574,7 +575,7 @@ public abstract class Foo : AbstractViewHandler @@ -2036,7 +2037,8 @@ because xbuild doesn't support framework reference assemblies. UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)" ProjectFullPath="$(MSBuildProjectFullPath)" IncludeWrapSh="$(AndroidIncludeWrapSh)" - CheckedBuild="$(_AndroidCheckedBuild)"> + CheckedBuild="$(_AndroidCheckedBuild)" + RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"> + CheckedBuild="$(_AndroidCheckedBuild)" + RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"> entry_name; +#if defined (NET6) + bool runtime_config_blob_found = false; +#endif // def NET6 // clang-tidy claims we have a leak in the loop: // @@ -87,6 +90,16 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, monodroid_sh if (strncmp (prefix, file_name, prefix_len) != 0) continue; +#if defined (NET6) + if (application_config.have_runtime_config_blob && !runtime_config_blob_found) { + if (utils.ends_with (file_name, SharedConstants::RUNTIME_CONFIG_BLOB_NAME)) { + runtime_config_blob_found = true; + runtime_config_blob_mmap = md_mmap_apk_file (fd, data_offset, file_size, file_name, apk_name); + continue; + } + } +#endif // def NET6 + // assemblies must be 4-byte aligned, or Bad Things happen if ((data_offset & 0x3) != 0) { log_fatal (LOG_ASSEMBLY, "Assembly '%s' is located at bad offset %lu within the .apk\n", file_name, data_offset); diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index 2ce875dbe1f..204b14d1a0a 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -3,6 +3,7 @@ #define INC_MONODROID_EMBEDDED_ASSEMBLIES_H #include +#include #include #include @@ -12,6 +13,7 @@ #include "strings.hh" #include "xamarin-app.hh" +#include "cpp-util.hh" struct TypeMapHeader; @@ -71,6 +73,21 @@ namespace xamarin::android::internal { void set_assemblies_prefix (const char *prefix); +#if defined (NET6) + void get_runtime_config_blob (const char *& area, uint32_t& size) const + { + area = static_cast(runtime_config_blob_mmap.area); + + abort_unless (runtime_config_blob_mmap.size < std::numeric_limits::max (), "Runtime config binary blob size exceeds %u bytes", std::numeric_limits::max ()); + size = static_cast(runtime_config_blob_mmap.size); + } + + bool have_runtime_config_blob () const + { + return application_config.have_runtime_config_blob && runtime_config_blob_mmap.area != nullptr; + } +#endif + private: const char* typemap_managed_to_java (MonoType *type, MonoClass *klass, const uint8_t *mvid); MonoReflectionType* typemap_java_to_managed (const char *java_type_name); @@ -140,6 +157,9 @@ namespace xamarin::android::internal { size_t type_map_count; #endif // DEBUG || !ANDROID const char *assemblies_prefix_override = nullptr; +#if defined (NET6) + md_mmap_info runtime_config_blob_mmap{}; +#endif // def NET6 }; } diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index ff076860669..0a416a77036 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -204,6 +204,7 @@ namespace xamarin::android::internal static const char* get_my_location (bool remove_file_name = true); #endif // defined(WINDOWS) || defined(APPLE_OS_X) #if defined (NET6) + static void cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data); static void* load_library_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map); static void* fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map, bool need_lock); static void* monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name); @@ -305,6 +306,7 @@ namespace xamarin::android::internal static pinvoke_api_map xa_pinvoke_map; static pinvoke_library_map other_pinvoke_map; static MonoCoreRuntimeProperties monovm_core_properties; + MonovmRuntimeConfigArguments runtime_config_args; #else // def NET6 static std::mutex api_init_lock; static void *api_dso_handle; diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 897be3c16c1..3a49fd60bbe 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -843,6 +843,20 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_stringkind != 1 || args->runtimeconfig.data.data == nullptr) { + return; + } + +#if !defined (WINDOWS) + munmap (static_cast(const_cast(args->runtimeconfig.data.data)), args->runtimeconfig.data.data_len); +#endif // ndef WINDOWS +} +#endif // def NET6 + MonoDomain* MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks, bool is_root_domain) { @@ -855,6 +869,23 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks gather_bundled_assemblies (runtimeApks, &user_assemblies_count); +#if defined (NET6) + timing_period blob_time; + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) + blob_time.mark_start (); + + if (embeddedAssemblies.have_runtime_config_blob ()) { + runtime_config_args.kind = 1; + embeddedAssemblies.get_runtime_config_blob (runtime_config_args.runtimeconfig.data.data, runtime_config_args.runtimeconfig.data.data_len); + monovm_runtimeconfig_initialize (&runtime_config_args, cleanup_runtime_config, nullptr); + } + + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + blob_time.mark_end (); + Timing::info (blob_time, "Register runtimeconfig binary blob"); + } +#endif // def NET6 + if (!have_mono_mkbundle_init && user_assemblies_count == 0 && androidSystem.count_override_assemblies () == 0 && !is_running_on_desktop) { log_fatal (LOG_DEFAULT, "No assemblies found in '%s' or '%s'. Assuming this is part of Fast Deployment. Exiting...", androidSystem.get_override_dir (0), diff --git a/src/monodroid/jni/shared-constants.hh b/src/monodroid/jni/shared-constants.hh index 376aad43678..e5e61e8dd8a 100644 --- a/src/monodroid/jni/shared-constants.hh +++ b/src/monodroid/jni/shared-constants.hh @@ -15,6 +15,10 @@ namespace xamarin::android::internal class SharedConstants { public: +#if defined (NET6) + static constexpr char RUNTIME_CONFIG_BLOB_NAME[] = "rc.bin"; +#endif // def NET6 + #if defined (ANDROID) || defined (__linux__) || defined (__linux) static constexpr char MONO_SGEN_SO[] = "libmonosgen-2.0.so"; static constexpr char MONO_SGEN_ARCH_SO[] = "libmonosgen-" __BITNESS__ "-2.0.so"; diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index b6303dfddca..6b5cb9ba41e 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -105,6 +105,7 @@ struct ApplicationConfig bool broken_exception_transitions; bool instant_run_enabled; bool jni_add_native_method_registration_attribute_present; + bool have_runtime_config_blob; uint8_t bound_exception_type; uint32_t package_naming_policy; uint32_t environment_variable_count; diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index 24c7368c41b..296d426a057 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -30,6 +30,7 @@ + diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/System/AppContextTests.cs b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/System/AppContextTests.cs new file mode 100644 index 00000000000..aa8a462d0d8 --- /dev/null +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/System/AppContextTests.cs @@ -0,0 +1,31 @@ +using NUnit.Framework; +using System; + +namespace SystemTests +{ + [TestFixture] + public class AppContextTests + { + static readonly object [] GetDataSource = new object [] { + new object [] { + /* name */ "test_bool", + /* expected */ "true", + }, + new object [] { + /* name */ "test_integer", + /* expected */ "42", + }, + new object [] { + /* name */ "test_string", + /* expected */ "foo", + }, + }; + + [Test] + [TestCaseSource (nameof (GetDataSource))] + public void GetData (string name, string expected) + { + Assert.AreEqual (expected, AppContext.GetData (name)); + } + } +} diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/runtimeconfig.template.json b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/runtimeconfig.template.json new file mode 100644 index 00000000000..a9abdfc23f0 --- /dev/null +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/runtimeconfig.template.json @@ -0,0 +1,7 @@ +{ + "configProperties": { + "test_bool": true, + "test_integer": 42, + "test_string": "foo" + } +} \ No newline at end of file