Revert "Revert "Remove limit of system property name length""

This reverts commit 489f58b5eaedd5a80635bb3a7b39e97037c585f6.
Bug: http://b/33926793
Bug: http://b/34670529
Test: Run bionic-unit-tests --gtest_filter=prop*

Change-Id: Id4e94652dc2310a21f5b7bd3af098bf79df3f380
diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp
index 5aefbaf..da71f09 100644
--- a/libc/bionic/system_properties.cpp
+++ b/libc/bionic/system_properties.cpp
@@ -59,8 +59,10 @@
 #include "private/bionic_sdk_version.h"
 #include "private/libc_logging.h"
 
-static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
+#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
 
+static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
+static const char* kServiceVersionPropertyName = "ro.property_service.version";
 
 /*
  * Properties are stored in a hybrid trie/binary tree structure.
@@ -81,8 +83,7 @@
 
 // Represents a node in the trie.
 struct prop_bt {
-    uint8_t namelen;
-    uint8_t reserved[3];
+    uint32_t namelen;
 
     // The property trie is updated only by the init process (single threaded) which provides
     // property service. And it can be read by multiple threads at the same time.
@@ -107,7 +108,7 @@
 
     char name[0];
 
-    prop_bt(const char *name, const uint8_t name_length) {
+    prop_bt(const char *name, const uint32_t name_length) {
         this->namelen = name_length;
         memcpy(this->name, name, name_length);
         this->name[name_length] = '\0';
@@ -140,9 +141,9 @@
 
 private:
     void *allocate_obj(const size_t size, uint_least32_t *const off);
-    prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off);
-    prop_info *new_prop_info(const char *name, uint8_t namelen,
-                             const char *value, uint8_t valuelen,
+    prop_bt *new_prop_bt(const char *name, uint32_t namelen, uint_least32_t *const off);
+    prop_info *new_prop_info(const char *name, uint32_t namelen,
+                             const char *value, uint32_t valuelen,
                              uint_least32_t *const off);
     void *to_prop_obj(uint_least32_t off);
     prop_bt *to_prop_bt(atomic_uint_least32_t *off_p);
@@ -151,11 +152,11 @@
     prop_bt *root_node();
 
     prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
-                          uint8_t namelen, bool alloc_if_needed);
+                          uint32_t namelen, bool alloc_if_needed);
 
     const prop_info *find_property(prop_bt *const trie, const char *name,
-                                   uint8_t namelen, const char *value,
-                                   uint8_t valuelen, bool alloc_if_needed);
+                                   uint32_t namelen, const char *value,
+                                   uint32_t valuelen, bool alloc_if_needed);
 
     bool foreach_property(prop_bt *const trie,
                           void (*propfn)(const prop_info *pi, void *cookie),
@@ -173,19 +174,20 @@
 
 struct prop_info {
     atomic_uint_least32_t serial;
+    // we need to keep this buffer around because the property
+    // value can be modified whereas name is constant.
     char value[PROP_VALUE_MAX];
     char name[0];
 
-    prop_info(const char *name, const uint8_t namelen, const char *value,
-              const uint8_t valuelen) {
+    prop_info(const char *name, uint32_t namelen, const char *value, uint32_t valuelen) {
         memcpy(this->name, name, namelen);
         this->name[namelen] = '\0';
         atomic_init(&this->serial, valuelen << 24);
         memcpy(this->value, value, valuelen);
         this->value[valuelen] = '\0';
     }
-private:
-    DISALLOW_COPY_AND_ASSIGN(prop_info);
+  private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(prop_info);
 };
 
 struct find_nth_cookie {
@@ -358,7 +360,7 @@
     return data_ + *off;
 }
 
-prop_bt *prop_area::new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off)
+prop_bt *prop_area::new_prop_bt(const char *name, uint32_t namelen, uint_least32_t *const off)
 {
     uint_least32_t new_offset;
     void *const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
@@ -371,8 +373,8 @@
     return NULL;
 }
 
-prop_info *prop_area::new_prop_info(const char *name, uint8_t namelen,
-        const char *value, uint8_t valuelen, uint_least32_t *const off)
+prop_info *prop_area::new_prop_info(const char *name, uint32_t namelen,
+        const char *value, uint32_t valuelen, uint_least32_t *const off)
 {
     uint_least32_t new_offset;
     void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
@@ -408,8 +410,8 @@
     return reinterpret_cast<prop_bt*>(to_prop_obj(0));
 }
 
-static int cmp_prop_name(const char *one, uint8_t one_len, const char *two,
-        uint8_t two_len)
+static int cmp_prop_name(const char *one, uint32_t one_len, const char *two,
+        uint32_t two_len)
 {
     if (one_len < two_len)
         return -1;
@@ -420,7 +422,7 @@
 }
 
 prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name,
-                                 uint8_t namelen, bool alloc_if_needed)
+                                 uint32_t namelen, bool alloc_if_needed)
 {
 
     prop_bt* current = bt;
@@ -471,7 +473,7 @@
 }
 
 const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
-        uint8_t namelen, const char *value, uint8_t valuelen,
+        uint32_t namelen, const char *value, uint32_t valuelen,
         bool alloc_if_needed)
 {
     if (!trie) return NULL;
@@ -481,7 +483,7 @@
     while (true) {
         const char *sep = strchr(remaining_name, '.');
         const bool want_subtree = (sep != NULL);
-        const uint8_t substr_size = (want_subtree) ?
+        const uint32_t substr_size = (want_subtree) ?
             sep - remaining_name : strlen(remaining_name);
 
         if (!substr_size) {
@@ -531,28 +533,93 @@
     }
 }
 
-static int send_prop_msg(const prop_msg *msg)
-{
-    const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
-    if (fd == -1) {
-        return -1;
+class PropertyServiceConnection {
+ public:
+  PropertyServiceConnection() : last_error_(0) {
+    fd_ = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+    if (fd_ == -1) {
+      last_error_ = errno;
+      return;
     }
 
     const size_t namelen = strlen(property_service_socket);
-
     sockaddr_un addr;
     memset(&addr, 0, sizeof(addr));
     strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
     addr.sun_family = AF_LOCAL;
     socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
-    if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) {
-        close(fd);
-        return -1;
+
+    if (TEMP_FAILURE_RETRY(connect(fd_, reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
+      close(fd_);
+      fd_ = -1;
+      last_error_ = errno;
+    }
+  }
+
+  bool IsValid() {
+    return fd_ != -1;
+  }
+
+  int GetLastError() {
+    return last_error_;
+  }
+
+  bool SendUint32(uint32_t value) {
+    int result = TEMP_FAILURE_RETRY(send(fd_, &value, sizeof(value), 0));
+    return CheckSendRecvResult(result, sizeof(value));
+  }
+
+  bool SendString(const char* value) {
+    uint32_t valuelen = strlen(value);
+    if (!SendUint32(valuelen)) {
+      return false;
     }
 
-    const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
+    int result = TEMP_FAILURE_RETRY(send(fd_, value, valuelen, 0));
+    return CheckSendRecvResult(result, valuelen);
+  }
+
+  bool RecvInt32(int32_t* value) {
+    int result = TEMP_FAILURE_RETRY(recv(fd_, value, sizeof(*value), MSG_WAITALL));
+    return CheckSendRecvResult(result, sizeof(*value));
+  }
+
+  int GetFd() {
+    return fd_;
+  }
+
+  ~PropertyServiceConnection() {
+    if (fd_ != -1) {
+      close(fd_);
+    }
+  }
+ private:
+  bool CheckSendRecvResult(int result, int expected_len) {
+    if (result == -1) {
+      last_error_ = errno;
+    } else if (result != expected_len) {
+      last_error_ = -1;
+    } else {
+      last_error_ = 0;
+    }
+
+    return last_error_ == 0;
+  }
+
+  int fd_;
+  int last_error_;
+};
+
+static int send_prop_msg(const prop_msg* msg) {
+    PropertyServiceConnection connection;
+    if (!connection.IsValid()) {
+      return connection.GetLastError();
+    }
 
     int result = -1;
+    int fd = connection.GetFd();
+
+    const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
     if (num_bytes == sizeof(prop_msg)) {
         // We successfully wrote to the property server but now we
         // wait for the property server to finish its work.  It
@@ -578,11 +645,13 @@
             // ms so callers who do read-after-write can reliably see
             // what they've written.  Most of the time.
             // TODO: fix the system properties design.
+            __libc_format_log(ANDROID_LOG_WARN, "libc",
+                              "Property service has timed out while trying to set \"%s\" to \"%s\"",
+                              msg->name, msg->value);
             result = 0;
         }
     }
 
-    close(fd);
     return result;
 }
 
@@ -1124,51 +1193,136 @@
         atomic_thread_fence(memory_order_acquire);
         if (serial ==
                 load_const_atomic(&(pi->serial), memory_order_relaxed)) {
-            if (name != 0) {
-                strcpy(name, pi->name);
+            if (name != nullptr) {
+                size_t namelen = strlcpy(name, pi->name, PROP_NAME_MAX);
+                if(namelen >= PROP_NAME_MAX) {
+                  __libc_format_log(ANDROID_LOG_ERROR, "libc",
+                                    "The property name length for \"%s\" is >= %d;"
+                                    " please use __system_property_read_callback"
+                                    " to read this property. (the name is truncated to \"%s\")",
+                                    pi->name, PROP_NAME_MAX - 1, name);
+                }
             }
             return len;
         }
     }
 }
 
+void __system_property_read_callback(const prop_info* pi,
+                                     void (*callback)(void* cookie, const char* name, const char* value),
+                                     void* cookie) {
+  // TODO (dimitry): do we need compat mode for this function?
+  if (__predict_false(compat_mode)) {
+    char value_buf[PROP_VALUE_MAX];
+    __system_property_read_compat(pi, nullptr, value_buf);
+    callback(cookie, pi->name, value_buf);
+    return;
+  }
+
+  while (true) {
+    uint32_t serial = __system_property_serial(pi); // acquire semantics
+    size_t len = SERIAL_VALUE_LEN(serial);
+    char value_buf[len+1];
+
+    memcpy(value_buf, pi->value, len);
+    value_buf[len] = '\0';
+
+    // TODO: see todo in __system_property_read function
+    atomic_thread_fence(memory_order_acquire);
+    if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
+      callback(cookie, pi->name, value_buf);
+      return;
+    }
+  }
+}
+
 int __system_property_get(const char *name, char *value)
 {
     const prop_info *pi = __system_property_find(name);
 
     if (pi != 0) {
-        return __system_property_read(pi, 0, value);
+        return __system_property_read(pi, nullptr, value);
     } else {
         value[0] = 0;
         return 0;
     }
 }
 
-int __system_property_set(const char *key, const char *value)
-{
-    if (key == 0) return -1;
-    if (value == 0) value = "";
-    if (strlen(key) >= PROP_NAME_MAX) return -1;
+static constexpr uint32_t kProtocolVersion1 = 1;
+static constexpr uint32_t kProtocolVersion2 = 2; // current
+
+static atomic_uint_least32_t g_propservice_protocol_version = 0;
+
+static void detect_protocol_version() {
+    char value[PROP_VALUE_MAX];
+    if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
+        g_propservice_protocol_version = kProtocolVersion1;
+        __libc_format_log(ANDROID_LOG_WARN, "libc",
+                          "Using old property service protocol (\"%s\" is not set)",
+                          kServiceVersionPropertyName);
+    } else {
+        uint32_t version = static_cast<uint32_t>(atoll(value));
+        if (version >= kProtocolVersion2) {
+            g_propservice_protocol_version = kProtocolVersion2;
+        } else {
+          __libc_format_log(ANDROID_LOG_WARN, "libc",
+                            "Using old property service protocol (\"%s\"=\"%s\")",
+                            kServiceVersionPropertyName, value);
+            g_propservice_protocol_version = kProtocolVersion1;
+        }
+    }
+}
+
+int __system_property_set(const char* key, const char* value) {
+    if (key == nullptr) return -1;
+    if (value == nullptr) value = "";
     if (strlen(value) >= PROP_VALUE_MAX) return -1;
 
-    prop_msg msg;
-    memset(&msg, 0, sizeof msg);
-    msg.cmd = PROP_MSG_SETPROP;
-    strlcpy(msg.name, key, sizeof msg.name);
-    strlcpy(msg.value, value, sizeof msg.value);
-
-    const int err = send_prop_msg(&msg);
-    if (err < 0) {
-        return err;
+    if (g_propservice_protocol_version == 0) {
+        detect_protocol_version();
     }
 
-    return 0;
+    int result = -1;
+    if (g_propservice_protocol_version == kProtocolVersion1) {
+        // Old protocol does not support long names
+        if (strlen(key) >= PROP_NAME_MAX) return -1;
+
+        prop_msg msg;
+        memset(&msg, 0, sizeof msg);
+        msg.cmd = PROP_MSG_SETPROP;
+        strlcpy(msg.name, key, sizeof msg.name);
+        strlcpy(msg.value, value, sizeof msg.value);
+
+        result = send_prop_msg(&msg);
+    } else {
+        // Use proper protocol
+        PropertyServiceConnection connection;
+        if (connection.IsValid() &&
+            connection.SendUint32(PROP_MSG_SETPROP2) &&
+            connection.SendString(key) &&
+            connection.SendString(value) &&
+            connection.RecvInt32(&result)) {
+          if (result != PROP_SUCCESS) {
+            __libc_format_log(ANDROID_LOG_WARN, "libc",
+                              "Unable to set property \"%s\" to \"%s\": error code: 0x%x",
+                              key, value, result);
+          }
+        } else {
+          result = connection.GetLastError();
+          __libc_format_log(ANDROID_LOG_WARN, "libc",
+                            "Unable to set property \"%s\" to \"%s\": error code: 0x%x (%s)",
+                            key, value, result, strerror(result));
+        }
+    }
+
+    return result != 0 ? -1 : 0;
 }
 
 int __system_property_update(prop_info *pi, const char *value, unsigned int len)
 {
-    if (len >= PROP_VALUE_MAX)
+    if (len >= PROP_VALUE_MAX) {
         return -1;
+    }
 
     prop_area* pa = __system_property_area__;
 
@@ -1183,7 +1337,8 @@
     // used memory_order_relaxed atomics, and use the analogous
     // counterintuitive fence.
     atomic_thread_fence(memory_order_release);
-    memcpy(pi->value, value, len + 1);
+    strlcpy(pi->value, value, len + 1);
+
     atomic_store_explicit(
         &pi->serial,
         (len << 24) | ((serial + 1) & 0xffffff),
@@ -1202,12 +1357,13 @@
 int __system_property_add(const char *name, unsigned int namelen,
             const char *value, unsigned int valuelen)
 {
-    if (namelen >= PROP_NAME_MAX)
+    if (valuelen >= PROP_VALUE_MAX) {
         return -1;
-    if (valuelen >= PROP_VALUE_MAX)
+    }
+
+    if (namelen < 1) {
         return -1;
-    if (namelen < 1)
-        return -1;
+    }
 
     if (!__system_property_area__) {
         return -1;
@@ -1221,8 +1377,9 @@
     }
 
     bool ret = pa->add(name, namelen, value, valuelen);
-    if (!ret)
+    if (!ret) {
         return -1;
+    }
 
     // There is only a single mutator, but we want to make sure that
     // updates are visible to a reader waiting for the update.
diff --git a/libc/bionic/system_properties_compat.c b/libc/bionic/system_properties_compat.c
index 6aeaa0c..ba73a2c 100644
--- a/libc/bionic/system_properties_compat.c
+++ b/libc/bionic/system_properties_compat.c
@@ -43,6 +43,7 @@
 
 #define TOC_NAME_LEN(toc)       ((toc) >> 24)
 #define TOC_TO_INFO(area, toc)  ((prop_info_compat*) (((char*) area) + ((toc) & 0xFFFFFF)))
+#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
 
 struct prop_area_compat {
     unsigned volatile count;
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index 89a1608..080280e 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -48,7 +48,6 @@
 
 #define PA_SIZE         (128 * 1024)
 
-#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
 #define SERIAL_DIRTY(serial) ((serial) & 1)
 
 __BEGIN_DECLS
@@ -61,6 +60,18 @@
 };
 
 #define PROP_MSG_SETPROP 1
+#define PROP_MSG_SETPROP2 0x00020001
+
+#define PROP_SUCCESS 0
+#define PROP_ERROR_READ_CMD 0x0004
+#define PROP_ERROR_READ_DATA 0x0008
+#define PROP_ERROR_READ_ONLY_PROPERTY 0x000B
+#define PROP_ERROR_INVALID_NAME 0x0010
+#define PROP_ERROR_INVALID_VALUE 0x0014
+#define PROP_ERROR_PERMISSION_DENIED 0x0018
+#define PROP_ERROR_INVALID_CMD 0x001B
+#define PROP_ERROR_HANDLE_CONTROL_MESSAGE 0x0020
+#define PROP_ERROR_SET_FAILED 0x0024
 
 /*
 ** Rules:
diff --git a/libc/include/sys/system_properties.h b/libc/include/sys/system_properties.h
index faed9a0..d80b2fe 100644
--- a/libc/include/sys/system_properties.h
+++ b/libc/include/sys/system_properties.h
@@ -30,6 +30,7 @@
 #define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
 
 #include <sys/cdefs.h>
+#include <stddef.h>
 
 __BEGIN_DECLS
 
@@ -64,13 +65,17 @@
 /* Read the value of a system property.  Returns the length
 ** of the value.  Copies the value and \0 terminator into
 ** the provided value pointer.  Total length (including
-** terminator) will be no greater that PROP_VALUE_MAX.
+** terminator) will be no greater that PROP_VALUE_MAX for
+** __system_property_read.
 **
 ** If name is nonzero, up to PROP_NAME_MAX bytes will be
 ** copied into the provided name pointer.  The name will
 ** be \0 terminated.
 */
 int __system_property_read(const prop_info *pi, char *name, char *value);
