Loading ravenwood/Android.bp +2 −11 Original line number Diff line number Diff line Loading @@ -282,20 +282,12 @@ cc_defaults { 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 { Loading Loading @@ -669,7 +661,6 @@ android_ravenwood_libgroup { jni_libs: [ // Libraries has to be loaded in the following order "libravenwood_initializer", "libravenwood_sysprop", "libravenwood_runtime", "libandroid_runtime", ], Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +6 −8 Original line number Diff line number Diff line Loading @@ -103,7 +103,6 @@ public class RavenwoodRuntimeEnvironmentController { 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"; /** Loading Loading @@ -215,8 +214,12 @@ public class RavenwoodRuntimeEnvironmentController { 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(); Loading @@ -226,11 +229,6 @@ public class RavenwoodRuntimeEnvironmentController { // 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)); Loading ravenwood/runtime-jni/ravenwood_initializer.cpp +184 −5 Original line number Diff line number Diff line Loading @@ -17,13 +17,171 @@ /* * 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) { Loading @@ -42,9 +200,30 @@ static void maybeRedirectLog() { 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; } ravenwood/runtime-jni/ravenwood_sysprop.cppdeleted 100644 → 0 +0 −200 Original line number Diff line number Diff line /* * 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; } Loading
ravenwood/Android.bp +2 −11 Original line number Diff line number Diff line Loading @@ -282,20 +282,12 @@ cc_defaults { 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 { Loading Loading @@ -669,7 +661,6 @@ android_ravenwood_libgroup { jni_libs: [ // Libraries has to be loaded in the following order "libravenwood_initializer", "libravenwood_sysprop", "libravenwood_runtime", "libandroid_runtime", ], Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +6 −8 Original line number Diff line number Diff line Loading @@ -103,7 +103,6 @@ public class RavenwoodRuntimeEnvironmentController { 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"; /** Loading Loading @@ -215,8 +214,12 @@ public class RavenwoodRuntimeEnvironmentController { 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(); Loading @@ -226,11 +229,6 @@ public class RavenwoodRuntimeEnvironmentController { // 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)); Loading
ravenwood/runtime-jni/ravenwood_initializer.cpp +184 −5 Original line number Diff line number Diff line Loading @@ -17,13 +17,171 @@ /* * 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) { Loading @@ -42,9 +200,30 @@ static void maybeRedirectLog() { 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; }
ravenwood/runtime-jni/ravenwood_sysprop.cppdeleted 100644 → 0 +0 −200 Original line number Diff line number Diff line /* * 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; }