From 0b6ad325873d6834024f5bcdd21d5c97db2e5f71 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Feb 2025 10:18:13 -0600 Subject: [PATCH 1/6] [Mono.Android] pass in `Context` at startup Calling `Android.App.Application.Context` currently reads the `mono.MonoPackageManager.Context` field, which is set on startup by a `ContentProvider`. `mono.MonoPackageManager` does not exist in NativeAOT (or CoreCLR), so let's approach this differently. Each `ContentProvider` can pass in the context to `JNIEnvInit` on startup, then the first call to `Android.App.Application.Context` does not have to read a field any longer as we already have the handle in managed code. --- src/Mono.Android/Android.App/Application.cs | 16 ++++++++-------- .../Android.Runtime/JNIEnvInit.cs | 2 ++ .../java/mono/android/MonoPackageManager.java | 8 ++------ .../java/mono/android/Runtime.java | 3 ++- .../mono/monodroid/mono_android_Runtime.h | 2 +- .../mono/monodroid/monodroid-glue-internal.hh | 7 ++++--- src/native/mono/monodroid/monodroid-glue.cc | 19 +++++++++++-------- 7 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/Mono.Android/Android.App/Application.cs b/src/Mono.Android/Android.App/Application.cs index e7fca8d2190..08aefea67dc 100644 --- a/src/Mono.Android/Android.App/Application.cs +++ b/src/Mono.Android/Android.App/Application.cs @@ -14,17 +14,17 @@ public static Context Context { if (_context != null) return _context; - IntPtr klass = JNIEnv.FindClass ("mono/MonoPackageManager"); - try { - IntPtr field = JNIEnv.GetStaticFieldID (klass, "Context", "Landroid/content/Context;"); - IntPtr lref = JNIEnv.GetStaticObjectField (klass, field); - return _context = Java.Lang.Object.GetObject (lref, JniHandleOwnership.TransferLocalRef)!; - } finally { - JNIEnv.DeleteGlobalRef (klass); - } + var lref = ContextHandle; + if (lref == IntPtr.Zero) + throw new InvalidOperationException ("Application.ContextHandle is not set!"); + + return _context = Java.Lang.Object.GetObject (lref, JniHandleOwnership.TransferLocalRef)!; } + internal set => _context = value; } + internal static IntPtr ContextHandle { get; set; } + static SyncContext? _sync; public static SynchronizationContext SynchronizationContext { get { diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 2499b693cf2..07e62c557d6 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -32,6 +32,7 @@ internal struct JnienvInitializeArgs { public bool jniRemappingInUse; public bool marshalMethodsEnabled; public IntPtr grefGCUserPeerable; + public IntPtr applicationContext; } #pragma warning restore 0649 @@ -92,6 +93,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) Logger.SetLogCategories ((LogCategories)args->logCategories); + Android.App.Application.ContextHandle = args->applicationContext; gref_gc_threshold = args->grefGcThreshold; jniRemappingInUse = args->jniRemappingInUse; diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index 0955d55c8db..09f609a925a 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -24,8 +24,6 @@ public class MonoPackageManager { static Object lock = new Object (); static boolean initialized; - static android.content.Context Context; - public static void LoadApplication (Context context) { synchronized (lock) { @@ -40,9 +38,6 @@ public static void LoadApplication (Context context) apks = new String[] { runtimePackage.sourceDir }; } - if (context instanceof android.app.Application) { - Context = context; - } if (!initialized) { android.content.IntentFilter timezoneChangedFilter = new android.content.IntentFilter ( android.content.Intent.ACTION_TIMEZONE_CHANGED @@ -126,7 +121,8 @@ public static void LoadApplication (Context context) loader, MonoPackageManager_Resources.Assemblies, isEmulator (), - haveSplitApks + haveSplitApks, + context ); net.dot.android.ApplicationRegistration.registerApplications (); diff --git a/src/java-runtime/java/mono/android/Runtime.java b/src/java-runtime/java/mono/android/Runtime.java index 8ad03b1a2c5..1af1799146d 100644 --- a/src/java-runtime/java/mono/android/Runtime.java +++ b/src/java-runtime/java/mono/android/Runtime.java @@ -25,7 +25,8 @@ public static native void initInternal ( ClassLoader loader, String[] assemblies, boolean isEmulator, - boolean haveSplitApks + boolean haveSplitApks, + android.content.Context context ); public static native void register (String managedType, java.lang.Class nativeClass, String methods); public static native void notifyTimeZoneChanged (); diff --git a/src/native/mono/monodroid/mono_android_Runtime.h b/src/native/mono/monodroid/mono_android_Runtime.h index 663a02a9b4f..f3024db2847 100644 --- a/src/native/mono/monodroid/mono_android_Runtime.h +++ b/src/native/mono/monodroid/mono_android_Runtime.h @@ -21,7 +21,7 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_init * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;ILjava/lang/ClassLoader;[Ljava/lang/String;IZZ)V */ JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal - (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jint, jobject, jobjectArray, jboolean, jboolean); + (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jint, jobject, jobjectArray, jboolean, jboolean, jobject); /* * Class: mono_android_Runtime diff --git a/src/native/mono/monodroid/monodroid-glue-internal.hh b/src/native/mono/monodroid/monodroid-glue-internal.hh index 36c9206e36d..f2bac1335fc 100644 --- a/src/native/mono/monodroid/monodroid-glue-internal.hh +++ b/src/native/mono/monodroid/monodroid-glue-internal.hh @@ -101,6 +101,7 @@ namespace xamarin::android::internal bool jniRemappingInUse; bool marshalMethodsEnabled; jobject grefGCUserPeerable; + jobject applicationContext; }; using jnienv_initialize_fn = void (*) (JnienvInitializeArgs*); @@ -119,7 +120,7 @@ namespace xamarin::android::internal static void Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader, jobjectArray assembliesJava, jboolean isEmulator, - jboolean haveSplitApks) noexcept; + jboolean haveSplitApks, jobject context) noexcept; static jint Java_JNI_OnLoad (JavaVM *vm, void *reserved) noexcept; @@ -185,7 +186,7 @@ namespace xamarin::android::internal static void set_debug_options () noexcept; static void parse_gdb_options () noexcept; static void mono_runtime_init (JNIEnv *env, dynamic_local_string& runtime_args) noexcept; - static void init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader) noexcept; + static void init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader, jobject context) noexcept; static void set_environment_variable_for_directory (const char *name, jstring_wrapper &value, bool createDirectory, mode_t mode) noexcept; static void set_environment_variable_for_directory (const char *name, jstring_wrapper &value) noexcept @@ -205,7 +206,7 @@ namespace xamarin::android::internal static MonoDomain* create_and_initialize_domain (JNIEnv* env, jclass runtimeClass, jstring_array_wrapper &runtimeApks, jstring_array_wrapper &assemblies, jobjectArray assembliesBytes, jstring_array_wrapper &assembliesPaths, jobject loader, bool is_root_domain, bool force_preload_assemblies, - bool have_split_apks) noexcept; + bool have_split_apks, jobject context) noexcept; static void gather_bundled_assemblies (jstring_array_wrapper &runtimeApks, size_t *out_user_assemblies_count, bool have_split_apks) noexcept; static bool should_register_file (const char *filename); diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index ebb72718c57..35ccb16e5d2 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -816,7 +816,7 @@ MonodroidRuntime::monodroid_debugger_unhandled_exception (MonoException *ex) } void -MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader) noexcept +MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader, jobject context) noexcept { constexpr std::string_view icall_typemap_java_to_managed { "Java.Interop.TypeManager::monodroid_typemap_java_to_managed" }; constexpr std::string_view icall_typemap_managed_to_java { "Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java" }; @@ -849,6 +849,7 @@ MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobjec init.jniAddNativeMethodRegistrationAttributePresent = application_config.jni_add_native_method_registration_attribute_present ? 1 : 0; init.jniRemappingInUse = application_config.jni_remapping_replacement_type_count > 0 || application_config.jni_remapping_replacement_method_index_entry_count > 0; init.marshalMethodsEnabled = application_config.marshal_methods_enabled; + init.applicationContext = context; java_System = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "java_lang_System", true); java_System_identityHashCode = env->GetStaticMethodID (java_System, "identityHashCode", "(Ljava/lang/Object;)I"); @@ -1253,7 +1254,7 @@ MonoDomain* MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass, jstring_array_wrapper &runtimeApks, jstring_array_wrapper &assemblies, [[maybe_unused]] jobjectArray assembliesBytes, [[maybe_unused]] jstring_array_wrapper &assembliesPaths, jobject loader, bool is_root_domain, - bool force_preload_assemblies, bool have_split_apks) noexcept + bool force_preload_assemblies, bool have_split_apks, jobject context) noexcept { MonoDomain* domain = create_domain (env, runtimeApks, is_root_domain, have_split_apks); // Asserting this on desktop apparently breaks a Designer test @@ -1275,7 +1276,7 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass bool preload = (AndroidSystem::is_assembly_preload_enabled () || (is_running_on_desktop && force_preload_assemblies)); load_assemblies (default_alc, preload, assemblies); - init_android_runtime (env, runtimeClass, loader); + init_android_runtime (env, runtimeClass, loader, context); osBridge.add_monodroid_domain (domain); return domain; @@ -1386,7 +1387,7 @@ inline void MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader, jobjectArray assembliesJava, jboolean isEmulator, - jboolean haveSplitApks) noexcept + jboolean haveSplitApks, jobject context) noexcept { char *mono_log_mask_raw = nullptr; char *mono_log_level_raw = nullptr; @@ -1522,7 +1523,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl jstring_array_wrapper assemblies (env, assembliesJava); jstring_array_wrapper assembliesPaths (env); /* the first assembly is used to initialize the AppDomain name */ - create_and_initialize_domain (env, klass, runtimeApks, assemblies, nullptr, assembliesPaths, loader, /*is_root_domain:*/ true, /*force_preload_assemblies:*/ false, haveSplitApks); + create_and_initialize_domain (env, klass, runtimeApks, assemblies, nullptr, assembliesPaths, loader, /*is_root_domain:*/ true, /*force_preload_assemblies:*/ false, haveSplitApks, context); // Install our dummy exception handler on Desktop if constexpr (is_running_on_desktop) { @@ -1581,7 +1582,8 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject loader, assembliesJava, /* isEmulator */ JNI_FALSE, - /* haveSplitApks */ JNI_FALSE + /* haveSplitApks */ JNI_FALSE, + /* context */ nullptr ); } @@ -1589,7 +1591,7 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader, jobjectArray assembliesJava, jboolean isEmulator, - jboolean haveSplitApks) + jboolean haveSplitApks, jobject context) { MonodroidRuntime::Java_mono_android_Runtime_initInternal ( env, @@ -1602,7 +1604,8 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, loader, assembliesJava, isEmulator, - application_config.ignore_split_configs ? false : haveSplitApks + application_config.ignore_split_configs ? false : haveSplitApks, + context ); } From fecf176a0fb58f3cc9ff99209d56c45d3c91fdc2 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Feb 2025 11:20:08 -0600 Subject: [PATCH 2/6] Update JNIEnvInit.cs --- src/Mono.Android/Android.Runtime/JNIEnvInit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 07e62c557d6..eed7659d6a4 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -93,7 +93,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) Logger.SetLogCategories ((LogCategories)args->logCategories); - Android.App.Application.ContextHandle = args->applicationContext; gref_gc_threshold = args->grefGcThreshold; jniRemappingInUse = args->jniRemappingInUse; @@ -120,6 +119,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) } SetSynchronizationContext (); + Android.App.Application.ContextHandle = args->applicationContext; } // NOTE: prevents Android.App.Application static ctor from running From 13e43ce50a74634602804a1195ac9c124c4c83a4 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Feb 2025 11:22:40 -0600 Subject: [PATCH 3/6] Move to static field in JNIEnvInit --- src/Mono.Android/Android.App/Application.cs | 6 ++---- src/Mono.Android/Android.Runtime/JNIEnvInit.cs | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Mono.Android/Android.App/Application.cs b/src/Mono.Android/Android.App/Application.cs index 08aefea67dc..a3a42cb654f 100644 --- a/src/Mono.Android/Android.App/Application.cs +++ b/src/Mono.Android/Android.App/Application.cs @@ -14,17 +14,15 @@ public static Context Context { if (_context != null) return _context; - var lref = ContextHandle; + var lref = JNIEnvInit.applicationContext; if (lref == IntPtr.Zero) - throw new InvalidOperationException ("Application.ContextHandle is not set!"); + throw new InvalidOperationException ("JNIEnvInit.applicationContext is not set!"); return _context = Java.Lang.Object.GetObject (lref, JniHandleOwnership.TransferLocalRef)!; } internal set => _context = value; } - internal static IntPtr ContextHandle { get; set; } - static SyncContext? _sync; public static SynchronizationContext SynchronizationContext { get { diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index eed7659d6a4..6352da97aca 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -46,6 +46,7 @@ internal struct JnienvInitializeArgs { internal static IntPtr grefIGCUserPeer_class; internal static IntPtr grefGCUserPeerable_class; internal static IntPtr java_class_loader; + internal static IntPtr applicationContext; internal static JniRuntime? androidRuntime; @@ -98,6 +99,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) jniRemappingInUse = args->jniRemappingInUse; MarshalMethodsEnabled = args->marshalMethodsEnabled; java_class_loader = args->grefLoader; + applicationContext = args->applicationContext; BoundExceptionType = (BoundExceptionType)args->ioExceptionType; androidRuntime = new AndroidRuntime (args->env, args->javaVm, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0); @@ -119,7 +121,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) } SetSynchronizationContext (); - Android.App.Application.ContextHandle = args->applicationContext; } // NOTE: prevents Android.App.Application static ctor from running From 4800555d09dd1d518a2b9d6034eff3d557b7464a Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Feb 2025 11:42:24 -0600 Subject: [PATCH 4/6] Update Application.cs --- src/Mono.Android/Android.App/Application.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mono.Android/Android.App/Application.cs b/src/Mono.Android/Android.App/Application.cs index a3a42cb654f..27062c65d5d 100644 --- a/src/Mono.Android/Android.App/Application.cs +++ b/src/Mono.Android/Android.App/Application.cs @@ -20,7 +20,6 @@ public static Context Context { return _context = Java.Lang.Object.GetObject (lref, JniHandleOwnership.TransferLocalRef)!; } - internal set => _context = value; } static SyncContext? _sync; From b1a98e1ee6f1abdc48639e535c4e67461901c85e Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Feb 2025 12:48:44 -0600 Subject: [PATCH 5/6] Update MonoPackageManager.java --- src/java-runtime/java/mono/android/MonoPackageManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index 09f609a925a..d53d3852982 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -24,6 +24,8 @@ public class MonoPackageManager { static Object lock = new Object (); static boolean initialized; + static android.content.Context Context; + public static void LoadApplication (Context context) { synchronized (lock) { @@ -38,6 +40,9 @@ public static void LoadApplication (Context context) apks = new String[] { runtimePackage.sourceDir }; } + if (context instanceof android.app.Application) { + Context = context; + } if (!initialized) { android.content.IntentFilter timezoneChangedFilter = new android.content.IntentFilter ( android.content.Intent.ACTION_TIMEZONE_CHANGED @@ -122,7 +127,7 @@ public static void LoadApplication (Context context) MonoPackageManager_Resources.Assemblies, isEmulator (), haveSplitApks, - context + Context ); net.dot.android.ApplicationRegistration.registerApplications (); From d2a9fe6811419dd636403832ae7d846d3af19a07 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 10 Feb 2025 16:28:13 -0600 Subject: [PATCH 6/6] WIP --- src/Mono.Android/Android.App/Application.cs | 6 +++--- src/Mono.Android/Android.Runtime/JNIEnvInit.cs | 2 +- src/java-runtime/java/mono/android/MonoPackageManager.java | 7 +------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Mono.Android/Android.App/Application.cs b/src/Mono.Android/Android.App/Application.cs index 27062c65d5d..ae4a144452d 100644 --- a/src/Mono.Android/Android.App/Application.cs +++ b/src/Mono.Android/Android.App/Application.cs @@ -14,11 +14,11 @@ public static Context Context { if (_context != null) return _context; - var lref = JNIEnvInit.applicationContext; - if (lref == IntPtr.Zero) + var gref = JNIEnvInit.applicationContext; + if (gref == IntPtr.Zero) throw new InvalidOperationException ("JNIEnvInit.applicationContext is not set!"); - return _context = Java.Lang.Object.GetObject (lref, JniHandleOwnership.TransferLocalRef)!; + return _context = Java.Lang.Object.GetObject (gref, JniHandleOwnership.TransferGlobalRef)!; } } diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 6352da97aca..f70c9ecbbcc 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -99,7 +99,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) jniRemappingInUse = args->jniRemappingInUse; MarshalMethodsEnabled = args->marshalMethodsEnabled; java_class_loader = args->grefLoader; - applicationContext = args->applicationContext; BoundExceptionType = (BoundExceptionType)args->ioExceptionType; androidRuntime = new AndroidRuntime (args->env, args->javaVm, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0); @@ -120,6 +119,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) } } + applicationContext = JNIEnv.NewGlobalRef (args->applicationContext); SetSynchronizationContext (); } diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index d53d3852982..09f609a925a 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -24,8 +24,6 @@ public class MonoPackageManager { static Object lock = new Object (); static boolean initialized; - static android.content.Context Context; - public static void LoadApplication (Context context) { synchronized (lock) { @@ -40,9 +38,6 @@ public static void LoadApplication (Context context) apks = new String[] { runtimePackage.sourceDir }; } - if (context instanceof android.app.Application) { - Context = context; - } if (!initialized) { android.content.IntentFilter timezoneChangedFilter = new android.content.IntentFilter ( android.content.Intent.ACTION_TIMEZONE_CHANGED @@ -127,7 +122,7 @@ public static void LoadApplication (Context context) MonoPackageManager_Resources.Assemblies, isEmulator (), haveSplitApks, - Context + context ); net.dot.android.ApplicationRegistration.registerApplications ();