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