Browse Source

Merge pull request #34101 from neikeq/baka

Mono/C#: Several android fixes
Rémi Verschelde 5 years ago
parent
commit
36164168b5

+ 28 - 0
modules/mono/build_scripts/mono_android_config.xml

@@ -0,0 +1,28 @@
+<configuration>
+	<dllmap wordsize="32" dll="i:cygwin1.dll" target="/system/lib/libc.so" />
+	<dllmap wordsize="64" dll="i:cygwin1.dll" target="/system/lib64/libc.so" />
+	<dllmap wordsize="32" dll="libc" target="/system/lib/libc.so" />
+	<dllmap wordsize="64" dll="libc" target="/system/lib64/libc.so" />
+	<dllmap wordsize="32" dll="intl" target="/system/lib/libc.so" />
+	<dllmap wordsize="64" dll="intl" target="/system/lib64/libc.so" />
+	<dllmap wordsize="32" dll="libintl" target="/system/lib/libc.so" />
+	<dllmap wordsize="64" dll="libintl" target="/system/lib64/libc.so" />
+	<dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so" />
+	<dllmap dll="System.Native" target="libmono-native.so" />
+	<dllmap wordsize="32" dll="i:msvcrt" target="/system/lib/libc.so" />
+	<dllmap wordsize="64" dll="i:msvcrt" target="/system/lib64/libc.so" />
+	<dllmap wordsize="32" dll="i:msvcrt.dll" target="/system/lib/libc.so" />
+	<dllmap wordsize="64" dll="i:msvcrt.dll" target="/system/lib64/libc.so" />
+	<dllmap wordsize="32" dll="sqlite" target="/system/lib/libsqlite.so" />
+	<dllmap wordsize="64" dll="sqlite" target="/system/lib64/libsqlite.so" />
+	<dllmap wordsize="32" dll="sqlite3" target="/system/lib/libsqlite.so" />
+	<dllmap wordsize="64" dll="sqlite3" target="/system/lib64/libsqlite.so" />
+	<dllmap wordsize="32" dll="liblog" target="/system/lib/liblog.so" />
+	<dllmap wordsize="64" dll="liblog" target="/system/lib64/liblog.so" />
+	<dllmap dll="i:kernel32.dll">
+		<dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/>
+		<dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/>
+		<dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/>
+		<dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/>
+	</dllmap>
+</configuration>

+ 4 - 3
modules/mono/build_scripts/mono_configure.py

@@ -206,6 +206,8 @@ def configure(env, env_mono):
             env_mono.Append(CPPDEFINES=['_REENTRANT'])
 
             if mono_static:
+                env.Append(LINKFLAGS=['-rdynamic'])
+
                 mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
 
                 if is_apple:
@@ -281,8 +283,6 @@ def configure(env, env_mono):
             libs_output_dir = get_android_out_dir(env) if is_android else '#bin'
             copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext)
 
-        env.Append(LINKFLAGS='-rdynamic')
-
     if not tools_enabled:
         if is_desktop(env['platform']):
             if not mono_root:
@@ -292,7 +292,8 @@ def configure(env, env_mono):
         elif is_android:
             # Compress Android Mono Config
             from . import make_android_mono_config
-            config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config')
+            module_dir = os.getcwd()
+            config_file_path = os.path.join(module_dir, 'build_scripts', 'mono_android_config.xml')
             make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
 
             # Copy the required shared libraries

+ 44 - 3
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

@@ -125,12 +125,20 @@ namespace GodotTools.Export
 
             dependencies[projectDllName] = projectDllSrcPath;
 
+            if (platform == OS.Platforms.Android)
             {
-                string platformBclDir = DeterminePlatformBclDir(platform);
+                string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
+                string monoAndroidAssemblyPath = Path.Combine(godotAndroidExtProfileDir, "Mono.Android.dll");
+
+                if (!File.Exists(monoAndroidAssemblyPath))
+                    throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
 
-                internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, platformBclDir, dependencies);
+                dependencies["Mono.Android"] = monoAndroidAssemblyPath;
             }
 
+            var initialDependencies = dependencies.Duplicate();
+            internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, DeterminePlatformBclDir(platform), dependencies);
+
             string outputDataDir = null;
 
             if (PlatformHasTemplateDir(platform))
@@ -579,6 +587,12 @@ namespace GodotTools.Export
             return null;
         }
 
+        private static string GetBclProfileDir(string profile)
+        {
+            string templatesDir = Internal.FullTemplatesDir;
+            return Path.Combine(templatesDir, "bcl", profile);
+        }
+
         private static string DeterminePlatformBclDir(string platform)
         {
             string templatesDir = Internal.FullTemplatesDir;
@@ -590,18 +604,45 @@ namespace GodotTools.Export
                 platformBclDir = Path.Combine(templatesDir, "bcl", profile);
 
                 if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
+                {
+                    if (PlatformRequiresCustomBcl(platform))
+                        throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
+
                     platformBclDir = null; // Use the one we're running on
+                }
             }
 
             return platformBclDir;
         }
 
