diff --git a/src/tasks/AndroidAppBuilder/Templates/MainActivity.java b/src/tasks/AndroidAppBuilder/Templates/MainActivity.java index 360fda7973b3c5..7c81422f400c82 100644 --- a/src/tasks/AndroidAppBuilder/Templates/MainActivity.java +++ b/src/tasks/AndroidAppBuilder/Templates/MainActivity.java @@ -41,11 +41,13 @@ protected void onCreate(Bundle savedInstanceState) } final Activity ctx = this; + MonoRunner.initializeRuntime(entryPointLibName, ctx); new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { - int retcode = MonoRunner.initialize(entryPointLibName, new String[0], ctx); + int retcode = MonoRunner.executeEntryPoint(entryPointLibName, new String[0]); textView.setText("Mono Runtime returned: " + retcode); + ctx.reportFullyDrawn(); } }, 1000); } diff --git a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java index 695d91068a261b..cb017daafcfdc1 100644 --- a/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java +++ b/src/tasks/AndroidAppBuilder/Templates/MonoRunner.java @@ -74,7 +74,7 @@ public void onCreate(Bundle arguments) { start(); } - public static int initialize(String entryPointLibName, String[] args, Context context) { + public static void initializeRuntime(String entryPointLibName, Context context) { String filesDir = context.getFilesDir().getAbsolutePath(); String cacheDir = context.getCacheDir().getAbsolutePath(); @@ -90,9 +90,24 @@ public static int initialize(String entryPointLibName, String[] args, Context co // unzip libs and test files to filesDir unzipAssets(context, filesDir, "assets.zip"); - Log.i("DOTNET", "MonoRunner initialize,, entryPointLibName=" + entryPointLibName); + // set environment variables + setEnv("HOME", filesDir); + setEnv("TMPDIR", cacheDir); + setEnv("TEST_RESULTS_DIR", testResultsDir); + + Log.i("DOTNET", "MonoRunner initializeRuntime, entryPointLibName=" + entryPointLibName); int localDateTimeOffset = getLocalDateTimeOffset(); - return initRuntime(filesDir, cacheDir, testResultsDir, entryPointLibName, args, localDateTimeOffset); + int rv = initRuntime(filesDir, entryPointLibName, localDateTimeOffset); + if (rv != 0) { + Log.e("DOTNET", "Failed to initialize runtime, return-code=" + rv); + freeNativeResources(); + System.exit(rv); + } + } + + public static int executeEntryPoint(String entryPointLibName, String[] args) { + int rv = execEntryPoint(entryPointLibName, args); + return rv; } @Override @@ -104,7 +119,9 @@ public void onStart() { finish(1, null); return; } - int retcode = initialize(entryPointLibName, argsToForward, getContext()); + + initializeRuntime(entryPointLibName, getContext()); + int retcode = executeEntryPoint(entryPointLibName, argsToForward); Log.i("DOTNET", "MonoRunner finished, return-code=" + retcode); result.putInt("return-code", retcode); @@ -119,6 +136,14 @@ public void onStart() { finish(retcode, result); } + @Override + public void onDestroy() { + Log.i("DOTNET", "MonoRunner onDestroy"); + super.onDestroy(); + // Cleanup native resources + freeNativeResources(); + } + static void unzipAssets(Context context, String toPath, String zipName) { AssetManager assetManager = context.getAssets(); try { @@ -162,7 +187,11 @@ static int getLocalDateTimeOffset() { } } - static native int initRuntime(String libsDir, String cacheDir, String testResultsDir, String entryPointLibName, String[] args, int local_date_time_offset); - static native int setEnv(String key, String value); + + static native int initRuntime(String libsDir, String entryPointLibName, int local_date_time_offset); + + static native int execEntryPoint(String entryPointLibName, String[] args); + + static native void freeNativeResources(); } diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c b/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c index b8fc9b27b59047..4489b0f23bfb83 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c @@ -24,12 +24,21 @@ void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value); int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time); +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time); + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args); + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz); /********* implementation *********/ -static char *bundle_path; -static char *executable; +static const char* g_bundle_path = NULL; +static const char* g_executable_path = NULL; +static unsigned int g_coreclr_domainId = 0; +static void* g_coreclr_handle = NULL; + #define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__) @@ -54,21 +63,11 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) jboolean isCopy = 0; const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy); strncpy (buff, copy_buff, nbuff); + buff[nbuff - 1] = '\0'; // ensure '\0' terminated if (isCopy) (*env)->ReleaseStringUTFChars (env, str, copy_buff); } -void -Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value) -{ - LOG_INFO ("Java_net_dot_MonoRunner_setEnv:"); - const char *key = (*env)->GetStringUTFChars(env, j_key, 0); - const char *val = (*env)->GetStringUTFChars(env, j_value, 0); - setenv (key, val, true); - (*env)->ReleaseStringUTFChars(env, j_key, key); - (*env)->ReleaseStringUTFChars(env, j_value, val); -} - /* * Get the list of trusted assemblies from a specified @dir_path. * The path is searched for .dll files which when found are concatenated @@ -130,11 +129,67 @@ get_tpas_from_path(const char* dir_path, const char** tpas) } static int -mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[], int local_date_time_offset) +bundle_executable_path (const char* executable, const char* bundle_path, const char** executable_path) { - LOG_INFO ("mono_droid_runtime_init called with executable: %s", executable); + size_t executable_path_len = strlen(bundle_path) + strlen(executable) + 1; // +1 for '/' + char* temp_path = (char*)malloc(sizeof(char) * (executable_path_len + 1)); // +1 for '\0' + if (temp_path == NULL) + { + return -1; + } - chdir (bundle_path); + size_t res = snprintf(temp_path, (executable_path_len + 1), "%s/%s", bundle_path, executable); + if (res < 0 || res != executable_path_len) + { + return -1; + } + *executable_path = temp_path; + return executable_path_len; +} + +static void +free_resources () +{ + if (g_bundle_path) + { + free (g_bundle_path); + g_bundle_path = NULL; + } + if (g_executable_path) + { + free (g_executable_path); + g_executable_path = NULL; + } + if (g_coreclr_handle) + { + // Clean up some coreclr resources. This doesn't make coreclr unloadable. + coreclr_shutdown (g_coreclr_handle, g_coreclr_domainId); + g_coreclr_handle = NULL; + } +} + +static int +mono_droid_execute_assembly (const char* executable_path, void* coreclr_handle, unsigned int coreclr_domainId, int managed_argc, const char** managed_argv) +{ + unsigned int rv; + LOG_INFO ("Calling coreclr_execute_assembly"); + coreclr_execute_assembly (coreclr_handle, coreclr_domainId, managed_argc, managed_argv, executable_path, &rv); + LOG_INFO ("Exit code: %u.", rv); + return rv; +} + +static int +mono_droid_runtime_init (const char* executable) +{ + LOG_INFO ("mono_droid_runtime_init (CoreCLR) called with executable: %s", executable); + + if (bundle_executable_path(executable, g_bundle_path, &g_executable_path) < 0) + { + LOG_ERROR("Failed to resolve full path for: %s", executable); + return -1; + } + + chdir (g_bundle_path); // TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES @@ -145,78 +200,99 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed const char* appctx_values[3]; appctx_values[0] = ANDROID_RUNTIME_IDENTIFIER; - appctx_values[1] = bundle_path; - size_t tpas_len = get_tpas_from_path(bundle_path, &appctx_values[2]); + appctx_values[1] = g_bundle_path; + size_t tpas_len = get_tpas_from_path(g_bundle_path, &appctx_values[2]); if (tpas_len < 1) { - LOG_ERROR("Failed to get trusted assemblies from path: %s", bundle_path); + LOG_ERROR("Failed to get trusted assemblies from path: %s", g_bundle_path); return -1; } - size_t executable_path_len = strlen(bundle_path) + strlen(executable) + 2; // +2 for '/' and '\0' - char* executable_path = (char*)malloc(executable_path_len); - size_t res = sprintf (executable_path, "%s/%s", bundle_path, executable); - if (res != executable_path_len - 1) - { - LOG_ERROR("Failed to resolve full path for: %s", executable); - return -1; - } - executable_path[res] = '\0'; - - unsigned int coreclr_domainId = 0; - void *coreclr_handle = NULL; - LOG_INFO ("Calling coreclr_initialize"); int rv = coreclr_initialize ( - executable_path, + g_executable_path, executable, 3, appctx_keys, appctx_values, - &coreclr_handle, - &coreclr_domainId + &g_coreclr_handle, + &g_coreclr_domainId ); LOG_INFO ("coreclr_initialize returned %d", rv); + return rv; +} - LOG_INFO ("Calling coreclr_execute_assembly"); - coreclr_execute_assembly (coreclr_handle, coreclr_domainId, managed_argc, managed_argv, executable_path, &rv); +void +Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value) +{ + LOG_INFO ("Java_net_dot_MonoRunner_setEnv:"); + assert (g_coreclr_handle == NULL); // setenv should be only called before the runtime is initialized - LOG_INFO ("Exit code: %d.", rv); - return rv; + const char *key = (*env)->GetStringUTFChars(env, j_key, 0); + const char *val = (*env)->GetStringUTFChars(env, j_value, 0); + + LOG_INFO ("Setting env: %s=%s", key, val); + setenv (key, val, true); + (*env)->ReleaseStringUTFChars(env, j_key, key); + (*env)->ReleaseStringUTFChars(env, j_value, val); } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time) { - LOG_INFO ("Java_net_dot_MonoRunner_initRuntime:"); + LOG_INFO ("Java_net_dot_MonoRunner_initRuntime (CoreCLR):"); char file_dir[2048]; - char cache_dir[2048]; - char testresults_dir[2048]; char entryPointLibName[2048]; strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir)); - strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir)); - strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir)); strncpy_str (env, entryPointLibName, j_entryPointLibName, sizeof(entryPointLibName)); - bundle_path = file_dir; - executable = entryPointLibName; + size_t file_dir_len = strlen(file_dir); + char* bundle_path_tmp = (char*)malloc(sizeof(char) * (file_dir_len + 1)); // +1 for '\0' + if (bundle_path_tmp == NULL) + { + LOG_ERROR("Failed to allocate memory for bundle_path"); + return -1; + } + strncpy(bundle_path_tmp, file_dir, file_dir_len + 1); + g_bundle_path = bundle_path_tmp; + + return mono_droid_runtime_init (entryPointLibName); +} + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args) +{ + LOG_INFO("Java_net_dot_MonoRunner_execEntryPoint (CoreCLR):"); + + if ((g_bundle_path == NULL) || (g_executable_path == NULL)) + { + LOG_ERROR("Bundle path or executable path not set"); + return -1; + } - setenv ("HOME", bundle_path, true); - setenv ("TMPDIR", cache_dir, true); - setenv ("TEST_RESULTS_DIR", testresults_dir, true); + if ((g_coreclr_handle == NULL) || (g_coreclr_domainId == 0)) + { + LOG_ERROR("CoreCLR not initialized"); + return -1; + } - int args_len = (*env)->GetArrayLength(env, j_args); + int args_len = (*env)->GetArrayLength (env, j_args); int managed_argc = args_len + 1; - char** managed_argv = (char**)malloc(managed_argc * sizeof(char*)); + const char** managed_argv = (const char**)malloc (managed_argc * sizeof(char*)); + if (managed_argv == NULL) + { + LOG_ERROR("Failed to allocate memory for managed_argv"); + return -1; + } - managed_argv[0] = bundle_path; + managed_argv[0] = g_bundle_path; for (int i = 0; i < args_len; ++i) { jstring j_arg = (*env)->GetObjectArrayElement(env, j_args, i); managed_argv[i + 1] = (char*)((*env)->GetStringUTFChars(env, j_arg, NULL)); } - int res = mono_droid_runtime_init (executable, managed_argc, managed_argv, current_local_time); + int rv = mono_droid_execute_assembly (g_executable_path, g_coreclr_handle, g_coreclr_domainId, managed_argc, managed_argv); for (int i = 0; i < args_len; ++i) { @@ -225,6 +301,12 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ } free(managed_argv); - return res; + return rv; } +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz) +{ + LOG_INFO ("Java_net_dot_MonoRunner_freeNativeResources (CoreCLR):"); + free_resources (); +} diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c b/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c index 2ab922255053a1..ef2f7f6a0744ca 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid-librarymode.c @@ -15,7 +15,13 @@ void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value); int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time); +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time); + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args); + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz); /********* imported symbols *********/ void SayHello (); @@ -28,6 +34,7 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) jboolean isCopy = 0; const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy); strncpy (buff, copy_buff, nbuff); + buff[nbuff - 1] = '\0'; // ensure '\0' terminated if (isCopy) (*env)->ReleaseStringUTFChars (env, str, copy_buff); } @@ -51,22 +58,26 @@ Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstrin } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time) { char file_dir[2048]; - char cache_dir[2048]; - char testresults_dir[2048]; strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir)); - strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir)); - strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir)); - setenv ("HOME", file_dir, true); setenv ("DOTNET_LIBRARY_ASSEMBLY_PATH", file_dir, true); - setenv ("TMPDIR", cache_dir, true); - setenv ("TEST_RESULTS_DIR", testresults_dir, true); //setenv ("MONO_LOG_LEVEL", "debug", true); //setenv ("MONO_LOG_MASK", "all", true); + return 0; +} +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args) +{ return invoke_netlibrary_entrypoints (); } + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz) +{ + // nothing to do +} diff --git a/src/tasks/AndroidAppBuilder/Templates/monodroid.c b/src/tasks/AndroidAppBuilder/Templates/monodroid.c index d139109f7b0a90..5ba3952a43d78a 100644 --- a/src/tasks/AndroidAppBuilder/Templates/monodroid.c +++ b/src/tasks/AndroidAppBuilder/Templates/monodroid.c @@ -34,7 +34,13 @@ void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value); int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time); +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time); + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args); + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz); // called from C# void @@ -42,8 +48,9 @@ invoke_external_native_api (void (*callback)(void)); /********* implementation *********/ -static char *bundle_path; -static char *executable; +static const char* g_bundle_path = NULL; +static MonoDomain* g_domain = NULL; +static MonoAssembly* g_assembly = NULL; #define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__) @@ -65,11 +72,12 @@ static char *executable; static MonoAssembly* mono_droid_load_assembly (const char *name, const char *culture) { + assert (g_bundle_path); char filename [1024]; char path [1024]; int res; - LOG_INFO ("assembly_preload_hook: %s %s %s\n", name, culture, bundle_path); + LOG_INFO ("assembly_preload_hook: %s %s %s\n", name, culture, g_bundle_path); int len = strlen (name); int has_extension = len > 3 && name [len - 4] == '.' && (!strcmp ("exe", name + (len - 3)) || !strcmp ("dll", name + (len - 3))); @@ -81,9 +89,9 @@ mono_droid_load_assembly (const char *name, const char *culture) } if (culture && strcmp (culture, "")) - res = snprintf (path, sizeof (path) - 1, "%s/%s/%s", bundle_path, culture, filename); + res = snprintf (path, sizeof (path) - 1, "%s/%s/%s", g_bundle_path, culture, filename); else - res = snprintf (path, sizeof (path) - 1, "%s/%s", bundle_path, filename); + res = snprintf (path, sizeof (path) - 1, "%s/%s", g_bundle_path, filename); assert (res > 0); struct stat buffer; @@ -106,6 +114,7 @@ mono_droid_assembly_preload_hook (MonoAssemblyName *aname, char **assemblies_pat static unsigned char * load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_handle) { + assert (g_bundle_path); *out_handle = NULL; char path [1024]; @@ -115,7 +124,7 @@ load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_han const char *aname = mono_assembly_name_get_name (assembly_name); LOG_INFO ("Looking for aot data for assembly '%s'.", aname); - res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", bundle_path, aname); + res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", g_bundle_path, aname); assert (res > 0); int fd = open (path, O_RDONLY); @@ -222,8 +231,9 @@ cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) } static int -mono_droid_runtime_init (const char* executable, int managed_argc, char* managed_argv[], int local_date_time_offset) +mono_droid_runtime_init (const char* executable, int local_date_time_offset) { + LOG_INFO ("mono_droid_runtime_init (Mono) called with executable: %s", executable); // NOTE: these options can be set via command line args for adb or xharness, see AndroidSampleApp.csproj // uncomment for debug output: @@ -240,7 +250,7 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed #endif bool wait_for_debugger = false; - chdir (bundle_path); + chdir (g_bundle_path); // TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES @@ -251,15 +261,15 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed const char* appctx_values[3]; appctx_values[0] = ANDROID_RUNTIME_IDENTIFIER; - appctx_values[1] = bundle_path; + appctx_values[1] = g_bundle_path; char local_date_time_offset_buffer[32]; snprintf (local_date_time_offset_buffer, sizeof(local_date_time_offset_buffer), "%d", local_date_time_offset); appctx_values[2] = strdup (local_date_time_offset_buffer); char *file_name = RUNTIMECONFIG_BIN_FILE; - int str_len = strlen (bundle_path) + strlen (file_name) + 1; // +1 is for the "/" + int str_len = strlen (g_bundle_path) + strlen (file_name) + 1; // +1 is for the "/" char *file_path = (char *)malloc (sizeof (char) * (str_len +1)); // +1 is for the terminating null character - int num_char = snprintf (file_path, (str_len + 1), "%s/%s", bundle_path, file_name); + int num_char = snprintf (file_path, (str_len + 1), "%s/%s", g_bundle_path, file_name); struct stat buffer; LOG_INFO ("file_path: %s\n", file_path); @@ -274,7 +284,9 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed free (file_path); } - monovm_initialize(3, appctx_keys, appctx_values); + LOG_INFO ("Calling monovm_initialize"); + int rv = monovm_initialize(3, appctx_keys, appctx_values); + LOG_INFO ("monovm_initialize returned %d", rv); mono_debug_init (MONO_DEBUG_FORMAT_MONO); mono_install_assembly_preload_hook (mono_droid_assembly_preload_hook, NULL); @@ -304,20 +316,54 @@ mono_droid_runtime_init (const char* executable, int managed_argc, char* managed mono_jit_set_aot_mode(MONO_AOT_MODE_NORMAL); #endif // FULL_AOT #endif // FORCE_INTERPRETER + + g_domain = mono_jit_init_version ("dotnet.android", "mobile"); + if (g_domain == NULL) { + LOG_ERROR ("mono_jit_init_version failed"); + return -1; + } - MonoDomain *domain = mono_jit_init_version ("dotnet.android", "mobile"); - assert (domain); + g_assembly = mono_droid_load_assembly (executable, NULL); + if (g_assembly == NULL) { + LOG_ERROR ("mono_droid_load_assembly failed"); + return -1; + } + + return rv; +} - MonoAssembly *assembly = mono_droid_load_assembly (executable, NULL); - assert (assembly); +static void +free_resources () +{ + if (g_bundle_path) + { + free (g_bundle_path); + g_bundle_path = NULL; + } + if (g_assembly) + { + mono_assembly_close (g_assembly); + g_assembly = NULL; + } + // Free g_domain + if (g_domain) + { + mono_domain_set (g_domain, false); + mono_domain_finalize (g_domain, 0); + g_domain = NULL; + } +} - LOG_INFO ("Executable: %s", executable); - int res = mono_jit_exec (domain, assembly, managed_argc, managed_argv); - LOG_INFO ("Exit code: %d.", res); +static int +mono_droid_execute_assembly (MonoDomain* domain, MonoAssembly* assembly, int managed_argc, char* managed_argv[]) +{ + LOG_INFO ("Calling mono_jit_exec"); + int rv = mono_jit_exec (domain, assembly, managed_argc, managed_argv); + LOG_INFO ("Exit code: %d.", rv); mono_jit_cleanup (domain); - return res; + return rv; } static void @@ -326,6 +372,7 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) jboolean isCopy = 0; const char *copy_buff = (*env)->GetStringUTFChars (env, str, &isCopy); strncpy (buff, copy_buff, nbuff); + buff[nbuff - 1] = '\0'; // ensure '\0' terminated if (isCopy) (*env)->ReleaseStringUTFChars (env, str, copy_buff); } @@ -333,44 +380,68 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff) void Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstring j_value) { + LOG_INFO ("Java_net_dot_MonoRunner_setEnv:"); + assert (g_domain == NULL); // setenv should be only called before the runtime is initialized + const char *key = (*env)->GetStringUTFChars(env, j_key, 0); const char *val = (*env)->GetStringUTFChars(env, j_value, 0); + + LOG_INFO ("Setting env var: %s=%s", key, val); setenv (key, val, true); (*env)->ReleaseStringUTFChars(env, j_key, key); (*env)->ReleaseStringUTFChars(env, j_value, val); } int -Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_cache_dir, jstring j_testresults_dir, jstring j_entryPointLibName, jobjectArray j_args, long current_local_time) +Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_dir, jstring j_entryPointLibName, long current_local_time) { + LOG_INFO ("Java_net_dot_MonoRunner_initRuntime (Mono):"); char file_dir[2048]; - char cache_dir[2048]; - char testresults_dir[2048]; char entryPointLibName[2048]; strncpy_str (env, file_dir, j_files_dir, sizeof(file_dir)); - strncpy_str (env, cache_dir, j_cache_dir, sizeof(cache_dir)); - strncpy_str (env, testresults_dir, j_testresults_dir, sizeof(testresults_dir)); strncpy_str (env, entryPointLibName, j_entryPointLibName, sizeof(entryPointLibName)); - bundle_path = file_dir; - executable = entryPointLibName; + size_t file_dir_len = strlen(file_dir); + char* bundle_path_tmp = (char*)malloc(sizeof(char) * (file_dir_len + 1)); // +1 for '\0' + if (bundle_path_tmp == NULL) + { + LOG_ERROR("Failed to allocate memory for bundle_path"); + return -1; + } + strncpy(bundle_path_tmp, file_dir, file_dir_len + 1); + g_bundle_path = bundle_path_tmp; + + return mono_droid_runtime_init (entryPointLibName, current_local_time); +} + +int +Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args) +{ + LOG_INFO("Java_net_dot_MonoRunner_execEntryPoint (Mono):"); + + if (g_bundle_path == NULL) + { + LOG_ERROR("Bundle path or executable name not set"); + return -1; + } - setenv ("HOME", bundle_path, true); - setenv ("TMPDIR", cache_dir, true); - setenv ("TEST_RESULTS_DIR", testresults_dir, true); + if (g_domain == NULL || g_assembly == NULL) + { + LOG_ERROR("Mono domain or assembly not initialized"); + return -1; + } int args_len = (*env)->GetArrayLength(env, j_args); int managed_argc = args_len + 1; char** managed_argv = (char**)malloc(managed_argc * sizeof(char*)); - - managed_argv[0] = bundle_path; + managed_argv[0] = g_bundle_path; for (int i = 0; i < args_len; ++i) { jstring j_arg = (*env)->GetObjectArrayElement(env, j_args, i); managed_argv[i + 1] = (char*)((*env)->GetStringUTFChars(env, j_arg, NULL)); } - int res = mono_droid_runtime_init (executable, managed_argc, managed_argv, current_local_time); + int rv = mono_droid_execute_assembly (g_domain, g_assembly, managed_argc, managed_argv); for (int i = 0; i < args_len; ++i) { @@ -379,7 +450,14 @@ Java_net_dot_MonoRunner_initRuntime (JNIEnv* env, jobject thiz, jstring j_files_ } free(managed_argv); - return res; + return rv; +} + +void +Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz) +{ + LOG_INFO ("Java_net_dot_MonoRunner_freeNativeResources (Mono):"); + free_resources (); } // called from C#