Skip to content

Commit

Permalink
[WIP] Include blob in apk and load it on runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
grendello committed Jun 10, 2021
1 parent ff92920 commit ea815d7
Show file tree
Hide file tree
Showing 15 changed files with 104 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ See: https://github.com/dotnet/runtime/blob/b13715b6984889a709ba29ea8a1961db469f

<Target Name="_ParseRuntimeConfigFiles"
AfterTargets="GenerateBuildRuntimeConfigurationFiles"
Condition=" '$(GenerateRuntimeConfigurationFiles)' == 'true' "
Inputs="$(ProjectRuntimeConfigFilePath)"
Outputs="$(_BinaryRuntimeConfigPath)">
<RuntimeConfigParserTask
Expand Down
6 changes: 6 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public class BuildApk : AndroidTask

public string CheckedBuild { get; set; }

public string RuntimeConfigBinFilePath { get; set; }

[Required]
public string ProjectFullPath { get; set; }

Expand Down Expand Up @@ -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, '/'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -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> (ApplicationConfigTaskState.RegisterTaskObjectKey, RegisteredTaskObjectLifetime.Build);
foreach (string abi in SupportedAbis) {
NativeAssemblerTargetProvider asmTargetProvider = GetAssemblyTargetProvider (abi);
Expand All @@ -279,6 +281,7 @@ void AddEnvironment ()
BoundExceptionType = boundExceptionType,
InstantRunEnabled = InstantRunEnabled,
JniAddNativeMethodRegistrationAttributePresent = appConfState != null ? appConfState.JniAddNativeMethodRegistrationAttributePresent : false,
HaveRuntimeConfigBlob = haveRuntimeConfigBlob,
};

using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' };
Expand Down Expand Up @@ -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:
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)}");
Expand Down Expand Up @@ -574,7 +575,7 @@ public abstract class Foo<TVirtualView, TNativeView> : AbstractViewHandler<TVirt
where TNativeView : Android.Views.View
#else
where TNativeView : class
#endif
#endif
{
protected Foo (PropertyMapper mapper) : base(mapper)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ApplicationConfigNativeAssemblyGenerator : NativeAssemblyGenerator
public global::Android.Runtime.BoundExceptionType BoundExceptionType { get; set; }
public bool InstantRunEnabled { get; set; }
public bool JniAddNativeMethodRegistrationAttributePresent { get; set; }
public bool HaveRuntimeConfigBlob { get; set; }

public PackageNamingPolicy PackageNamingPolicy { get; set; }

Expand Down Expand Up @@ -70,6 +71,9 @@ protected override void WriteSymbols (StreamWriter output)
WriteCommentLine (output, "jni_add_native_method_registration_attribute_present");
size += WriteData (output, JniAddNativeMethodRegistrationAttributePresent);
WriteCommentLine (output, "have_runtime_config_blob");
size += WriteData (output, HaveRuntimeConfigBlob);
WriteCommentLine (output, "bound_exception_type");
size += WriteData (output, (byte)BoundExceptionType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,7 @@ because xbuild doesn't support framework reference assemblies.
PackageNamingPolicy="$(AndroidPackageNamingPolicy)"
BoundExceptionType="$(AndroidBoundExceptionType)"
InstantRunEnabled="$(_InstantRunEnabled)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
>
<Output TaskParameter="BuildId" PropertyName="_XamarinBuildId" />
</GeneratePackageManagerJava>
Expand Down Expand Up @@ -2036,7 +2037,8 @@ because xbuild doesn't support framework reference assemblies.
UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)"
ProjectFullPath="$(MSBuildProjectFullPath)"
IncludeWrapSh="$(AndroidIncludeWrapSh)"
CheckedBuild="$(_AndroidCheckedBuild)">
CheckedBuild="$(_AndroidCheckedBuild)"
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)">
<Output TaskParameter="OutputFiles" ItemName="ApkFiles" />
</BuildApk>
<BuildBaseAppBundle
Expand Down
1 change: 1 addition & 0 deletions src/monodroid/jni/application_dso_stub.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ ApplicationConfig application_config = {
/*.broken_exception_transitions =*/ false,
/*.instant_run_enabled =*/ false,
/*.jni_add_native_method_registration_attribute_present =*/ false,
/*.have_runtime_config_blob =*/ false,
/*.bound_exception_type =*/ 0, // System
/*.package_naming_policy =*/ 0,
/*.environment_variable_count =*/ 0,
Expand Down
13 changes: 13 additions & 0 deletions src/monodroid/jni/embedded-assemblies-zip.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, monodroid_sh
}

dynamic_local_string<SENSIBLE_PATH_MAX> entry_name;
#if defined (NET6)
bool runtime_config_blob_found = false;
#endif // def NET6

// clang-tidy claims we have a leak in the loop:
//
Expand Down Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions src/monodroid/jni/embedded-assemblies.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define INC_MONODROID_EMBEDDED_ASSEMBLIES_H

#include <cstring>
#include <limits>
#include <mono/metadata/object.h>
#include <mono/metadata/assembly.h>

Expand All @@ -12,6 +13,7 @@

#include "strings.hh"
#include "xamarin-app.hh"
#include "cpp-util.hh"

struct TypeMapHeader;

Expand Down Expand Up @@ -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<char*>(runtime_config_blob_mmap.area);

abort_unless (runtime_config_blob_mmap.size < std::numeric_limits<uint32_t>::max (), "Runtime config binary blob size exceeds %u bytes", std::numeric_limits<uint32_t>::max ());
size = static_cast<uint32_t>(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);
Expand Down Expand Up @@ -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
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/monodroid/jni/monodroid-glue-internal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions src/monodroid/jni/monodroid-glue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,20 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_string<PROPE
#endif
}

#if defined (NET6)
void
MonodroidRuntime::cleanup_runtime_config (MonovmRuntimeConfigArguments *args, [[maybe_unused]] void *user_data)
{
if (args == nullptr || args->kind != 1 || args->runtimeconfig.data.data == nullptr) {
return;
}

#if !defined (WINDOWS)
munmap (static_cast<void*>(const_cast<char*>(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)
{
Expand All @@ -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),
Expand Down
4 changes: 4 additions & 0 deletions src/monodroid/jni/shared-constants.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
1 change: 1 addition & 0 deletions src/monodroid/jni/xamarin-app.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit ea815d7

Please sign in to comment.