do not merge. Move property setting from libcutils to bionic.
Backport I110b653a58f3
All the other property stuff is already here. Property setting was
only in libcutils previously to leverage a utility function / constant
or two.
Unfortunately in the process of fixing a race condition we would've
had to do break abstraction boundaries and put some libc-internal
details into libcutils so instead of that we'll just move this
into bionic.
Along with Iee1ca9b7, this now passes:
$ adb shell am instrument -w -e class android.os.SystemPropertiesTest \
com.android.frameworks.coretests.systemproperties/android.test.InstrumentationTestRunner
Bug: 3511230
Change-Id: I1b588db3344169621e1279ecc0b660cf4e1015d7
diff --git a/libc/bionic/system_properties.c b/libc/bionic/system_properties.c
index 767baa3..c40950f 100644
--- a/libc/bionic/system_properties.c
+++ b/libc/bionic/system_properties.c
@@ -38,13 +38,14 @@
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
+#include <unistd.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
#include <sys/atomics.h>
-static const char property_service_name[] = PROP_SERVICE_NAME;
+static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static unsigned dummy_props = 0;
@@ -152,6 +153,114 @@
}
}
+
+static int send_prop_msg(prop_msg *msg)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ size_t namelen;
+ int s;
+ int r;
+
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if(s < 0) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ namelen = strlen(property_service_socket);
+ strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path);
+ addr.sun_family = AF_LOCAL;
+ alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+
+ if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen) < 0)) {
+ close(s);
+ return -1;
+ }
+
+ r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
+
+ if(r == sizeof(prop_msg)) {
+ r = 0;
+ } else {
+ r = -1;
+ }
+
+ close(s);
+ return r;
+}
+
+int __system_property_set(const char *key, const char *value)
+{
+ unsigned old_serial;
+ volatile unsigned *serial;
+ prop_msg msg;
+ int err;
+ prop_area *pa = __system_property_area__;
+ int tries = 0;
+ int update_seen = 0;
+
+ if(key == 0) return -1;
+ if(value == 0) value = "";
+ if(strlen(key) >= PROP_NAME_MAX) return -1;
+ if(strlen(value) >= PROP_VALUE_MAX) return -1;
+
+ memset(&msg, 0, sizeof msg);
+ msg.cmd = PROP_MSG_SETPROP;
+ strlcpy(msg.name, key, sizeof msg.name);
+ strlcpy(msg.value, value, sizeof msg.value);
+
+ /* Note the system properties serial number before we do our update. */
+ const prop_info *pi = __system_property_find(key);
+ if(pi != NULL) {
+ serial = &pi->serial;
+ } else {
+ serial = &pa->serial;
+ }
+ old_serial = *serial;
+
+ err = send_prop_msg(&msg);
+ if(err < 0) {
+ return err;
+ }
+
+ /**
+ * Wait for the shared memory page to be written back and be
+ * visible in our address space before returning to the caller
+ * who might reasonably expect subsequent reads to match what was
+ * just written.
+ *
+ * Sleep 5 ms after failed checks and only wait up to a 500 ms
+ * total, just in case the system property server fails to update
+ * for whatever reason.
+ */
+ do {
+ struct timespec timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 2500000; // 2.5 ms
+
+ if(tries++ > 0) {
+ usleep(2500); // 2.5 ms
+ }
+ __futex_wait(serial, old_serial, &timeout);
+ if(pi != NULL) {
+ unsigned new_serial = *serial;
+ /* Waiting on a specific prop_info to be updated. */
+ if (old_serial != new_serial && !SERIAL_DIRTY(new_serial)) {
+ update_seen = 1;
+ }
+ } else {
+ /* Waiting for a prop_info to be created. */
+ const prop_info *new_pi = __system_property_find(key);
+ if(new_pi != NULL && !SERIAL_DIRTY(new_pi->serial)) {
+ update_seen = 1;
+ }
+ }
+ } while (!update_seen && tries < 100);
+
+ return 0;
+}
+
int __system_property_wait(const prop_info *pi)
{
unsigned n;