Merge changes from topic "property-handle" am: 54543bb423 am: 1077797c14
am: d5e5232632
Change-Id: I23a5fe95b271a50d60546231a9facf59440bb2dd
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 9d14d9d..e7a271c 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -26,6 +26,9 @@
import com.android.internal.annotations.GuardedBy;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
import libcore.util.HexEncoding;
import java.nio.charset.StandardCharsets;
@@ -93,18 +96,43 @@
}
}
+ // The one-argument version of native_get used to be a regular native function. Nowadays,
+ // we use the two-argument form of native_get all the time, but we can't just delete the
+ // one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation
+ // indicates. Let's just live with having a Java function with a very unusual name.
@UnsupportedAppUsage
- private static native String native_get(String key);
+ private static String native_get(String key) {
+ return native_get(key, "");
+ }
+
+ @FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native String native_get(String key, String def);
+ @FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native int native_get_int(String key, int def);
+ @FastNative
@UnsupportedAppUsage
private static native long native_get_long(String key, long def);
+ @FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native boolean native_get_boolean(String key, boolean def);
+
+ @FastNative
+ private static native long native_find(String name);
+ @FastNative
+ private static native String native_get(long handle);
+ @CriticalNative
+ private static native int native_get_int(long handle, int def);
+ @CriticalNative
+ private static native long native_get_long(long handle, long def);
+ @CriticalNative
+ private static native boolean native_get_boolean(long handle, boolean def);
+
+ // _NOT_ FastNative: native_set performs IPC and can block
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native void native_set(String key, String def);
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native void native_add_change_callback();
private static native void native_report_sysprop_change();
@@ -246,25 +274,27 @@
@SuppressWarnings("unused") // Called from native code.
private static void callChangeCallbacks() {
+ ArrayList<Runnable> callbacks = null;
synchronized (sChangeCallbacks) {
//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
if (sChangeCallbacks.size() == 0) {
return;
}
- ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
- final long token = Binder.clearCallingIdentity();
- try {
- for (int i = 0; i < callbacks.size(); i++) {
- try {
- callbacks.get(i).run();
- } catch (Throwable t) {
- Log.wtf(TAG, "Exception in SystemProperties change callback", t);
- // Ignore and try to go on.
- }
+ callbacks = new ArrayList<Runnable>(sChangeCallbacks);
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ callbacks.get(i).run();
+ } catch (Throwable t) {
+ // Ignore and try to go on. Don't use wtf here: that
+ // will cause the process to exit on some builds and break tests.
+ Log.e(TAG, "Exception in SystemProperties change callback", t);
}
- } finally {
- Binder.restoreCallingIdentity(token);
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -301,4 +331,60 @@
@UnsupportedAppUsage
private SystemProperties() {
}
+
+ /**
+ * Look up a property location by name.
+ * @name name of the property
+ * @return property handle or {@code null} if property isn't set
+ * @hide
+ */
+ @Nullable public static Handle find(@NonNull String name) {
+ long nativeHandle = native_find(name);
+ if (nativeHandle == 0) {
+ return null;
+ }
+ return new Handle(nativeHandle);
+ }
+
+ /**
+ * Handle to a pre-located property. Looking up a property handle in advance allows
+ * for optimal repeated lookup of a single property.
+ * @hide
+ */
+ public static final class Handle {
+
+ private final long mNativeHandle;
+
+ /**
+ * @return Value of the property
+ */
+ @NonNull public String get() {
+ return native_get(mNativeHandle);
+ }
+ /**
+ * @param def default value
+ * @return value or {@code def} on parse error
+ */
+ public int getInt(int def) {
+ return native_get_int(mNativeHandle, def);
+ }
+ /**
+ * @param def default value
+ * @return value or {@code def} on parse error
+ */
+ public long getLong(long def) {
+ return native_get_long(mNativeHandle, def);
+ }
+ /**
+ * @param def default value
+ * @return value or {@code def} on parse error
+ */
+ public boolean getBoolean(boolean def) {
+ return native_get_boolean(mNativeHandle, def);
+ }
+
+ private Handle(long nativeHandle) {
+ mNativeHandle = nativeHandle;
+ }
+ }
}
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index cfdf26a..7f8bec6 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -17,7 +17,12 @@
#define LOG_TAG "SysPropJNI"
+#include <utility>
+#include <optional>
+
#include "android-base/logging.h"
+#include "android-base/parsebool.h"
+#include "android-base/parseint.h"
#include "android-base/properties.h"
#include "utils/misc.h"
#include <utils/Log.h>
@@ -27,86 +32,171 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
-namespace android
-{
+#if defined(__BIONIC__)
+# include <sys/system_properties.h>
+#else
+struct prop_info;
+#endif
+namespace android {
namespace {
-template <typename T, typename Handler>
-T ConvertKeyAndForward(JNIEnv *env, jstring keyJ, T defJ, Handler handler) {
- std::string key;
- {
- // Scope the String access. If the handler can throw an exception,
- // releasing the string characters late would trigger an abort.
- ScopedUtfChars key_utf(env, keyJ);
- if (key_utf.c_str() == nullptr) {
- return defJ;
- }
- key = key_utf.c_str(); // This will make a copy, but we can't avoid
- // with the existing interface in
- // android::base.
- }
- return handler(key, defJ);
+using android::base::ParseBoolResult;
+
+template<typename Functor>
+void ReadProperty(const prop_info* prop, Functor&& functor)
+{
+#if defined(__BIONIC__)
+ auto thunk = [](void* cookie,
+ const char* /*name*/,
+ const char* value,
+ uint32_t /*serial*/) {
+ std::forward<Functor>(*static_cast<Functor*>(cookie))(value);
+ };
+ __system_property_read_callback(prop, thunk, &functor);
+#else
+ LOG(FATAL) << "fast property access supported only on device";
+#endif
}
-jstring SystemProperties_getSS(JNIEnv *env, jclass clazz, jstring keyJ,
+template<typename Functor>
+void ReadProperty(JNIEnv* env, jstring keyJ, Functor&& functor)
+{
+ ScopedUtfChars key(env, keyJ);
+ if (!key.c_str()) {
+ return;
+ }
+#if defined(__BIONIC__)
+ const prop_info* prop = __system_property_find(key.c_str());
+ if (!prop) {
+ return;
+ }
+ ReadProperty(prop, std::forward<Functor>(functor));
+#else
+ std::forward<Functor>(functor)(
+ android::base::GetProperty(key.c_str(), "").c_str());
+#endif
+}
+
+jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ,
jstring defJ)
{
- // Using ConvertKeyAndForward is sub-optimal for copying the key string,
- // but improves reuse and reasoning over code.
- auto handler = [&](const std::string& key, jstring defJ) {
- std::string prop_val = android::base::GetProperty(key, "");
- if (!prop_val.empty()) {
- return env->NewStringUTF(prop_val.c_str());
- };
- if (defJ != nullptr) {
- return defJ;
+ jstring ret = defJ;
+ ReadProperty(env, keyJ, [&](const char* value) {
+ if (value[0]) {
+ ret = env->NewStringUTF(value);
}
- // This function is specified to never return null (or have an
- // exception pending).
- return env->NewStringUTF("");
- };
- return ConvertKeyAndForward(env, keyJ, defJ, handler);
-}
-
-jstring SystemProperties_getS(JNIEnv *env, jclass clazz, jstring keyJ)
-{
- return SystemProperties_getSS(env, clazz, keyJ, nullptr);
+ });
+ if (ret == nullptr && !env->ExceptionCheck()) {
+ ret = env->NewStringUTF(""); // Legacy behavior
+ }
+ return ret;
}
template <typename T>
T SystemProperties_get_integral(JNIEnv *env, jclass, jstring keyJ,
T defJ)
{
- auto handler = [](const std::string& key, T defV) {
- return android::base::GetIntProperty<T>(key, defV);
- };
- return ConvertKeyAndForward(env, keyJ, defJ, handler);
+ T ret = defJ;
+ ReadProperty(env, keyJ, [&](const char* value) {
+ android::base::ParseInt<T>(value, &ret);
+ });
+ return ret;
+}
+
+static jboolean jbooleanFromParseBoolResult(ParseBoolResult parseResult, jboolean defJ) {
+ jboolean ret;
+ switch (parseResult) {
+ case ParseBoolResult::kError:
+ ret = defJ;
+ break;
+ case ParseBoolResult::kFalse:
+ ret = JNI_FALSE;
+ break;
+ case ParseBoolResult::kTrue:
+ ret = JNI_TRUE;
+ break;
+ }
+ return ret;
}
jboolean SystemProperties_get_boolean(JNIEnv *env, jclass, jstring keyJ,
jboolean defJ)
{
- auto handler = [](const std::string& key, jboolean defV) -> jboolean {
- bool result = android::base::GetBoolProperty(key, defV);
- return result ? JNI_TRUE : JNI_FALSE;
- };
- return ConvertKeyAndForward(env, keyJ, defJ, handler);
+ ParseBoolResult parseResult;
+ ReadProperty(env, keyJ, [&](const char* value) {
+ parseResult = android::base::ParseBool(value);
+ });
+ return jbooleanFromParseBoolResult(parseResult, defJ);
+}
+
+jlong SystemProperties_find(JNIEnv* env, jclass, jstring keyJ)
+{
+#if defined(__BIONIC__)
+ ScopedUtfChars key(env, keyJ);
+ if (!key.c_str()) {
+ return 0;
+ }
+ const prop_info* prop = __system_property_find(key.c_str());
+ return reinterpret_cast<jlong>(prop);
+#else
+ LOG(FATAL) << "fast property access supported only on device";
+ __builtin_unreachable(); // Silence warning
+#endif
+}
+
+jstring SystemProperties_getH(JNIEnv* env, jclass clazz, jlong propJ)
+{
+ jstring ret;
+ auto prop = reinterpret_cast<const prop_info*>(propJ);
+ ReadProperty(prop, [&](const char* value) {
+ ret = env->NewStringUTF(value);
+ });
+ return ret;
+}
+
+template <typename T>
+T SystemProperties_get_integralH(CRITICAL_JNI_PARAMS_COMMA jlong propJ, T defJ)
+{
+ T ret = defJ;
+ auto prop = reinterpret_cast<const prop_info*>(propJ);
+ ReadProperty(prop, [&](const char* value) {
+ android::base::ParseInt<T>(value, &ret);
+ });
+ return ret;
+}
+
+jboolean SystemProperties_get_booleanH(CRITICAL_JNI_PARAMS_COMMA jlong propJ, jboolean defJ)
+{
+ ParseBoolResult parseResult;
+ auto prop = reinterpret_cast<const prop_info*>(propJ);
+ ReadProperty(prop, [&](const char* value) {
+ parseResult = android::base::ParseBool(value);
+ });
+ return jbooleanFromParseBoolResult(parseResult, defJ);
}
void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
jstring valJ)
{
- auto handler = [&](const std::string& key, bool) {
- std::string val;
- if (valJ != nullptr) {
- ScopedUtfChars key_utf(env, valJ);
- val = key_utf.c_str();
+ ScopedUtfChars key(env, keyJ);
+ if (!key.c_str()) {
+ return;
+ }
+ std::optional<ScopedUtfChars> value;
+ if (valJ != nullptr) {
+ value.emplace(env, valJ);
+ if (!value->c_str()) {
+ return;
}
- return android::base::SetProperty(key, val);
- };
- if (!ConvertKeyAndForward(env, keyJ, true, handler)) {
- // Must have been a failure in SetProperty.
+ }
+ bool success;
+#if defined(__BIONIC__)
+ success = !__system_property_set(key.c_str(), value ? value->c_str() : "");
+#else
+ success = android::base::SetProperty(key.c_str(), value ? value->c_str() : "");
+#endif
+ if (!success) {
jniThrowException(env, "java/lang/RuntimeException",
"failed to set system property (check logcat for reason)");
}
@@ -156,8 +246,6 @@
int register_android_os_SystemProperties(JNIEnv *env)
{
const JNINativeMethod method_table[] = {
- { "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
- (void*) SystemProperties_getS },
{ "native_get",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getSS },
@@ -167,6 +255,18 @@
(void*) SystemProperties_get_integral<jlong> },
{ "native_get_boolean", "(Ljava/lang/String;Z)Z",
(void*) SystemProperties_get_boolean },
+ { "native_find",
+ "(Ljava/lang/String;)J",
+ (void*) SystemProperties_find },
+ { "native_get",
+ "(J)Ljava/lang/String;",
+ (void*) SystemProperties_getH },
+ { "native_get_int", "(JI)I",
+ (void*) SystemProperties_get_integralH<jint> },
+ { "native_get_long", "(JJ)J",
+ (void*) SystemProperties_get_integralH<jlong> },
+ { "native_get_boolean", "(JZ)Z",
+ (void*) SystemProperties_get_booleanH },
{ "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) SystemProperties_set },
{ "native_add_change_callback", "()V",
@@ -178,4 +278,4 @@
method_table, NELEM(method_table));
}
-};
+} // namespace android
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 928351e..b48ac33 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -93,6 +93,27 @@
}
@SmallTest
+ private static void testHandle() throws Exception {
+ String value;
+ SystemProperties.Handle handle = SystemProperties.find("doesnotexist_2341431");
+ assertNull(handle);
+ SystemProperties.set(KEY, "abc");
+ handle = SystemProperties.find(KEY);
+ assertNotNull(handle);
+ value = handle.get();
+ assertEquals("abc", value);
+ SystemProperties.set(KEY, "blarg");
+ value = handle.get();
+ assertEquals("blarg", value);
+ SystemProperties.set(KEY, "1");
+ assertEquals(1, handle.getInt(-1));
+ assertEquals(1, handle.getLong(-1));
+ assertEquals(true, handle.getBoolean(false));
+ SystemProperties.set(KEY, "");
+ assertEquals(12345, handle.getInt(12345));
+ }
+
+ @SmallTest
public void testIntegralProperties() throws Exception {
testInt("", 123, 123);
testInt("", 0, 0);