Enable libcutils property_get()/property_set() on the host.
Expose working equivalents of the bionic __system_property_get() and
__system_property_set() functions for libcutils to use.
Various improvements to the implementations so that we can pass all the
libcutils tests too.
Bug: http://b/151789258
Test: treehugger
Change-Id: If4634d1ac11cc7c84c029518beedfa653f74f66b
diff --git a/include/android-base/properties.h b/include/android-base/properties.h
index 49f1f31..8b34573 100644
--- a/include/android-base/properties.h
+++ b/include/android-base/properties.h
@@ -99,3 +99,10 @@
} // namespace base
} // namespace android
+
+#if !defined(__BIONIC__)
+/** Implementation detail. */
+extern "C" int __system_property_set(const char*, const char*);
+/** Implementation detail. */
+extern "C" int __system_property_get(const char*, char*);
+#endif
diff --git a/properties.cpp b/properties.cpp
index 5c9ec7e..8190987 100644
--- a/properties.cpp
+++ b/properties.cpp
@@ -32,6 +32,39 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#if !defined(__BIONIC__)
+
+#define PROP_VALUE_MAX 92
+
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+
+int __system_property_set(const char* key, const char* value) {
+ if (key == nullptr || *key == '\0') return -1;
+ if (value == nullptr) value = "";
+
+ bool read_only = !strncmp(key, "ro.", 3);
+ if (read_only) {
+ const auto [it, success] = g_properties.insert({key, value});
+ return success ? 0 : -1;
+ }
+
+ if (strlen(value) >= 92) return -1;
+ g_properties[key] = value;
+ return 0;
+}
+
+int __system_property_get(const char* key, char* value) {
+ auto it = g_properties.find(key);
+ if (it == g_properties.end()) {
+ *value = '\0';
+ return 0;
+ }
+ snprintf(value, PROP_VALUE_MAX, "%s", it->second.c_str());
+ return strlen(value);
+}
+
+#endif
+
namespace android {
namespace base {
@@ -73,14 +106,6 @@
template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
-#if !defined(__BIONIC__)
-static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
-static int __system_property_set(const char* key, const char* value) {
- g_properties[key] = value;
- return 0;
-}
-#endif
-
std::string GetProperty(const std::string& key, const std::string& default_value) {
std::string property_value;
#if defined(__BIONIC__)
@@ -94,6 +119,7 @@
},
&property_value);
#else
+ // TODO: implement host __system_property_find()/__system_property_read_callback()?
auto it = g_properties.find(key);
if (it == g_properties.end()) return default_value;
property_value = it->second;
diff --git a/properties_test.cpp b/properties_test.cpp
index c30c41e..94ae690 100644
--- a/properties_test.cpp
+++ b/properties_test.cpp
@@ -44,13 +44,33 @@
ASSERT_EQ("default", s);
}
-TEST(properties, empty) {
+TEST(properties, too_long) {
+ // Properties have a fixed limit on the size of their value.
+ std::string key("debug.libbase.property_too_long");
+ std::string value(92, 'a');
+ ASSERT_FALSE(android::base::SetProperty(key, value));
+ ASSERT_EQ("missing", android::base::GetProperty(key, "missing"));
+
+ // Except for "ro." properties, which can have arbitrarily-long values.
+ key = "ro." + key + std::to_string(time(nullptr));
+ ASSERT_TRUE(android::base::SetProperty(key, value));
+ ASSERT_EQ(value, android::base::GetProperty(key, "missing"));
+ // ...because you can't change them.
+ ASSERT_FALSE(android::base::SetProperty(key, "hello"));
+ ASSERT_EQ(value, android::base::GetProperty(key, "missing"));
+}
+
+TEST(properties, empty_key) {
+ ASSERT_FALSE(android::base::SetProperty("", "hello"));
+ ASSERT_EQ("default", android::base::GetProperty("", "default"));
+}
+
+TEST(properties, empty_value) {
// Because you can't delete a property, people "delete" them by
// setting them to the empty string. In that case we'd want to
// keep the default value (like cutils' property_get did).
- android::base::SetProperty("debug.libbase.property_test", "");
- std::string s = android::base::GetProperty("debug.libbase.property_test", "default");
- ASSERT_EQ("default", s);
+ ASSERT_TRUE(android::base::SetProperty("debug.libbase.property_empty_value", ""));
+ ASSERT_EQ("default", android::base::GetProperty("debug.libbase.property_empty_value", "default"));
}
static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) {