+        /// <summary>
+        /// Determines whether the BCL bundled with the Godot editor can be used for the target platform,
+        /// or if it requires a custom BCL that must be distributed with the export templates.
+        /// </summary>
+        private static bool PlatformRequiresCustomBcl(string platform)
+        {
+            if (new[] {OS.Platforms.Android, OS.Platforms.HTML5}.Contains(platform))
+                return true;
+
+            // The 'net_4_x' BCL is not compatible between Windows and the other platforms.
+            // We use the names 'net_4_x_win' and 'net_4_x' to differentiate between the two.
+
+            bool isWinOrUwp = new[]
+            {
+                OS.Platforms.Windows,
+                OS.Platforms.UWP
+            }.Contains(platform);
+
+            return OS.IsWindows ? !isWinOrUwp : isWinOrUwp;
+        }
+
         private static string DeterminePlatformBclProfile(string platform)
         {
             switch (platform)
             {
                 case OS.Platforms.Windows:
                 case OS.Platforms.UWP:
+                    return "net_4_x_win";
                 case OS.Platforms.OSX:
                 case OS.Platforms.X11:
                 case OS.Platforms.Server:
@@ -627,7 +668,7 @@ namespace GodotTools.Export
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
+        private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialDependencies,
             string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies);
     }
 }

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs

@@ -65,7 +65,7 @@ namespace GodotTools.Utils
         private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android));
         private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
 
-        public static bool IsWindows => _isWindows.Value;
+        public static bool IsWindows => _isWindows.Value || IsUWP;
         public static bool IsOSX => _isOSX.Value;
         public static bool IsX11 => _isX11.Value;
         public static bool IsServer => _isServer.Value;

+ 3 - 4
modules/mono/editor/editor_internal_calls.cpp

@@ -219,15 +219,14 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje
 	return err;
 }
 
-uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path,
+uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_dependencies,
 		MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) {
-	String project_dll_name = GDMonoMarshal::mono_string_to_godot(p_project_dll_name);
-	String project_dll_src_path = GDMonoMarshal::mono_string_to_godot(p_project_dll_src_path);
+	Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_dependencies);
 	String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
 	String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
 	Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies);
 
-	return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_bcl_dir, dependencies);
+	return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, dependencies);
 }
 
 MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {

+ 17 - 8
modules/mono/editor/godotsharp_export.cpp

@@ -101,23 +101,32 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
 	return OK;
 }
 
-Error get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
+Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
+		const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
 	MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
 	ERR_FAIL_NULL_V(export_domain, FAILED);
 	_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
 
 	_GDMONO_SCOPE_DOMAIN_(export_domain);
 
-	GDMonoAssembly *scripts_assembly = NULL;
-	bool load_success = GDMono::get_singleton()->load_assembly_from(p_project_dll_name,
-			p_project_dll_src_path, &scripts_assembly, /* refonly: */ true);
-
-	ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + p_project_dll_name + "'.");
-
 	Vector<String> search_dirs;
 	GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
 
-	return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies);
+	for (const Variant *key = p_initial_dependencies.next(); key; key = p_initial_dependencies.next(key)) {
+		String assembly_name = *key;
+		String assembly_path = p_initial_dependencies[*key];
+
+		GDMonoAssembly *assembly = NULL;
+		bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
+
+		ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
+
+		Error err = get_assembly_dependencies(assembly, search_dirs, r_dependencies);
+		if (err != OK)
+			return err;
+	}
+
+	return OK;
 }
 
 } // namespace GodotSharpExport

+ 2 - 3
modules/mono/editor/godotsharp_export.h

@@ -41,9 +41,8 @@ namespace GodotSharpExport {
 
 Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
 
-Error get_exported_assembly_dependencies(const String &p_project_dll_name,
-		const String &p_project_dll_src_path, const String &p_build_config,
-		const String &p_custom_lib_dir, Dictionary &r_dependencies);
+Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
+		const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies);
 
 } // namespace GodotSharpExport
 

+ 13 - 0
modules/mono/glue/Managed/Files/Array.cs

@@ -64,6 +64,11 @@ namespace Godot.Collections
             return safeHandle.DangerousGetHandle();
         }
 
+        public Array Duplicate(bool deep = false)
+        {
+            return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
+        }
+
         public Error Resize(int newSize)
         {
             return godot_icall_Array_Resize(GetPtr(), newSize);
@@ -178,6 +183,9 @@ namespace Godot.Collections
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
 
@@ -250,6 +258,11 @@ namespace Godot.Collections
             return from.objectArray;
         }
 
