From 2925015f71eedc0b4cccc234df4a66f7218a9569 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang <16830051+mdh1418@users.noreply.github.com> Date: Tue, 27 Sep 2022 14:04:18 -0400 Subject: [PATCH] [monodroid, java-runtime] Add local date time offset monovm prop (#7331) Context: https://github.com/dotnet/runtime/pull/74459 Context: https://github.com/dotnet/runtime/issues/71004 Context: https://github.com/dotnet/runtime/pull/54845 dotnet/runtime#54845 added support TimeZoneInfo support for Android. This was found to slow down initial use of `DateTimeOffset.Now` by ~277ms (dotnet/runtime#17004). dotnet/runtime#74459 started reducing this startup overhead by adding support for a new `System.TimeZoneInfo.LocalDateTimeOffset` property on `AppContext`: if `System.TimeZoneInfo.LocalDateTimeOffset` is set, it is used as the initial local DateTime Offset value. Update `mono.android.MonoPackageManager.LoadApplication()` and `libmono-android*.so` so that the `System.TimeZoneInfo.LocalDateTimeOffset` AppContext property is set during process startup. This can reduce `DateTimeOffset.Now` startup overhead from ~277ms to ~50ms. Note: `System.TimeZoneInfo.LocalDateTimeOffset` is fastest on API-26+ (Android 8.0+) targets, as it uses [`java.time.ZoneOffset.getTotalSeconds()`][0]. Note: `$(AndroidJavaRuntimeApiLevel)` is updated to API-26 so that `ZoneOffset.getTotalSeconds()` can be used. Care should be taken within `src/java-runtime` to ensure that Android API use is appropriately protected behind runtime version checks. [0]: https://developer.android.com/reference/kotlin/java/time/ZoneOffset#gettotalseconds --- Configuration.props | 2 +- .../java/mono/android/MonoPackageManager.java | 12 ++++++++++++ src/java-runtime/java/mono/android/Runtime.java | 1 + src/monodroid/jni/mono_android_Runtime.h | 4 ++-- src/monodroid/jni/monodroid-glue-internal.hh | 4 ++-- src/monodroid/jni/monodroid-glue.cc | 10 ++++++---- src/monodroid/jni/monovm-properties.cc | 2 ++ src/monodroid/jni/monovm-properties.hh | 11 +++++++++-- 8 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Configuration.props b/Configuration.props index 4f8d499d9d4..0aa03683792 100644 --- a/Configuration.props +++ b/Configuration.props @@ -25,7 +25,7 @@ v4.4 19 - 21 + 26 21 $(AndroidFirstApiLevel) diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index 4a8fac7dbdb..c5f42a87729 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -2,6 +2,9 @@ import java.io.*; import java.lang.String; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Calendar; import java.util.Locale; import java.util.HashSet; import java.util.zip.*; @@ -42,6 +45,14 @@ public static void LoadApplication (Context context, ApplicationInfo runtimePack String dataDir = getNativeLibraryPath (context); ClassLoader loader = context.getClassLoader (); String runtimeDir = getNativeLibraryPath (runtimePackage); + int localDateTimeOffset; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + localDateTimeOffset = OffsetDateTime.now().getOffset().getTotalSeconds(); + } + else { + localDateTimeOffset = (Calendar.getInstance ().get (Calendar.ZONE_OFFSET) + Calendar.getInstance ().get (Calendar.DST_OFFSET)) / 1000; + } // // Should the order change here, src/monodroid/jni/SharedConstants.hh must be updated accordingly @@ -106,6 +117,7 @@ public static void LoadApplication (Context context, ApplicationInfo runtimePack apks, runtimeDir, appDirs, + localDateTimeOffset, loader, MonoPackageManager_Resources.Assemblies, Build.VERSION.SDK_INT, diff --git a/src/java-runtime/java/mono/android/Runtime.java b/src/java-runtime/java/mono/android/Runtime.java index 32b3276012a..01e62b2398c 100644 --- a/src/java-runtime/java/mono/android/Runtime.java +++ b/src/java-runtime/java/mono/android/Runtime.java @@ -20,6 +20,7 @@ public static native void initInternal ( String[] runtimeApks, String runtimeDataDir, String[] appDirs, + int localDateTimeOffset, ClassLoader loader, String[] assemblies, int apiLevel, diff --git a/src/monodroid/jni/mono_android_Runtime.h b/src/monodroid/jni/mono_android_Runtime.h index 321df5be496..a9be6770e9a 100644 --- a/src/monodroid/jni/mono_android_Runtime.h +++ b/src/monodroid/jni/mono_android_Runtime.h @@ -18,10 +18,10 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_init /* * Class: mono_android_Runtime * Method: initInternal - * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/ClassLoader;[Ljava/lang/String;IZZ)V + * 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, jobject, jobjectArray, jint, jboolean, jboolean); + (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jint, jobject, jobjectArray, jint, jboolean, jboolean); /* * Class: mono_android_Runtime diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index c90a69c57fe..122bda2c948 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -174,8 +174,8 @@ namespace xamarin::android::internal public: void Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, jclass nativeClass, jstring methods); void Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, - jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, + jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, + jobject loader, jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, jboolean haveSplitApks); #if !defined (ANDROID) jint Java_mono_android_Runtime_createNewContextWithData (JNIEnv *env, jclass klass, jobjectArray runtimeApksJava, jobjectArray assembliesJava, diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 883f4ac8a27..516c5ff75ae 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -2152,8 +2152,8 @@ MonodroidRuntime::install_logging_handlers () inline void MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, - jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, + jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, + jobject loader, jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, jboolean haveSplitApks) { char *mono_log_mask_raw = nullptr; @@ -2180,7 +2180,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl mono_opt_aot_lazy_assembly_load = application_config.aot_lazy_load ? TRUE : FALSE; { - MonoVMProperties monovm_props { home }; + MonoVMProperties monovm_props { home, localDateTimeOffset }; // NOTE: the `const_cast` breaks the contract made to MonoVMProperties that the arrays it returns won't be // modified, but it's "ok" since Mono doesn't modify them and by using `const char* const*` in MonoVMProperties @@ -2455,6 +2455,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject runtimeApksJava, runtimeNativeLibDir, appDirs, + 0, loader, assembliesJava, apiLevel, @@ -2465,7 +2466,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, - jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, + jstring runtimeNativeLibDir, jobjectArray appDirs, jint localDateTimeOffset, jobject loader, jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, jboolean haveSplitApks) { @@ -2476,6 +2477,7 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, runtimeApksJava, runtimeNativeLibDir, appDirs, + localDateTimeOffset, loader, assembliesJava, apiLevel, diff --git a/src/monodroid/jni/monovm-properties.cc b/src/monodroid/jni/monovm-properties.cc index 4650d46fe1a..b98eac0b6ee 100644 --- a/src/monodroid/jni/monovm-properties.cc +++ b/src/monodroid/jni/monovm-properties.cc @@ -5,9 +5,11 @@ using namespace xamarin::android::internal; MonoVMProperties::property_array MonoVMProperties::_property_keys { RUNTIME_IDENTIFIER_KEY, APP_CONTEXT_BASE_DIRECTORY_KEY, + LOCAL_DATE_TIME_OFFSET_KEY, }; MonoVMProperties::property_array MonoVMProperties::_property_values { SharedConstants::runtime_identifier, nullptr, + nullptr, }; diff --git a/src/monodroid/jni/monovm-properties.hh b/src/monodroid/jni/monovm-properties.hh index f800b1a4261..8a9437c738b 100644 --- a/src/monodroid/jni/monovm-properties.hh +++ b/src/monodroid/jni/monovm-properties.hh @@ -10,7 +10,7 @@ namespace xamarin::android::internal { class MonoVMProperties final { - constexpr static size_t PROPERTY_COUNT = 2; + constexpr static size_t PROPERTY_COUNT = 3; constexpr static char RUNTIME_IDENTIFIER_KEY[] = "RUNTIME_IDENTIFIER"; constexpr static size_t RUNTIME_IDENTIFIER_INDEX = 0; @@ -18,15 +18,22 @@ namespace xamarin::android::internal constexpr static char APP_CONTEXT_BASE_DIRECTORY_KEY[] = "APP_CONTEXT_BASE_DIRECTORY"; constexpr static size_t APP_CONTEXT_BASE_DIRECTORY_INDEX = 1; + constexpr static char LOCAL_DATE_TIME_OFFSET_KEY[] = "System.TimeZoneInfo.LocalDateTimeOffset"; + constexpr static size_t LOCAL_DATE_TIME_OFFSET_INDEX = 2; + using property_array = const char*[PROPERTY_COUNT]; public: - explicit MonoVMProperties (jstring_wrapper& filesDir) + explicit MonoVMProperties (jstring_wrapper& filesDir, jint localDateTimeOffset) { static_assert (PROPERTY_COUNT == N_PROPERTY_KEYS); static_assert (PROPERTY_COUNT == N_PROPERTY_VALUES); _property_values[APP_CONTEXT_BASE_DIRECTORY_INDEX] = strdup (filesDir.get_cstr ()); + + static_local_string<32> localDateTimeOffsetBuffer; + localDateTimeOffsetBuffer.append (localDateTimeOffset); + _property_values[LOCAL_DATE_TIME_OFFSET_INDEX] = strdup (localDateTimeOffsetBuffer.get ()); } constexpr int property_count () const