xref: /aosp_15_r20/frameworks/base/ravenwood/runtime-jni/ravenwood_initializer.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * This file is compiled into a single SO file, which we load at the very first.
19  * We can do process-wide initialization here.
20  * Please be aware that all symbols defined in this SO file will be reloaded
21  * as `RTLD_GLOBAL`, so make sure all functions are static except those we EXPLICITLY
22  * want to expose and override globally.
23  */
24 
25 #include <dlfcn.h>
26 #include <fcntl.h>
27 
28 #include <set>
29 
30 #include "jni_helper.h"
31 
32 // Implement a rudimentary system properties data store
33 
34 #define PROP_VALUE_MAX 92
35 
36 namespace {
37 
38 struct prop_info {
39     std::string key;
40     mutable std::string value;
41     mutable uint32_t serial;
42 
prop_info__anon2e97a4bd0111::prop_info43     prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
44 };
45 
46 struct prop_info_cmp {
47     using is_transparent = void;
operator ()__anon2e97a4bd0111::prop_info_cmp48     bool operator()(const prop_info& lhs, const prop_info& rhs) {
49         return lhs.key < rhs.key;
50     }
operator ()__anon2e97a4bd0111::prop_info_cmp51     bool operator()(std::string_view lhs, const prop_info& rhs) {
52         return lhs < rhs.key;
53     }
operator ()__anon2e97a4bd0111::prop_info_cmp54     bool operator()(const prop_info& lhs, std::string_view rhs) {
55         return lhs.key < rhs;
56     }
57 };
58 
59 } // namespace
60 
61 static auto& g_properties_lock = *new std::mutex;
62 static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
63 
property_set(const char * key,const char * value)64 static bool property_set(const char* key, const char* value) {
65     if (key == nullptr || *key == '\0') return false;
66     if (value == nullptr) value = "";
67     bool read_only = !strncmp(key, "ro.", 3);
68     if (!read_only && strlen(value) >= PROP_VALUE_MAX) return false;
69 
70     std::lock_guard lock(g_properties_lock);
71     auto [it, success] = g_properties.emplace(key, value);
72     if (read_only) return success;
73     if (!success) {
74         it->value = value;
75         ++it->serial;
76     }
77     return true;
78 }
79 
80 template <typename Func>
property_get(const char * key,Func callback)81 static void property_get(const char* key, Func callback) {
82     std::lock_guard lock(g_properties_lock);
83     auto it = g_properties.find(key);
84     if (it != g_properties.end()) {
85         callback(*it);
86     }
87 }
88 
89 // Redefine the __system_property_XXX functions here so we can perform
90 // logging and access checks for all sysprops in native code.
91 
92 static void check_system_property_access(const char* key, bool write);
93 
94 extern "C" {
95 
__system_property_set(const char * key,const char * value)96 int __system_property_set(const char* key, const char* value) {
97     check_system_property_access(key, true);
98     return property_set(key, value) ? 0 : -1;
99 }
100 
__system_property_get(const char * key,char * value)101 int __system_property_get(const char* key, char* value) {
102     check_system_property_access(key, false);
103     *value = '\0';
104     property_get(key, [&](const prop_info& info) {
105         snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
106     });
107     return strlen(value);
108 }
109 
__system_property_find(const char * key)110 const prop_info* __system_property_find(const char* key) {
111     check_system_property_access(key, false);
112     const prop_info* pi = nullptr;
113     property_get(key, [&](const prop_info& info) { pi = &info; });
114     return pi;
115 }
116 
__system_property_read_callback(const prop_info * pi,void (* callback)(void *,const char *,const char *,uint32_t),void * cookie)117 void __system_property_read_callback(const prop_info* pi,
118                                      void (*callback)(void*, const char*, const char*, uint32_t),
119                                      void* cookie) {
120     std::lock_guard lock(g_properties_lock);
121     callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
122 }
123 
124 } // extern "C"
125 
126 // ---- JNI ----
127 
128 static JavaVM* gVM = nullptr;
129 static jclass gRunnerState = nullptr;
130 static jmethodID gCheckSystemPropertyAccess;
131 
reloadNativeLibrary(JNIEnv * env,jclass,jstring javaPath)132 static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
133     ScopedUtfChars path(env, javaPath);
134     // Force reload ourselves as global
135     dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
136 }
137 
138 // Call back into Java code to check property access
check_system_property_access(const char * key,bool write)139 static void check_system_property_access(const char* key, bool write) {
140     if (gVM != nullptr && gRunnerState != nullptr) {
141         JNIEnv* env;
142         if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
143             ALOGV("%s access to system property '%s'", write ? "Write" : "Read", key);
144             env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
145                                       env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
146             return;
147         }
148     }
149     // Not on JVM thread, abort
150     LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
151 }
152 
getSystemProperty(JNIEnv * env,jclass,jstring javaKey)153 static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
154     ScopedUtfChars key(env, javaKey);
155     jstring value = nullptr;
156     property_get(key.c_str(),
157                  [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
158     return value;
159 }
160 
setSystemProperty(JNIEnv * env,jclass,jstring javaKey,jstring javaValue)161 static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
162     ScopedUtfChars key(env, javaKey);
163     ScopedUtfChars value(env, javaValue);
164     return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
165 }
166 
removeSystemProperty(JNIEnv * env,jclass,jstring javaKey)167 static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
168     std::lock_guard lock(g_properties_lock);
169 
170     if (javaKey == nullptr) {
171         g_properties.clear();
172         return JNI_TRUE;
173     } else {
174         ScopedUtfChars key(env, javaKey);
175         auto it = g_properties.find(key);
176         if (it != g_properties.end()) {
177             g_properties.erase(it);
178             return JNI_TRUE;
179         } else {
180             return JNI_FALSE;
181         }
182     }
183 }
184 
maybeRedirectLog()185 static void maybeRedirectLog() {
186     auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
187     if (ravenwoodLogOut == NULL) {
188         return;
189     }
190     ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
191 
192     // Redirect stdin / stdout to /dev/tty.
193     int ttyFd = open(ravenwoodLogOut, O_WRONLY | O_APPEND);
194     if (ttyFd == -1) {
195         ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
196                 strerror(errno));
197         return;
198     }
199     dup2(ttyFd, 1);
200     dup2(ttyFd, 2);
201 }
202 
203 static const JNINativeMethod sMethods[] = {
204         {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
205         {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
206         {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
207         {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
208 };
209 
JNI_OnLoad(JavaVM * vm,void *)210 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
211     ALOGV("%s: JNI_OnLoad", __FILE__);
212 
213     maybeRedirectLog();
214 
215     JNIEnv* env = GetJNIEnvOrDie(vm);
216     gVM = vm;
217 
218     // Fetch several references for future use
219     gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
220     gCheckSystemPropertyAccess =
221             GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
222                                    "(Ljava/lang/String;Z)V");
223 
224     // Expose raw property methods as JNI methods
225     jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
226     if (res < 0) return -1;
227 
228     return JNI_VERSION_1_4;
229 }
230