+        public Array<T> Duplicate(bool deep = false)
+        {
+            return new Array<T>(objectArray.Duplicate(deep));
+        }
+
         public Error Resize(int newSize)
         {
             return objectArray.Resize(newSize);

+ 13 - 0
modules/mono/glue/Managed/Files/Dictionary.cs

@@ -80,6 +80,11 @@ namespace Godot.Collections
             disposed = true;
         }
 
+        public Dictionary Duplicate(bool deep = false)
+        {
+            return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
+        }
+
         // IDictionary
 
         public ICollection Keys
@@ -234,6 +239,9 @@ namespace Godot.Collections
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
 
@@ -313,6 +321,11 @@ namespace Godot.Collections
             return objectDict.GetPtr();
         }
 
+        public Dictionary<TKey, TValue> Duplicate(bool deep = false)
+        {
+            return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep));
+        }
+
         // IDictionary<TKey, TValue>
 
         public TValue this[TKey key]

+ 10 - 0
modules/mono/glue/collections_glue.cpp

@@ -103,6 +103,10 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
 	}
 }
 
+Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) {
+	return memnew(Array(ptr->duplicate(deep)));
+}
+
 int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
 	return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
 }
@@ -224,6 +228,10 @@ MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key)
 	return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
 }
 
+Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) {
+	return memnew(Dictionary(ptr->duplicate(deep)));
+}
+
 MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
 	return ptr->erase(GDMonoMarshal::mono_object_to_variant(key));
 }
@@ -284,6 +292,7 @@ void godot_register_collections_icalls() {
 	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
 	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
 	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
+	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate);
 	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
 	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
 	mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
@@ -304,6 +313,7 @@ void godot_register_collections_icalls() {
 	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
 	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
 	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
+	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", (void *)godot_icall_Dictionary_Duplicate);
 	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
 	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
 	mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);

+ 4 - 0
modules/mono/glue/collections_glue.h

@@ -59,6 +59,8 @@ MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item);
 
 void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
 
+Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep);
+
 int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
 
 void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
@@ -99,6 +101,8 @@ MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, Mo
 
 MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
 
+Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep);
+
 MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
 
 MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);

+ 12 - 1
modules/mono/mono_gd/gd_mono.cpp

