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