xref: /aosp_15_r20/frameworks/base/core/jni/android_os_SystemProperties.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /* //device/libs/android_runtime/android_os_SystemProperties.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #define LOG_TAG "SysPropJNI"
19 
20 #include <utility>
21 #include <optional>
22 
23 #include "android-base/logging.h"
24 #include "android-base/parsebool.h"
25 #include "android-base/parseint.h"
26 #include "android-base/properties.h"
27 #include "utils/misc.h"
28 #include <utils/Log.h>
29 #include "jni.h"
30 #include "core_jni_helpers.h"
31 #include <nativehelper/JNIHelp.h>
32 #include <nativehelper/ScopedPrimitiveArray.h>
33 #include <nativehelper/ScopedUtfChars.h>
34 
35 #if defined(__BIONIC__)
36 # include <sys/system_properties.h>
37 #endif
38 
39 namespace android {
40 namespace {
41 
42 using android::base::ParseBoolResult;
43 
44 template<typename Functor>
ReadProperty(const prop_info * prop,Functor && functor)45 void ReadProperty(const prop_info* prop, Functor&& functor)
46 {
47     auto thunk = [](void* cookie,
48                     const char* /*name*/,
49                     const char* value,
50                     uint32_t /*serial*/) {
51         std::forward<Functor>(*static_cast<Functor*>(cookie))(value);
52     };
53     __system_property_read_callback(prop, thunk, &functor);
54 }
55 
56 template<typename Functor>
ReadProperty(JNIEnv * env,jstring keyJ,Functor && functor)57 void ReadProperty(JNIEnv* env, jstring keyJ, Functor&& functor)
58 {
59     ScopedUtfChars key(env, keyJ);
60     if (!key.c_str()) {
61         return;
62     }
63     const prop_info* prop = __system_property_find(key.c_str());
64     if (!prop) {
65         return;
66     }
67     ReadProperty(prop, std::forward<Functor>(functor));
68 }
69 
SystemProperties_getSS(JNIEnv * env,jclass clazz,jstring keyJ,jstring defJ)70 jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ,
71                                jstring defJ)
72 {
73     jstring ret = defJ;
74     ReadProperty(env, keyJ, [&](const char* value) {
75         if (value[0]) {
76             ret = env->NewStringUTF(value);
77         }
78     });
79     if (ret == nullptr && !env->ExceptionCheck()) {
80       ret = env->NewStringUTF("");  // Legacy behavior
81     }
82     return ret;
83 }
84 
85 template <typename T>
SystemProperties_get_integral(JNIEnv * env,jclass,jstring keyJ,T defJ)86 T SystemProperties_get_integral(JNIEnv *env, jclass, jstring keyJ,
87                                        T defJ)
88 {
89     T ret = defJ;
90     ReadProperty(env, keyJ, [&](const char* value) {
91         android::base::ParseInt<T>(value, &ret);
92     });
93     return ret;
94 }
95 
jbooleanFromParseBoolResult(ParseBoolResult parseResult,jboolean defJ)96 static jboolean jbooleanFromParseBoolResult(ParseBoolResult parseResult, jboolean defJ) {
97     jboolean ret;
98     switch (parseResult) {
99         case ParseBoolResult::kError:
100             ret = defJ;
101             break;
102         case ParseBoolResult::kFalse:
103             ret = JNI_FALSE;
104             break;
105         case ParseBoolResult::kTrue:
106             ret = JNI_TRUE;
107             break;
108     }
109     return ret;
110 }
111 
SystemProperties_get_boolean(JNIEnv * env,jclass,jstring keyJ,jboolean defJ)112 jboolean SystemProperties_get_boolean(JNIEnv *env, jclass, jstring keyJ,
113                                       jboolean defJ)
114 {
115     ParseBoolResult parseResult = ParseBoolResult::kError;
116     ReadProperty(env, keyJ, [&](const char* value) {
117         parseResult = android::base::ParseBool(value);
118     });
119     return jbooleanFromParseBoolResult(parseResult, defJ);
120 }
121 
SystemProperties_find(JNIEnv * env,jclass,jstring keyJ)122 jlong SystemProperties_find(JNIEnv* env, jclass, jstring keyJ)
123 {
124     ScopedUtfChars key(env, keyJ);
125     if (!key.c_str()) {
126         return 0;
127     }
128     const prop_info* prop = __system_property_find(key.c_str());
129     return reinterpret_cast<jlong>(prop);
130 }
131 
SystemProperties_getH(JNIEnv * env,jclass clazz,jlong propJ)132 jstring SystemProperties_getH(JNIEnv* env, jclass clazz, jlong propJ)
133 {
134     jstring ret;
135     auto prop = reinterpret_cast<const prop_info*>(propJ);
136     ReadProperty(prop, [&](const char* value) {
137         ret = env->NewStringUTF(value);
138     });
139     return ret;
140 }
141 
142 template <typename T>
SystemProperties_get_integralH(CRITICAL_JNI_PARAMS_COMMA jlong propJ,T defJ)143 T SystemProperties_get_integralH(CRITICAL_JNI_PARAMS_COMMA jlong propJ, T defJ)
144 {
145     T ret = defJ;
146     auto prop = reinterpret_cast<const prop_info*>(propJ);
147     ReadProperty(prop, [&](const char* value) {
148         android::base::ParseInt<T>(value, &ret);
149     });
150     return ret;
151 }
152 
SystemProperties_get_booleanH(CRITICAL_JNI_PARAMS_COMMA jlong propJ,jboolean defJ)153 jboolean SystemProperties_get_booleanH(CRITICAL_JNI_PARAMS_COMMA jlong propJ, jboolean defJ)
154 {
155     ParseBoolResult parseResult = ParseBoolResult::kError;
156     auto prop = reinterpret_cast<const prop_info*>(propJ);
157     ReadProperty(prop, [&](const char* value) {
158         parseResult = android::base::ParseBool(value);
159     });
160     return jbooleanFromParseBoolResult(parseResult, defJ);
161 }
162 
SystemProperties_set(JNIEnv * env,jobject clazz,jstring keyJ,jstring valJ)163 void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
164                           jstring valJ)
165 {
166     ScopedUtfChars key(env, keyJ);
167     if (!key.c_str()) {
168         return;
169     }
170     std::optional<ScopedUtfChars> value;
171     if (valJ != nullptr) {
172         value.emplace(env, valJ);
173         if (!value->c_str()) {
174             return;
175         }
176     }
177     // Calling SystemProperties.set() with a null value is equivalent to an
178     // empty string, but this is not true for the underlying libc function.
179     const char* value_c_str = value ? value->c_str() : "";
180     // Explicitly clear errno so we can recognize __system_property_set()
181     // failures from failed system calls (as opposed to "init rejected your
182     // request" failures).
183     errno = 0;
184     bool success;
185     success = !__system_property_set(key.c_str(), value_c_str);
186     if (!success) {
187         if (errno != 0) {
188             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
189                                  "failed to set system property \"%s\" to \"%s\": %m",
190                                  key.c_str(), value_c_str);
191         } else {
192             // Must have made init unhappy, which will have logged something,
193             // but there's no API to ask for more detail.
194             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
195                                  "failed to set system property \"%s\" to \"%s\" (check logcat for reason)",
196                                  key.c_str(), value_c_str);
197         }
198     }
199 }
200 
201 JavaVM* sVM = nullptr;
202 jclass sClazz = nullptr;
203 jmethodID sCallChangeCallbacks;
204 
do_report_sysprop_change()205 void do_report_sysprop_change() {
206     //ALOGI("Java SystemProperties: VM=%p, Clazz=%p", sVM, sClazz);
207     if (sVM != nullptr && sClazz != nullptr) {
208         JNIEnv* env;
209         if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) {
210             //ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks);
211             env->CallStaticVoidMethod(sClazz, sCallChangeCallbacks);
212             // There should not be any exceptions. But we must guarantee
213             // there are none on return.
214             if (env->ExceptionCheck()) {
215                 env->ExceptionClear();
216                 LOG(ERROR) << "Exception pending after sysprop_change!";
217             }
218         }
219     }
220 }
221 
SystemProperties_add_change_callback(JNIEnv * env,jobject clazz)222 void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz)
223 {
224     // This is called with the Java lock held.
225     if (sVM == nullptr) {
226         env->GetJavaVM(&sVM);
227     }
228     if (sClazz == nullptr) {
229         sClazz = (jclass) env->NewGlobalRef(clazz);
230         sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V");
231         add_sysprop_change_callback(do_report_sysprop_change, -10000);
232     }
233 }
234 
SystemProperties_report_sysprop_change(JNIEnv,jobject)235 void SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
236 {
237     report_sysprop_change();
238 }
239 
240 }  // namespace
241 
register_android_os_SystemProperties(JNIEnv * env)242 int register_android_os_SystemProperties(JNIEnv *env)
243 {
244     const JNINativeMethod method_table[] = {
245         { "native_get",
246           "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
247           (void*) SystemProperties_getSS },
248         { "native_get_int", "(Ljava/lang/String;I)I",
249           (void*) SystemProperties_get_integral<jint> },
250         { "native_get_long", "(Ljava/lang/String;J)J",
251           (void*) SystemProperties_get_integral<jlong> },
252         { "native_get_boolean", "(Ljava/lang/String;Z)Z",
253           (void*) SystemProperties_get_boolean },
254         { "native_find",
255           "(Ljava/lang/String;)J",
256           (void*) SystemProperties_find },
257         { "native_get",
258           "(J)Ljava/lang/String;",
259           (void*) SystemProperties_getH },
260         { "native_get_int", "(JI)I",
261           (void*) SystemProperties_get_integralH<jint> },
262         { "native_get_long", "(JJ)J",
263           (void*) SystemProperties_get_integralH<jlong> },
264         { "native_get_boolean", "(JZ)Z",
265           (void*) SystemProperties_get_booleanH },
266         { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
267           (void*) SystemProperties_set },
268         { "native_add_change_callback", "()V",
269           (void*) SystemProperties_add_change_callback },
270         { "native_report_sysprop_change", "()V",
271           (void*) SystemProperties_report_sysprop_change },
272     };
273     return RegisterMethodsOrDie(env, "android/os/SystemProperties",
274                                 method_table, NELEM(method_table));
275 }
276 
277 }  // namespace android
278