@@ -330,7 +330,7 @@ void GDMono::initialize() {
 #endif
 
 #if defined(ANDROID_ENABLED)
-	GDMonoAndroid::register_android_dl_fallback();
+	GDMonoAndroid::initialize();
 #endif
 
 	GDMonoAssembly::initialize();
@@ -363,6 +363,9 @@ void GDMono::initialize() {
 	}
 #endif
 
+	// NOTE: Internal calls must be registered after the Mono runtime initialization.
+	// Otherwise registration fails with the error: 'assertion 'hash != NULL' failed'.
+
 	root_domain = gd_initialize_mono_runtime();
 	ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
 
@@ -376,6 +379,10 @@ void GDMono::initialize() {
 
 	print_verbose("Mono: Runtime initialized");
 
+#if defined(ANDROID_ENABLED)
+	GDMonoAndroid::register_internal_calls();
+#endif
+
 	// mscorlib assembly MUST be present at initialization
 	bool corlib_loaded = _load_corlib_assembly();
 	ERR_FAIL_COND_MSG(!corlib_loaded, "Mono: Failed to load mscorlib assembly.");
@@ -1253,6 +1260,10 @@ GDMono::~GDMono() {
 
 		mono_jit_cleanup(root_domain);
 
+#if defined(ANDROID_ENABLED)
+		GDMonoAndroid::cleanup();
+#endif
+
 		print_verbose("Mono: Finalized");
 
 		runtime_initialized = false;

+ 570 - 31
modules/mono/mono_gd/gd_mono_android.cpp

@@ -34,38 +34,97 @@
 
 #include <dlfcn.h> // dlopen, dlsym
 #include <mono/utils/mono-dl-fallback.h>
+#include <sys/system_properties.h>
+#include <cstddef>
+
+#if __ANDROID_API__ < 24
+#include "thirdparty/misc/ifaddrs-android.h"
+#else
+#include <ifaddrs.h>
+#endif
 
 #include "core/os/os.h"
 #include "core/ustring.h"
+#include "platform/android/java_godot_wrapper.h"
+#include "platform/android/os_android.h"
 #include "platform/android/thread_jandroid.h"
 
 #include "../utils/path_utils.h"
 #include "../utils/string_utils.h"
+#include "gd_mono_cache.h"
+#include "gd_mono_marshal.h"
+
+// Warning: JNI boilerplate ahead... continue at your own risk
 
 namespace GDMonoAndroid {
 
+template <typename T>
+struct ScopedLocalRef {
+	JNIEnv *env;
+	T local_ref;
+
+	_FORCE_INLINE_ T get() const { return local_ref; }
+	_FORCE_INLINE_ operator T() const { return local_ref; }
+	_FORCE_INLINE_ operator jvalue() const { return (jvalue)local_ref; }
+
+	_FORCE_INLINE_ operator bool() const { return local_ref != NULL; }
+
+	_FORCE_INLINE_ bool operator==(std::nullptr_t) const {
+		return local_ref == nullptr;
+	}
+
+	_FORCE_INLINE_ bool operator!=(std::nullptr_t) const {
+		return local_ref != nullptr;
+	}
+
+	ScopedLocalRef(const ScopedLocalRef &) = delete;
+	ScopedLocalRef &operator=(const ScopedLocalRef &) = delete;
+
+	ScopedLocalRef(JNIEnv *p_env, T p_local_ref) :
+			env(p_env), local_ref(p_local_ref) {
+	}
+
+	~ScopedLocalRef() {
+		if (local_ref) {
+			env->DeleteLocalRef(local_ref);
+		}
+	}
+};
+
+bool jni_exception_check(JNIEnv *p_env) {
+	if (p_env->ExceptionCheck()) {
+		// Print the exception to logcat
+		p_env->ExceptionDescribe();
+
+		p_env->ExceptionClear();
+		return true;
+	}
+
+	return false;
+}
+
 String app_native_lib_dir_cache;
 
 String determine_app_native_lib_dir() {
 	JNIEnv *env = ThreadAndroid::get_env();
 
-	jclass activityThreadClass = env->FindClass("android/app/ActivityThread");
+	ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
 	jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
-	jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread);
+	ScopedLocalRef<jobject> activityThread(env, env->CallStaticObjectMethod(activityThreadClass, currentActivityThread));
 	jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
-	jobject ctx = env->CallObjectMethod(activityThread, getApplication);
+	ScopedLocalRef<jobject> ctx(env, env->CallObjectMethod(activityThread, getApplication));
 
 	jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
-	jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo);
+	ScopedLocalRef<jobject> applicationInfo(env, env->CallObjectMethod(ctx, getApplicationInfo));
 	jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
-	jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField);
+	ScopedLocalRef<jstring> nativeLibraryDir(env, (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField));
 
 	String result;
 
-	const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
-	if (nativeLibraryDir_utf8) {
-		result.parse_utf8(nativeLibraryDir_utf8);
-		env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8);
+	const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
+	if (nativeLibraryDirUtf8) {
+		result.parse_utf8(nativeLibraryDirUtf8);
+		env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
 	}
 
 	return result;
@@ -90,7 +149,53 @@ int gd_mono_convert_dl_flags(int flags) {
 	return lflags;
 }
 
+#ifndef GD_MONO_ANDROID_SO_NAME
+#define GD_MONO_ANDROID_SO_NAME "libmonosgen-2.0.so"
+#endif
+
+const char *mono_so_name = GD_MONO_ANDROID_SO_NAME;
+const char *godot_so_name = "libgodot_android.so";
+
+void *mono_dl_handle = NULL;
+void *godot_dl_handle = NULL;
+
+void *try_dlopen(const String &p_so_path, int p_flags) {
+	if (!FileAccess::exists(p_so_path)) {
+		if (OS::get_singleton()->is_stdout_verbose())
+			OS::get_singleton()->print("Cannot find shared library: '%s'\n", p_so_path.utf8().get_data());
+		return NULL;
+	}
+
+	int lflags = gd_mono_convert_dl_flags(p_flags);
+
+	void *handle = dlopen(p_so_path.utf8().get_data(), lflags);
+
+	if (!handle) {
+		if (OS::get_singleton()->is_stdout_verbose())
+			OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", p_so_path.utf8().get_data(), dlerror());
+		return NULL;
+	}
+
+	if (OS::get_singleton()->is_stdout_verbose())
+		OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", p_so_path.utf8().get_data());
+
+	return handle;
+}
+
 void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
+	if (p_name == NULL) {
+		// __Internal
+
+		if (!mono_dl_handle) {
+			String app_native_lib_dir = get_app_native_lib_dir();
+			String so_path = path::join(app_native_lib_dir, mono_so_name);
+
+			mono_dl_handle = try_dlopen(so_path, p_flags);
+		}
+
+		return mono_dl_handle;
+	}
+
 	String name = String::utf8(p_name);
 
 	if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) {
@@ -100,26 +205,7 @@ void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void
 		String so_name = "lib-aot-" + orig_so_name;
 		String so_path = path::join(app_native_lib_dir, so_name);
 
-		if (!FileAccess::exists(so_path)) {
-			if (OS::get_singleton()->is_stdout_verbose())
-				OS::get_singleton()->print("Cannot find shared library: '%s'\n", so_path.utf8().get_data());
-			return NULL;
-		}
-
-		int lflags = gd_mono_convert_dl_flags(p_flags);
-
-		void *handle = dlopen(so_path.utf8().get_data(), lflags);
-
-		if (!handle) {
-			if (OS::get_singleton()->is_stdout_verbose())
-				OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", so_path.utf8().get_data(), dlerror());
-			return NULL;
-		}
-
-		if (OS::get_singleton()->is_stdout_verbose())
-			OS::get_singleton()->print("Successfully loaded AOT shared library: '%s'\n", so_path.utf8().get_data());
-
-		return handle;
+		return try_dlopen(so_path, p_flags);
 	}
 
 	return NULL;
@@ -131,16 +217,469 @@ void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, vo
 	if (sym_addr)
 		return sym_addr;
 
+	if (p_handle == mono_dl_handle && godot_dl_handle) {
+		// Looking up for '__Internal' P/Invoke. We want to search in both the Mono and Godot shared libraries.
+		// This is needed to resolve the monodroid P/Invoke functions that are defined at the bottom of the file.
+		sym_addr = dlsym(godot_dl_handle, p_name);
+
+		if (sym_addr)
+			return sym_addr;
+	}
+
 	if (r_err)
 		*r_err = str_format_new("%s\n", dlerror());
 
 	return NULL;
 }
 