+void __system_property_read_callback(const prop_info *pi,
+                                     void (*)(void* cookie, const char *name, const char *value),
+                                     void* cookie) __INTRODUCED_IN_FUTURE;
 
 /* Return a prop_info for the nth system property, or NULL if 
 ** there is no nth property.  Use __system_property_read() to
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 3ff7ead..4319429 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1265,6 +1265,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index a696c41..f88c284 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1188,6 +1188,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     catclose; # future
     catgets; # future
     catopen; # future
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 391c65c..46087e6 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1290,6 +1290,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index c89b4ad..075746c 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1249,6 +1249,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index a696c41..f88c284 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1188,6 +1188,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     catclose; # future
     catgets; # future
     catopen; # future
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 51b222d..75c7757 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1247,6 +1247,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     bsd_signal; # arm x86 mips versioned=26
     catclose; # future
     catgets; # future
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index a696c41..f88c284 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1188,6 +1188,7 @@
 
 LIBC_O {
   global:
+    __system_property_read_callback; # future
     catclose; # future
     catgets; # future
     catopen; # future
diff --git a/tests/Android.bp b/tests/Android.bp
index 85ae26e..64a5c30 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -132,6 +132,7 @@
         "sys_vfs_test.cpp",
         "sys_xattr_test.cpp",
         "system_properties_test.cpp",
+        "system_properties_test2.cpp",
         "time_test.cpp",
         "uchar_test.cpp",
         "unistd_nofortify_test.cpp",
diff --git a/tests/system_properties_test.cpp b/tests/system_properties_test.cpp
index 70482f0..6b037d8 100644
--- a/tests/system_properties_test.cpp
+++ b/tests/system_properties_test.cpp
@@ -69,7 +69,7 @@
 static void foreach_test_callback(const prop_info *pi, void* cookie) {
     size_t *count = static_cast<size_t *>(cookie);
 
-    ASSERT_NE((prop_info *)NULL, pi);
+    ASSERT_TRUE(pi != nullptr);
     (*count)++;
 }
 
@@ -98,16 +98,15 @@
     ok[name_i][name_j][name_k] = true;
 }
 
-static void *PropertyWaitHelperFn(void *arg) {
-    int *flag = (int *)arg;
-    prop_info *pi;
-    pi = (prop_info *)__system_property_find("property");
+static void* PropertyWaitHelperFn(void* arg) {
+    int* flag = static_cast<int*>(arg);
+    prop_info* pi = const_cast<prop_info*>(__system_property_find("property"));
     usleep(100000);
 
     *flag = 1;
     __system_property_update(pi, "value3", 6);
 
-    return NULL;
+    return nullptr;
 }
 
 #endif // __BIONIC__
@@ -123,6 +122,16 @@
     ASSERT_EQ(0, __system_property_add("other_property", 14, "value2", 6));
     ASSERT_EQ(0, __system_property_add("property_other", 14, "value3", 6));
 
+    // check that there is no limit on property name length
+    char name[PROP_NAME_MAX + 11];
+    name[0] = 'p';
+    for (size_t i = 1; i < sizeof(name); i++) {
+      name[i] = 'x';
+    }
+
+    name[sizeof(name)-1] = '\0';
+    ASSERT_EQ(0, __system_property_add(name, strlen(name), "value", 5));
+
     ASSERT_EQ(6, __system_property_get("property", propvalue));
     ASSERT_STREQ(propvalue, "value1");
 
@@ -131,6 +140,9 @@
 
     ASSERT_EQ(6, __system_property_get("property_other", propvalue));
     ASSERT_STREQ(propvalue, "value3");
+
+    ASSERT_EQ(5, __system_property_get(name, propvalue));
+    ASSERT_STREQ(propvalue, "value");
 #else // __BIONIC__
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif // __BIONIC__
@@ -323,7 +335,6 @@
     ASSERT_EQ(0, __system_property_find("property1"));
     ASSERT_EQ(0, __system_property_get("property1", prop_value));
 
-    ASSERT_EQ(-1, __system_property_add("name", PROP_NAME_MAX, "value", 5));
     ASSERT_EQ(-1, __system_property_add("name", 4, "value", PROP_VALUE_MAX));
     ASSERT_EQ(-1, __system_property_update(NULL, "value", PROP_VALUE_MAX));
 #else // __BIONIC__
@@ -359,12 +370,13 @@
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
     serial = __system_property_wait_any(0);
-    pi = (prop_info *)__system_property_find("property");
-    ASSERT_NE((prop_info *)NULL, pi);
+
+    pi = const_cast<prop_info*>(__system_property_find("property"));
+    ASSERT_TRUE(pi != nullptr);
     __system_property_update(pi, "value2", 6);
     serial = __system_property_wait_any(serial);
 
-    ASSERT_EQ(0, pthread_create(&t, NULL, PropertyWaitHelperFn, &flag));
+    ASSERT_EQ(0, pthread_create(&t, nullptr, PropertyWaitHelperFn, &flag));
     ASSERT_EQ(flag, 0);
     serial = __system_property_wait_any(serial);
     ASSERT_EQ(flag, 1);
@@ -396,9 +408,7 @@
 
   // This test only makes sense if we're talking to the real system property service.
   struct stat sb;
-  if (stat(PROP_FILENAME, &sb) == -1 && errno == ENOENT) {
-    return;
-  }
+  ASSERT_FALSE(stat(PROP_FILENAME, &sb) == -1 && errno == ENOENT);
 
   ASSERT_EXIT(__system_property_add("property", 8, "value", 5), KilledByFault(), "");
 #else // __BIONIC__
diff --git a/tests/system_properties_test2.cpp b/tests/system_properties_test2.cpp
new file mode 100644
index 0000000..858bc17
--- /dev/null
+++ b/tests/system_properties_test2.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sstream>
+#include <string>
+
+#if defined(__BIONIC__)
+#include <sys/system_properties.h>
+
+static uint64_t NanoTime() {
+  timespec now;
+  clock_gettime(CLOCK_MONOTONIC, &now);
+  return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
+}
+#endif
+
+// Note that this test affects global state of the system
+// this tests tries to mitigate this by using utime+pid
+// prefix for the property name. It is still results in
+// pollution of property service since properties cannot
+// be removed.
+//
+// Note that there is also possibility to run into "out-of-memory"
+// if this test if it is executed often enough without reboot.
+TEST(properties, smoke) {
+#if defined(__BIONIC__)
+    char propvalue[PROP_VALUE_MAX];
+
+    std::stringstream ss;
+    ss << "debug.test." << getpid() << "." << NanoTime() << ".";
+    const std::string property_prefix = ss.str();
+    const std::string property_name = property_prefix + "property1";
+
+    // Set brand new property
+    ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value1"));
+    ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
+    ASSERT_STREQ("value1", propvalue);
+
+    std::string long_value = "property-";
+    for (size_t i = 0; i < PROP_VALUE_MAX; i++) {
+      long_value += "y";
+    }
+
+    // Make sure that attempts to set invalid property value fails and preserves
+    // previous value.
+    propvalue[0] = '\0';
+    ASSERT_EQ(-1, __system_property_set(property_name.c_str(), long_value.c_str()));
+    ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
+    ASSERT_STREQ("value1", propvalue);
+
+    // Update property
+    ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value1-1"));
+    ASSERT_EQ(8, __system_property_get(property_name.c_str(), propvalue));
+    ASSERT_STREQ("value1-1", propvalue);
+
+
+    // check that there is no limit on property name length
+    char suffix[1024];
+    for (size_t i = 0; i < sizeof(suffix); i++) {
+      suffix[i] = 'x';
+    }
+
+    suffix[sizeof(suffix)-1] = '\0';
+    const std::string long_property_name = property_prefix + suffix;
+
+    ASSERT_EQ(0, __system_property_set(long_property_name.c_str(), "value2"));
+    ASSERT_EQ(6, __system_property_get(long_property_name.c_str(), propvalue));
+    ASSERT_STREQ("value2", propvalue);
+
+    // test find and read_callback
+    const prop_info* pi = __system_property_find(property_name.c_str());
+    ASSERT_TRUE(pi != nullptr);
+
+    std::string expected_name = property_name;
+    __system_property_read_callback(pi, [](void* cookie, const char *name, const char *value) {
+      const std::string* expected_name = static_cast<const std::string*>(cookie);
+      ASSERT_EQ(*expected_name, name);
+      ASSERT_STREQ("value1-1", value);
+    }, &expected_name);
+
+    pi = __system_property_find(long_property_name.c_str());
+    ASSERT_TRUE(pi != nullptr);
+
+    expected_name = long_property_name;
+    __system_property_read_callback(pi, [](void* cookie, const char *name, const char *value) {
+      const std::string* expected_name = static_cast<const std::string*>(cookie);
+      ASSERT_EQ(*expected_name, name);
+      ASSERT_STREQ("value2", value);
+    }, &expected_name);
+
+    // Check that read() for long names still works but returns truncated version of the name
+    pi = __system_property_find(property_name.c_str());
+    ASSERT_TRUE(pi != nullptr);
+    char legacy_name[PROP_NAME_MAX];
+    expected_name = std::string(property_name.c_str(), PROP_NAME_MAX-1);
+    ASSERT_EQ(8, __system_property_read(pi, &legacy_name[0], propvalue));
+    ASSERT_EQ(expected_name, legacy_name);
+    ASSERT_STREQ("value1-1", propvalue);
+
+    const prop_info* pi_long = __system_property_find(long_property_name.c_str());
+    ASSERT_TRUE(pi != nullptr);
+    expected_name = std::string(long_property_name.c_str(), PROP_NAME_MAX-1);
+    ASSERT_EQ(6, __system_property_read(pi_long, &legacy_name[0], propvalue));
+    ASSERT_EQ(expected_name, legacy_name);
+    ASSERT_STREQ("value2", propvalue);
+#else // __BIONIC__
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif // __BIONIC__
+}
+