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 #else
38 struct prop_info;
39 #endif
40 
41 namespace android {
42 namespace {
43 
44 using android::base::ParseBoolResult;
45 
46 template<typename Functor>
ReadProperty(const prop_info * prop,Functor && functor)47 void ReadProperty(const prop_info* prop, Functor&& functor)
48 {
49 #if defined(__BIONIC__)
50     auto thunk = [](void* cookie,
51                     const char* /*name*/,
52                     const char* value,
53                     uint32_t /*serial*/) {
54         std::forward<Functor>(*static_cast<Functor*>(cookie))(value);
55     };
56     __system_property_read_callback(prop, thunk, &functor);
57 #else
58     LOG(FATAL) << "fast property access supported only on device";
59 #endif
60 }
61 
62 template<typename Functor>
ReadProperty(JNIEnv * env,jstring keyJ,Functor && functor)63 void ReadProperty(JNIEnv* env, jstring keyJ, Functor&& functor)
64 {
65     ScopedUtfChars key(env, keyJ);
66     if (!key.c_str()) {
67         return;
68     }
69 #if defined(__BIONIC__)
70     const prop_info* prop = __system_property_find(key.c_str());
71     if (!prop) {
72         return;
73     }
74     ReadProperty(prop, std::forward<Functor>(functor));
75 #else
76     std::forward<Functor>(functor)(
77         android::base::GetProperty(key.c_str(), "").c_str());
78 #endif
79 }
80 
SystemProperties_getSS(JNIEnv * env,jclass clazz,jstring keyJ,jstring defJ)81 jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ,
82                                jstring defJ)
83 {
84     jstring ret = defJ;
85     ReadProperty(env, keyJ, [&](const char* value) {
86         if (value[0]) {
87             ret = env->NewStringUTF(value);
88         }
89     });
90     if (ret == nullptr && !env->ExceptionCheck()) {
91       ret = env->NewStringUTF("");  // Legacy behavior
92     }
93     return ret;
94 }
95 
96 template <typename T>
SystemProperties_get_integral(JNIEnv * env,jclass,jstring keyJ,T defJ)97 T SystemProperties_get_integral(JNIEnv *env, jclass, jstring keyJ,
98                                        T defJ)
99 {
100     T ret = defJ;
101     ReadProperty(env, keyJ, [&](const char* value) {
102         android::base::ParseInt<T>(value, &ret);
103     });
104     return ret;
105 }
106 
jbooleanFromParseBoolResult(ParseBoolResult parseResult,jboolean defJ)107 static jboolean jbooleanFromParseBoolResult(ParseBoolResult parseResult, jboolean defJ) {
108     jboolean ret;
109     switch (parseResult) {
110         case ParseBoolResult::kError:
111             ret = defJ;
112             break;
113         case ParseBoolResult::kFalse:
114             ret = JNI_FALSE;
115             break;
116         case ParseBoolResult::kTrue:
117             ret = JNI_TRUE;
118             break;
119     }
120     return ret;
121 }
122 
SystemProperties_get_boolean(JNIEnv * env,jclass,jstring keyJ,jboolean defJ)123 jboolean SystemProperties_get_boolean(JNIEnv *env, jclass, jstring keyJ,
124                                       jboolean defJ)
125 {
126     ParseBoolResult parseResult = ParseBoolResult::kError;
127     ReadProperty(env, keyJ, [&](const char* value) {
128         parseResult = android::base::ParseBool(value);
129     });
130     return jbooleanFromParseBoolResult(parseResult, defJ);
131 }
132 
SystemProperties_find(JNIEnv * env,jclass,jstring keyJ)133 jlong SystemProperties_find(JNIEnv* env, jclass, jstring keyJ)
134 {
135 #if defined(__BIONIC__)
136     ScopedUtfChars key(env, keyJ);
137     if (!key.c_str()) {
138         return 0;
139     }
140     const prop_info* prop = __system_property_find(key.c_str());
141     return reinterpret_cast<jlong>(prop);
142 #else
143     LOG(FATAL) << "fast property access supported only on device";
144     __builtin_unreachable();  // Silence warning
145 #endif
146 }
147 
SystemProperties_getH(JNIEnv * env,jclass clazz,jlong propJ)148 jstring SystemProperties_getH(JNIEnv* env, jclass clazz, jlong propJ)
149 {
150     jstring ret;
151     auto prop = reinterpret_cast<const prop_info*>(propJ);
152     ReadProperty(prop, [&](const char* value) {
153         ret = env->NewStringUTF(value);
154     });
155     return ret;
156 }
157 
158 template <typename T>
SystemProperties_get_integralH(CRITICAL_JNI_PARAMS_COMMA jlong propJ,T defJ)159 T SystemProperties_get_integralH(CRITICAL_JNI_PARAMS_COMMA jlong propJ, T defJ)
160 {
161     T ret = defJ;
162     auto prop = reinterpret_cast<const prop_info*>(propJ);
163     ReadProperty(prop, [&](const char* value) {
164         android::base::ParseInt<T>(value, &ret);
165     });
166     return ret;
167 }
168 
SystemProperties_get_booleanH(CRITICAL_JNI_PARAMS_COMMA jlong propJ,jboolean defJ)169 jboolean SystemProperties_get_booleanH(CRITICAL_JNI_PARAMS_COMMA jlong propJ, jboolean defJ)
170 {
171     ParseBoolResult parseResult = ParseBoolResult::kError;
172     auto prop = reinterpret_cast<const prop_info*>(propJ);
173     ReadProperty(prop, [&](const char* value) {
174         parseResult = android::base::ParseBool(value);
175     });
176     return jbooleanFromParseBoolResult(parseResult, defJ);
177 }
178 
SystemProperties_set(JNIEnv * env,jobject clazz,jstring keyJ,jstring valJ)179 void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
180                           jstring valJ)
181 {
182     ScopedUtfChars key(env, keyJ);
183     if (!key.c_str()) {
184         return;
185     }
186     std::optional<ScopedUtfChars> value;
187     if (valJ != nullptr) {
188         value.emplace(env, valJ);
189         if (!value->c_str()) {
190             return;
191         }
192     }
193     // Calling SystemProperties.set() with a null value is equivalent to an
194     // empty string, but this is not true for the underlying libc function.
195     const char* value_c_str = value ? value->c_str() : "";
196     // Explicitly clear errno so we can recognize __system_property_set()
197     // failures from failed system calls (as opposed to "init rejected your
198     // request" failures).
199     errno = 0;
200     bool success;
201 #if defined(__BIONIC__)
202     success = !__system_property_set(key.c_str(), value_c_str);
203 #else
204     success = android::base::SetProperty(key.c_str(), value_c_str);
205 #endif
206     if (!success) {
207         if (errno != 0) {
208             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
209                                  "failed to set system property \"%s\" to \"%s\": %m",
210                                  key.c_str(), value_c_str);
211         } else {
212             // Must have made init unhappy, which will have logged something,
213             // but there's no API to ask for more detail.
214             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
215                                  "failed to set system property \"%s\" to \"%s\" (check logcat for reason)",
216                                  key.c_str(), value_c_str);
217         }
218     }
219 }
220 
221 JavaVM* sVM = nullptr;
222 jclass sClazz = nullptr;
223 jmethodID sCallChangeCallbacks;
224 
do_report_sysprop_change()225 void do_report_sysprop_change() {
226     //ALOGI("Java SystemProperties: VM=%p, Clazz=%p", sVM, sClazz);
227     if (sVM != nullptr && sClazz != nullptr) {
228         JNIEnv* env;
229         if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) {
230             //ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks);
231             env->CallStaticVoidMethod(sClazz, sCallChangeCallbacks);
232             // There should not be any exceptions. But we must guarantee
233             // there are none on return.
234             if (env->ExceptionCheck()) {
235                 env->ExceptionClear();
236                 LOG(ERROR) << "Exception pending after sysprop_change!";
237             }
238         }
239     }
240 }
241 
SystemProperties_add_change_callback(JNIEnv * env,jobject clazz)242 void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz)
243 {
244     // This is called with the Java lock held.
245     if (sVM == nullptr) {
246         env->GetJavaVM(&sVM);
247     }
248     if (sClazz == nullptr) {
249         sClazz = (jclass) env->NewGlobalRef(clazz);
250         sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V");
251         add_sysprop_change_callback(do_report_sysprop_change, -10000);
252     }
253 }
254 
SystemProperties_report_sysprop_change(JNIEnv,jobject)255 void SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
256 {
257     report_sysprop_change();
258 }
259 
260 }  // namespace
261 
register_android_os_SystemProperties(JNIEnv * env)262 int register_android_os_SystemProperties(JNIEnv *env)
263 {
264     const JNINativeMethod method_table[] = {
265         { "native_get",
266           "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
267           (void*) SystemProperties_getSS },
268         { "native_get_int", "(Ljava/lang/String;I)I",
269           (void*) SystemProperties_get_integral<jint> },
270         { "native_get_long", "(Ljava/lang/String;J)J",
271           (void*) SystemProperties_get_integral<jlong> },
272         { "native_get_boolean", "(Ljava/lang/String;Z)Z",
273           (void*) SystemProperties_get_boolean },
274         { "native_find",
275           "(Ljava/lang/String;)J",
276           (void*) SystemProperties_find },
277         { "native_get",
278           "(J)Ljava/lang/String;",
279           (void*) SystemProperties_getH },
280         { "native_get_int", "(JI)I",
281           (void*) SystemProperties_get_integralH<jint> },
282         { "native_get_long", "(JJ)J",
283           (void*) SystemProperties_get_integralH<jlong> },
284         { "native_get_boolean", "(JZ)Z",
285           (void*) SystemProperties_get_booleanH },
286         { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
287           (void*) SystemProperties_set },
288         { "native_add_change_callback", "()V",
289           (void*) SystemProperties_add_change_callback },
290         { "native_report_sysprop_change", "()V",
291           (void*) SystemProperties_report_sysprop_change },
292     };
293     return RegisterMethodsOrDie(env, "android/os/SystemProperties",
294                                 method_table, NELEM(method_table));
295 }
296 
297 }  // namespace android
298