-void register_android_dl_fallback() {
-	mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, NULL, NULL);
+void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
+	dlclose(p_handle);
+
+	// Not sure if this ever happens. Does Mono close the handle for the main module?
+	if (p_handle == mono_dl_handle)
+		mono_dl_handle = NULL;
+
+	return NULL;
+}
+
+int32_t build_version_sdk_int = 0;
+
+int32_t get_build_version_sdk_int() {
+	// The JNI code is the equivalent of:
+	//
+	// android.os.Build.VERSION.SDK_INT
+
+	if (build_version_sdk_int == 0) {
+		JNIEnv *env = ThreadAndroid::get_env();
+
+		jclass versionClass = env->FindClass("android/os/Build$VERSION");
+		ERR_FAIL_NULL_V(versionClass, 0);
+
+		jfieldID sdkIntField = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
+		ERR_FAIL_NULL_V(sdkIntField, 0);
+
+		build_version_sdk_int = (int32_t)env->GetStaticIntField(versionClass, sdkIntField);
+	}
+
+	return build_version_sdk_int;
+}
+
+jobject certStore = NULL; // KeyStore
+
+MonoBoolean _gd_mono_init_cert_store() {
+	// The JNI code is the equivalent of:
+	//
+	// try {
+	// 	certStoreLocal = KeyStore.getInstance("AndroidCAStore");
+	// 	certStoreLocal.load(null);
+	//	certStore = certStoreLocal;
+	//	return true;
+	// } catch (Exception e) {
+	//	return false;
+	// }
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
+
+	jmethodID getInstance = env->GetStaticMethodID(keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
+	jmethodID load = env->GetMethodID(keyStoreClass, "load", "(Ljava/security/KeyStore$LoadStoreParameter;)V");
+
+	ScopedLocalRef<jstring> androidCAStoreString(env, env->NewStringUTF("AndroidCAStore"));
+
+	ScopedLocalRef<jobject> certStoreLocal(env, env->CallStaticObjectMethod(keyStoreClass, getInstance, androidCAStoreString.get()));
+
+	if (jni_exception_check(env))
+		return 0;
+
+	env->CallVoidMethod(certStoreLocal, load, NULL);
+
+	if (jni_exception_check(env))
+		return 0;
+
+	certStore = env->NewGlobalRef(certStoreLocal);
+
+	return 1;
+}
+
+MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
+	// The JNI code is the equivalent of:
+	//
+	// Certificate certificate = certStore.getCertificate(alias);
+	// if (certificate == null)
+	//	return null;
+	// return certificate.getEncoded();
+
+	MonoError mono_error;
+	char *alias_utf8 = mono_string_to_utf8_checked(p_alias, &mono_error);
+
+	if (!mono_error_ok(&mono_error)) {
+		ERR_PRINTS(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
+		mono_error_cleanup(&mono_error);
+		return NULL;
+	}
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
+	mono_free(alias_utf8);
+
+	ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
+	ERR_FAIL_NULL_V(keyStoreClass, NULL);
+	ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
+	ERR_FAIL_NULL_V(certificateClass, NULL);
+
+	jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
+	ERR_FAIL_NULL_V(getCertificate, NULL);
+
+	jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
+	ERR_FAIL_NULL_V(getEncoded, NULL);
+
+	ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
+
+	if (!certificate)
+		return NULL;
+
+	ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
+	jsize encodedLength = env->GetArrayLength(encoded);
+
+	MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
+	uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
+
+	env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
+
+	return encoded_ret;
+}
+
+void initialize() {
+	// We need to set this environment variable to make the monodroid BCL use btls instead of legacy as the default provider
+	OS::get_singleton()->set_environment("XA_TLS_PROVIDER", "btls");
+
+	mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, NULL);
+
+	String app_native_lib_dir = get_app_native_lib_dir();
+	String so_path = path::join(app_native_lib_dir, godot_so_name);
+
+	godot_dl_handle = try_dlopen(so_path, gd_mono_convert_dl_flags(MONO_DL_LAZY));
+}
+
+void register_internal_calls() {
+	mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store);
+	mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup);
+}
+
+void cleanup() {
+	// This is called after shutting down the Mono runtime
+
+	if (mono_dl_handle)
+		gd_mono_android_dlclose(mono_dl_handle, NULL);
+
+	if (godot_dl_handle)
+		gd_mono_android_dlclose(godot_dl_handle, NULL);
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	if (certStore) {
+		env->DeleteGlobalRef(certStore);
+		certStore = NULL;
+	}
 }
 
 } // namespace GDMonoAndroid
 
