[Ravenwood] Merge libinit and libsysprop

Flag: EXEMPT host test change only
Bug: 292141694
Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh
Change-Id: Ie475a13f979ee8aaceb19adf8525e16654f6da20
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 0c2ce8d..66c8d0f 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -282,20 +282,12 @@
     visibility: ["//visibility:private"],
 }
 
-cc_library_host_shared {
-    name: "libravenwood_initializer",
-    defaults: ["ravenwood_jni_defaults"],
-    srcs: [
-        "runtime-jni/ravenwood_initializer.cpp",
-    ],
-}
-
 // We need this as a separate library because we need to overload the
 // sysprop symbols before libbase is loaded into the process
 cc_library_host_shared {
-    name: "libravenwood_sysprop",
+    name: "libravenwood_initializer",
     defaults: ["ravenwood_jni_defaults"],
-    srcs: ["runtime-jni/ravenwood_sysprop.cpp"],
+    srcs: ["runtime-jni/ravenwood_initializer.cpp"],
 }
 
 cc_library_host_shared {
@@ -669,7 +661,6 @@
     jni_libs: [
         // Libraries has to be loaded in the following order
         "libravenwood_initializer",
-        "libravenwood_sysprop",
         "libravenwood_runtime",
         "libandroid_runtime",
     ],
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index b3987f4..172cec3 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -103,7 +103,6 @@
 
     private static final String MAIN_THREAD_NAME = "RavenwoodMain";
     private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
-    private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
     private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
 
     /**
@@ -215,8 +214,12 @@
             Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
         }
 
-        // Some process-wide initialization. (maybe redirect stdout/stderr)
-        RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
+        // Some process-wide initialization:
+        // - maybe redirect stdout/stderr
+        // - override native system property functions
+        var lib = RavenwoodCommonUtils.getJniLibraryPath(LIBRAVENWOOD_INITIALIZER_NAME);
+        System.load(lib);
+        RavenwoodRuntimeNative.reloadNativeLibrary(lib);
 
         // Redirect stdout/stdin to the Log API.
         RuntimeInit.redirectLogStreams();
@@ -226,11 +229,6 @@
         // We haven't initialized liblog yet, so directly write to System.out here.
         RavenwoodCommonUtils.log(TAG, "globalInitInner()");
 
-        // Load libravenwood_sysprop before other libraries that may use SystemProperties.
-        var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME);
-        System.load(libProp);
-        RavenwoodRuntimeNative.reloadNativeLibrary(libProp);
-
         // Make sure libravenwood_runtime is loaded.
         System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
 
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
index 89fb7c3..dbbc345 100644
--- a/ravenwood/runtime-jni/ravenwood_initializer.cpp
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -14,16 +14,174 @@
  * limitations under the License.
  */
 
- /*
-  * This file is compiled into a single SO file, which we load at the very first.
-  * We can do process-wide initialization here.
-  */
+/*
+ * This file is compiled into a single SO file, which we load at the very first.
+ * We can do process-wide initialization here.
+ * Please be aware that all symbols defined in this SO file will be reloaded
+ * as `RTLD_GLOBAL`, so make sure all functions are static except those we EXPLICITLY
+ * want to expose and override globally.
+ */
 
+#include <dlfcn.h>
 #include <fcntl.h>
-#include <unistd.h>
+
+#include <set>
 
 #include "jni_helper.h"
 
+// Implement a rudimentary system properties data store
+
+#define PROP_VALUE_MAX 92
+
+namespace {
+
+struct prop_info {
+    std::string key;
+    mutable std::string value;
+    mutable uint32_t serial;
+
+    prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
+};
+
+struct prop_info_cmp {
+    using is_transparent = void;
+    bool operator()(const prop_info& lhs, const prop_info& rhs) {
+        return lhs.key < rhs.key;
+    }
+    bool operator()(std::string_view lhs, const prop_info& rhs) {
+        return lhs < rhs.key;
+    }
+    bool operator()(const prop_info& lhs, std::string_view rhs) {
+        return lhs.key < rhs;
+    }
+};
+
+} // namespace
+
+static auto& g_properties_lock = *new std::mutex;
+static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
+
+static bool property_set(const char* key, const char* value) {
+    if (key == nullptr || *key == '\0') return false;
+    if (value == nullptr) value = "";
+    bool read_only = !strncmp(key, "ro.", 3);
+    if (!read_only && strlen(value) >= PROP_VALUE_MAX) return false;
+
+    std::lock_guard lock(g_properties_lock);
+    auto [it, success] = g_properties.emplace(key, value);
+    if (read_only) return success;
+    if (!success) {
+        it->value = value;
+        ++it->serial;
+    }
+    return true;
+}
+
+template <typename Func>
+static void property_get(const char* key, Func callback) {
+    std::lock_guard lock(g_properties_lock);
+    auto it = g_properties.find(key);
+    if (it != g_properties.end()) {
+        callback(*it);
+    }
+}
+
+// Redefine the __system_property_XXX functions here so we can perform
+// logging and access checks for all sysprops in native code.
+
+static void check_system_property_access(const char* key, bool write);
+
+extern "C" {
+
+int __system_property_set(const char* key, const char* value) {
+    check_system_property_access(key, true);
+    return property_set(key, value) ? 0 : -1;
+}
+
+int __system_property_get(const char* key, char* value) {
+    check_system_property_access(key, false);
+    *value = '\0';
+    property_get(key, [&](const prop_info& info) {
+        snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
+    });
+    return strlen(value);
+}
+
+const prop_info* __system_property_find(const char* key) {
+    check_system_property_access(key, false);
+    const prop_info* pi = nullptr;
+    property_get(key, [&](const prop_info& info) { pi = &info; });
+    return pi;
+}
+
+void __system_property_read_callback(const prop_info* pi,
+                                     void (*callback)(void*, const char*, const char*, uint32_t),
+                                     void* cookie) {
+    std::lock_guard lock(g_properties_lock);
+    callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
+}
+
+} // extern "C"
+
+// ---- JNI ----
+
+static JavaVM* gVM = nullptr;
+static jclass gRunnerState = nullptr;
+static jmethodID gCheckSystemPropertyAccess;
+
+static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
+    ScopedUtfChars path(env, javaPath);
+    // Force reload ourselves as global
+    dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
+}
+
+// Call back into Java code to check property access
+static void check_system_property_access(const char* key, bool write) {
+    if (gVM != nullptr && gRunnerState != nullptr) {
+        JNIEnv* env;
+        if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
+            ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
+            env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
+                                      env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
+            return;
+        }
+    }
+    // Not on JVM thread, abort
+    LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
+}
+
+static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
+    ScopedUtfChars key(env, javaKey);
+    jstring value = nullptr;
+    property_get(key.c_str(),
+                 [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
+    return value;
+}
+
+static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
+    ScopedUtfChars key(env, javaKey);
+    ScopedUtfChars value(env, javaValue);
+    return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
+    std::lock_guard lock(g_properties_lock);
+
+    if (javaKey == nullptr) {
+        g_properties.clear();
+        return JNI_TRUE;
+    } else {
+        ScopedUtfChars key(env, javaKey);
+        auto it = g_properties.find(key);
+        if (it != g_properties.end()) {
+            g_properties.erase(it);
+            return JNI_TRUE;
+        } else {
+            return JNI_FALSE;
+        }
+    }
+}
+
 static void maybeRedirectLog() {
     auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
     if (ravenwoodLogOut == NULL) {
@@ -42,9 +200,30 @@
     dup2(ttyFd, 2);
 }
 
+static const JNINativeMethod sMethods[] = {
+        {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
+        {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
+        {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
+        {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
+};
+
 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
     ALOGI("%s: JNI_OnLoad", __FILE__);
 
     maybeRedirectLog();
+
+    JNIEnv* env = GetJNIEnvOrDie(vm);
+    gVM = vm;
+
+    // Fetch several references for future use
+    gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
+    gCheckSystemPropertyAccess =
+            GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
+                                   "(Ljava/lang/String;Z)V");
+
+    // Expose raw property methods as JNI methods
+    jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
+    if (res < 0) return -1;
+
     return JNI_VERSION_1_4;
 }
diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
deleted file mode 100644
index a78aa8d..0000000
--- a/ravenwood/runtime-jni/ravenwood_sysprop.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dlfcn.h>
-
-#include <set>
-
-#include "jni_helper.h"
-
-// Implement a rudimentary system properties data store
-
-#define PROP_VALUE_MAX 92
-
-namespace {
-
-struct prop_info {
-    std::string key;
-    mutable std::string value;
-    mutable uint32_t serial;
-
-    prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
-};
-
-struct prop_info_cmp {
-    using is_transparent = void;
-    bool operator()(const prop_info& lhs, const prop_info& rhs) {
-        return lhs.key < rhs.key;
-    }
-    bool operator()(std::string_view lhs, const prop_info& rhs) {
-        return lhs < rhs.key;
-    }
-    bool operator()(const prop_info& lhs, std::string_view rhs) {
-        return lhs.key < rhs;
-    }
-};
-
-} // namespace
-
-static auto& g_properties_lock = *new std::mutex;
-static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
-
-static bool property_set(const char* key, const char* value) {
-    if (key == nullptr || *key == '\0') return false;
-    if (value == nullptr) value = "";
-    bool read_only = !strncmp(key, "ro.", 3);
-    if (!read_only && strlen(value) >= PROP_VALUE_MAX) return false;
-
-    std::lock_guard lock(g_properties_lock);
-    auto [it, success] = g_properties.emplace(key, value);
-    if (read_only) return success;
-    if (!success) {
-        it->value = value;
-        ++it->serial;
-    }
-    return true;
-}
-
-template <typename Func>
-static void property_get(const char* key, Func callback) {
-    std::lock_guard lock(g_properties_lock);
-    auto it = g_properties.find(key);
-    if (it != g_properties.end()) {
-        callback(*it);
-    }
-}
-
-// Redefine the __system_property_XXX functions here so we can perform
-// logging and access checks for all sysprops in native code.
-
-static void check_system_property_access(const char* key, bool write);
-
-extern "C" {
-
-int __system_property_set(const char* key, const char* value) {
-    check_system_property_access(key, true);
-    return property_set(key, value) ? 0 : -1;
-}
-
-int __system_property_get(const char* key, char* value) {
-    check_system_property_access(key, false);
-    *value = '\0';
-    property_get(key, [&](const prop_info& info) {
-        snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
-    });
-    return strlen(value);
-}
-
-const prop_info* __system_property_find(const char* key) {
-    check_system_property_access(key, false);
-    const prop_info* pi = nullptr;
-    property_get(key, [&](const prop_info& info) { pi = &info; });
-    return pi;
-}
-
-void __system_property_read_callback(const prop_info* pi,
-                                     void (*callback)(void*, const char*, const char*, uint32_t),
-                                     void* cookie) {
-    std::lock_guard lock(g_properties_lock);
-    callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
-}
-
-} // extern "C"
-
-// ---- JNI ----
-
-static JavaVM* gVM = nullptr;
-static jclass gRunnerState = nullptr;
-static jmethodID gCheckSystemPropertyAccess;
-
-static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
-    ScopedUtfChars path(env, javaPath);
-    // Force reload ourselves as global
-    dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
-}
-
-// Call back into Java code to check property access
-static void check_system_property_access(const char* key, bool write) {
-    if (gVM != nullptr && gRunnerState != nullptr) {
-        JNIEnv* env;
-        if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
-            ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
-            env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
-                                      env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
-            return;
-        }
-    }
-    // Not on JVM thread, abort
-    LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
-}
-
-static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
-    ScopedUtfChars key(env, javaKey);
-    jstring value = nullptr;
-    property_get(key.c_str(),
-                 [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
-    return value;
-}
-
-static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
-    ScopedUtfChars key(env, javaKey);
-    ScopedUtfChars value(env, javaValue);
-    return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
-    std::lock_guard lock(g_properties_lock);
-
-    if (javaKey == nullptr) {
-        g_properties.clear();
-        return JNI_TRUE;
-    } else {
-        ScopedUtfChars key(env, javaKey);
-        auto it = g_properties.find(key);
-        if (it != g_properties.end()) {
-            g_properties.erase(it);
-            return JNI_TRUE;
-        } else {
-            return JNI_FALSE;
-        }
-    }
-}
-
-static const JNINativeMethod sMethods[] = {
-        {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
-        {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
-        {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
-        {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
-};
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
-    ALOGI("%s: JNI_OnLoad", __FILE__);
-
-    JNIEnv* env = GetJNIEnvOrDie(vm);
-    gVM = vm;
-
-    // Fetch several references for future use
-    gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
-    gCheckSystemPropertyAccess =
-            GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
-                                   "(Ljava/lang/String;Z)V");
-
-    // Expose raw property methods as JNI methods
-    jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
-    if (res < 0) return -1;
-
-    return JNI_VERSION_1_4;
-}