From 1d177277a1e335ba0e1c11d9d771fef496d82241 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