+using namespace GDMonoAndroid;
+
+// The following are P/Invoke functions required by the monodroid profile of the BCL.
+// These are P/Invoke functions and not internal calls, hence why they use
+// 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
+
+#define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
+
+GD_PINVOKE_EXPORT int32_t _monodroid_get_android_api_level() {
+	return get_build_version_sdk_int();
+}
+
+GD_PINVOKE_EXPORT void monodroid_free(void *ptr) {
+	free(ptr);
+}
+
+GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char **r_value) {
+	char prop_value_str[PROP_VALUE_MAX + 1] = { 0 };
+
+	int len = __system_property_get(p_name, prop_value_str);
+
+	if (r_value) {
+		if (len >= 0) {
+			*r_value = (char *)malloc(len + 1);
+			if (!*r_value)
+				return -1;
+			memcpy(*r_value, prop_value_str, len);
+			(*r_value)[len] = '\0';
+		} else {
+			*r_value = NULL;
+		}
+	}
+
+	return len;
+}
+
+GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char *p_ifname, mono_bool *r_is_up) {
+	// The JNI code is the equivalent of:
+	//
+	// NetworkInterface.getByName(p_ifname).isUp()
+
+	if (!r_is_up || !p_ifname || strlen(p_ifname) == 0)
+		return 0;
+
+	*r_is_up = 0;
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
+	ERR_FAIL_NULL_V(networkInterfaceClass, 0);
+
+	jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
+	ERR_FAIL_NULL_V(getByName, 0);
+
+	jmethodID isUp = env->GetMethodID(networkInterfaceClass, "isUp", "()Z");
+	ERR_FAIL_NULL_V(isUp, 0);
+
+	ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
+	ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
+
+	if (!networkInterface)
+		return 0;
+
+	*r_is_up = (mono_bool)env->CallBooleanMethod(networkInterface, isUp);
+
+	return 1;
+}
+
+GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(const char *p_ifname, mono_bool *r_supports_multicast) {
+	// The JNI code is the equivalent of:
+	//
+	// NetworkInterface.getByName(p_ifname).supportsMulticast()
+
+	if (!r_supports_multicast || !p_ifname || strlen(p_ifname) == 0)
+		return 0;
+
+	*r_supports_multicast = 0;
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
+	ERR_FAIL_NULL_V(networkInterfaceClass, 0);
+
+	jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
+	ERR_FAIL_NULL_V(getByName, 0);
+
+	jmethodID supportsMulticast = env->GetMethodID(networkInterfaceClass, "supportsMulticast", "()Z");
+	ERR_FAIL_NULL_V(supportsMulticast, 0);
+
+	ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
+	ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
+
+	if (!networkInterface)
+		return 0;
+
+	*r_supports_multicast = (mono_bool)env->CallBooleanMethod(networkInterface, supportsMulticast);
+
+	return 1;
+}
+
+static const int dns_servers_len = 8;
+
+static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dns_servers_count) {
+	// The JNI code is the equivalent of:
+	//
+	// ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext()
+	// 		.getSystemService(Context.CONNECTIVITY_SERVICE);
+	// Network activeNerwork = connectivityManager.getActiveNetwork();
+	// LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNerwork);
+	// List<String> dnsServers = linkProperties.getDnsServers().stream()
+	// 		.map(inetAddress -> inetAddress.getHostAddress()).collect(Collectors.toList());
+
+#ifdef DEBUG_ENABLED
+	CRASH_COND(get_build_version_sdk_int() < 23);
+#endif
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
+	jobject activity = godot_java->get_activity();
+
+	ScopedLocalRef<jclass> activityClass(env, env->GetObjectClass(activity));
+	ERR_FAIL_NULL(activityClass);
+
+	jmethodID getApplicationContext = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
+
+	ScopedLocalRef<jobject> applicationContext(env, env->CallObjectMethod(activity, getApplicationContext));
+
+	ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
+	ERR_FAIL_NULL(contextClass);
+
+	jfieldID connectivityServiceField = env->GetStaticFieldID(contextClass, "CONNECTIVITY_SERVICE", "Ljava/lang/String;");
+	ScopedLocalRef<jstring> connectivityServiceString(env, (jstring)env->GetStaticObjectField(contextClass, connectivityServiceField));
+
+	jmethodID getSystemService = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+
+	ScopedLocalRef<jobject> connectivityManager(env, env->CallObjectMethod(applicationContext, getSystemService, connectivityServiceString.get()));
+
+	if (!connectivityManager)
+		return;
+
+	ScopedLocalRef<jclass> connectivityManagerClass(env, env->FindClass("android/net/ConnectivityManager"));
+	ERR_FAIL_NULL(connectivityManagerClass);
+
+	jmethodID getActiveNetwork = env->GetMethodID(connectivityManagerClass, "getActiveNetwork", "()Landroid/net/Network;");
+	ERR_FAIL_NULL(getActiveNetwork);
+
+	ScopedLocalRef<jobject> activeNetwork(env, env->CallObjectMethod(connectivityManager, getActiveNetwork));
+
+	if (!activeNetwork)
+		return;
+
+	jmethodID getLinkProperties = env->GetMethodID(connectivityManagerClass,
+			"getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;");
+	ERR_FAIL_NULL(getLinkProperties);
+
+	ScopedLocalRef<jobject> linkProperties(env, env->CallObjectMethod(connectivityManager, getLinkProperties, activeNetwork.get()));
+
+	if (!linkProperties)
+		return;
+
+	ScopedLocalRef<jclass> linkPropertiesClass(env, env->FindClass("android/net/LinkProperties"));
+	ERR_FAIL_NULL(linkPropertiesClass);
+
+	jmethodID getDnsServers = env->GetMethodID(linkPropertiesClass, "getDnsServers", "()Ljava/util/List;");
+	ERR_FAIL_NULL(getDnsServers);
+
+	ScopedLocalRef<jobject> dnsServers(env, env->CallObjectMethod(linkProperties, getDnsServers));
+
+	if (!dnsServers)
+		return;
+
+	ScopedLocalRef<jclass> listClass(env, env->FindClass("java/util/List"));
+	ERR_FAIL_NULL(listClass);
+
+	jmethodID listSize = env->GetMethodID(listClass, "size", "()I");
+	ERR_FAIL_NULL(listSize);
+
+	int dnsServersCount = env->CallIntMethod(dnsServers, listSize);
+
+	if (dnsServersCount > dns_servers_len)
+		dnsServersCount = dns_servers_len;
+
+	if (dnsServersCount <= 0)
+		return;
+
+	jmethodID listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+	ERR_FAIL_NULL(listGet);
+
+	ScopedLocalRef<jclass> inetAddressClass(env, env->FindClass("java/net/InetAddress"));
+	ERR_FAIL_NULL(inetAddressClass);
+
+	jmethodID getHostAddress = env->GetMethodID(inetAddressClass, "getHostAddress", "()Ljava/lang/String;");
+	ERR_FAIL_NULL(getHostAddress);
+
+	for (int i = 0; i < dnsServersCount; i++) {
+		ScopedLocalRef<jobject> dnsServer(env, env->CallObjectMethod(dnsServers, listGet, (jint)i));
+		if (!dnsServer)
+			continue;
+
+		ScopedLocalRef<jstring> hostAddress(env, (jstring)env->CallObjectMethod(dnsServer, getHostAddress));
+		const char *host_address = env->GetStringUTFChars(hostAddress, 0);
+
+		r_dns_servers[i] = strdup(host_address); // freed by the BCL
+		(*dns_servers_count)++;
+
+		env->ReleaseStringUTFChars(hostAddress, host_address);
+	}
+
+	// jesus...
+}
+
+GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array) {
+	if (!r_dns_servers_array)
+		return -1;
+
+	*r_dns_servers_array = NULL;
+
+	char *dns_servers[dns_servers_len];
+	int dns_servers_count = 0;
+
+	if (_monodroid_get_android_api_level() < 26) {
+		// The 'net.dns*' system properties are no longer available in Android 8.0 (API level 26) and greater:
+		// https://developer.android.com/about/versions/oreo/android-8.0-changes.html#o-pri
+
+		char prop_name[] = "net.dns*";
+
+		for (int i = 0; i < dns_servers_len; i++) {
+			prop_name[7] = (char)(i + 0x31);
+			char *prop_value;
+			int32_t len = monodroid_get_system_property(prop_name, &prop_value);
+
+			if (len > 0) {
+				dns_servers[dns_servers_count] = strndup(prop_value, (size_t)len); // freed by the BCL
+				dns_servers_count++;
+				free(prop_value);
+			}
+		}
+	} else {
+		// Alternative for Oreo and greater
+		interop_get_active_network_dns_servers(dns_servers, &dns_servers_count);
+	}
+
+	if (dns_servers_count > 0) {
+		size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
+		*r_dns_servers_array = malloc(ret_size); // freed by the BCL
+		memcpy(*r_dns_servers_array, dns_servers, ret_size);
+	}
+
+	return dns_servers_count;
+}
+
+GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
+	// The JNI code is the equivalent of:
+	//
+	// TimeZone.getDefault().getID()
+
+	JNIEnv *env = ThreadAndroid::get_env();
+
+	ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
+	ERR_FAIL_NULL_V(timeZoneClass, NULL);
+
+	jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
+	ERR_FAIL_NULL_V(getDefault, NULL);
+
+	jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
+	ERR_FAIL_NULL_V(getID, NULL);
+
+	ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
+
+	if (!defaultTimeZone)
+		return NULL;
+
+	ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
+
+	if (!defaultTimeZoneID)
+		return NULL;
+
+	const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
+
+	char *result = strdup(default_time_zone_id); // freed by the BCL
+
+	env->ReleaseStringUTFChars(defaultTimeZoneID, default_time_zone_id);
+
+	return result;
+}
+
+GD_PINVOKE_EXPORT int32_t _monodroid_getifaddrs(struct ifaddrs **p_ifap) {
+	return getifaddrs(p_ifap);
+}
+
+GD_PINVOKE_EXPORT void _monodroid_freeifaddrs(struct ifaddrs *p_ifap) {
+	freeifaddrs(p_ifap);
+}
+
 #endif

+ 5 - 1
modules/mono/mono_gd/gd_mono_android.h

@@ -39,7 +39,11 @@ namespace GDMonoAndroid {
 
 String get_app_native_lib_dir();
 
-void register_android_dl_fallback();
+void initialize();
+
+void register_internal_calls();
+
+void cleanup();
 
 } // namespace GDMonoAndroid
 

+ 14 - 14
modules/mono/mono_gd/gd_mono_cache.h

@@ -42,20 +42,20 @@ struct CachedData {
 	// corlib classes
 
 	// Let's use the no-namespace format for these too
-	GDMonoClass *class_MonoObject;
-	GDMonoClass *class_bool;
-	GDMonoClass *class_int8_t;
-	GDMonoClass *class_int16_t;
-	GDMonoClass *class_int32_t;
-	GDMonoClass *class_int64_t;
-	GDMonoClass *class_uint8_t;
-	GDMonoClass *class_uint16_t;
-	GDMonoClass *class_uint32_t;
-	GDMonoClass *class_uint64_t;
-	GDMonoClass *class_float;
-	GDMonoClass *class_double;
-	GDMonoClass *class_String;
-	GDMonoClass *class_IntPtr;
+	GDMonoClass *class_MonoObject; // object
+	GDMonoClass *class_bool; // bool
+	GDMonoClass *class_int8_t; // sbyte
+	GDMonoClass *class_int16_t; // short
+	GDMonoClass *class_int32_t; // int
+	GDMonoClass *class_int64_t; // long
+	GDMonoClass *class_uint8_t; // byte
+	GDMonoClass *class_uint16_t; // ushort
+	GDMonoClass *class_uint32_t; // uint
+	GDMonoClass *class_uint64_t; // ulong
+	GDMonoClass *class_float; // float
+	GDMonoClass *class_double; // double
+	GDMonoClass *class_String; // string
+	GDMonoClass *class_IntPtr; // System.IntPtr
 
 	GDMonoClass *class_System_Collections_IEnumerable;
 	GDMonoClass *class_System_Collections_IDictionary;

+ 2 - 0
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -935,6 +935,8 @@ Array mono_array_to_Array(MonoArray *p_array) {
 	return ret;
 }
 
+// TODO: Use memcpy where possible
+
 MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) {
 	PoolIntArray::Read r = p_array.read();
 

+ 19 - 0
thirdparty/misc/ifaddrs-android.h

@@ -32,6 +32,11 @@
 // Fills out a list of ifaddr structs (see below) which contain information
 // about every network interface available on the host.
 // See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function).
+// -- GODOT start --
+#ifdef __cplusplus
+extern "C" {
+#endif
+// -- GODOT end --
 struct ifaddrs {
   struct ifaddrs* ifa_next;
   char* ifa_name;
@@ -40,7 +45,21 @@ struct ifaddrs {
   struct sockaddr* ifa_netmask;
   // Real ifaddrs has broadcast, point to point and data members.
   // We don't need them (yet?).
+  // -- GODOT start --
+  // We never initialize the following members. We only define them to match the ifaddrs struct.
+  union
+  {
+    struct sockaddr *ifu_broadaddr;
+    struct sockaddr *ifu_dstaddr;
+  } ifa_ifu;
+  void *ifa_data;
+  // -- GODOT end --
 };
+// -- GODOT start --
+#ifdef __cplusplus
+}
+#endif
+// -- GODOT end --
 int getifaddrs(struct ifaddrs** result);
 void freeifaddrs(struct ifaddrs* addrs);
 #endif  // TALK_BASE_IFADDRS_ANDROID_H_