bionic: make property area expandable

The property area is initially one 4K region, automatically expanding as
needed up to 64 regions.

To avoid duplicating code, __system_property_area_init() now allocates
and initializes the first region (previously it was allocated in init's
init_property_area() and initialized in bionic).  For testing purposes,
__system_property_set_filename() may be used to override the file used
to map in regions.

Change-Id: Ibe00ef52464bfa590953c4699a6d98383b0142b1
Signed-off-by: Greg Hackmann <ghackmann@google.com>
diff --git a/tests/system_properties_test.cpp b/tests/system_properties_test.cpp
index 70ff1d6..50bdfdf 100644
--- a/tests/system_properties_test.cpp
+++ b/tests/system_properties_test.cpp
@@ -16,32 +16,61 @@
 
 #include <gtest/gtest.h>
 #include <sys/wait.h>
+#include <unistd.h>
+#include <string>
 
 #if __BIONIC__
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-extern void *__system_property_area__;
+extern void *__system_property_regions__[PA_REGION_COUNT];
 
 struct LocalPropertyTestState {
-    LocalPropertyTestState() {
-        old_pa = __system_property_area__;
-        pa = malloc(PA_SIZE);
-        __system_property_area_init(pa);
+    LocalPropertyTestState() : valid(false) {
+        char dir_template[] = "/data/nativetest/prop-XXXXXX";
+        char *dirname = mkdtemp(dir_template);
+        if (!dirname) {
+            perror("making temp file for test state failed (is /data/nativetest writable?)");
+            return;
+        }
+
+        for (size_t i = 0; i < PA_REGION_COUNT; i++) {
+            old_pa[i] = __system_property_regions__[i];
+            __system_property_regions__[i] = NULL;
+        }
+
+        pa_dirname = dirname;
+        pa_filename = pa_dirname + "/__properties__";
+
+        __system_property_set_filename(pa_filename.c_str());
+        __system_property_area_init();
+        valid = true;
     }
 
     ~LocalPropertyTestState() {
-        __system_property_area__ = old_pa;
-        free(pa);
+        if (!valid)
+            return;
+
+        for (size_t i = 0; i < PA_REGION_COUNT; i++) {
+            __system_property_regions__[i] = old_pa[i];
+        }
+
+        __system_property_set_filename(PROP_FILENAME);
+        unlink(pa_filename.c_str());
+        rmdir(pa_dirname.c_str());
     }
+public:
+    bool valid;
 private:
-    void *pa;
-    void *old_pa;
+    std::string pa_dirname;
+    std::string pa_filename;
+    void *old_pa[PA_REGION_COUNT];
 };
 
 TEST(properties, add) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
 
     char propvalue[PROP_VALUE_MAX];
 
@@ -61,6 +90,7 @@
 
 TEST(properties, update) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
 
     char propvalue[PROP_VALUE_MAX];
     prop_info *pi;
@@ -91,27 +121,34 @@
     ASSERT_STREQ(propvalue, "value6");
 }
 
-// 247 = max # of properties supported by current implementation
-// (this should never go down)
-TEST(properties, fill_247) {
+TEST(properties, fill) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
     char prop_name[PROP_NAME_MAX];
     char prop_value[PROP_VALUE_MAX];
     char prop_value_ret[PROP_VALUE_MAX];
+    int count = 0;
     int ret;
 
-    for (int i = 0; i < 247; i++) {
-        ret = snprintf(prop_name, PROP_NAME_MAX - 1, "property_%d", i);
+    while (true) {
+        ret = snprintf(prop_name, PROP_NAME_MAX - 1, "property_%d", count);
         memset(prop_name + ret, 'a', PROP_NAME_MAX - 1 - ret);
-        ret = snprintf(prop_value, PROP_VALUE_MAX - 1, "value_%d", i);
+        ret = snprintf(prop_value, PROP_VALUE_MAX - 1, "value_%d", count);
         memset(prop_value + ret, 'b', PROP_VALUE_MAX - 1 - ret);
         prop_name[PROP_NAME_MAX - 1] = 0;
         prop_value[PROP_VALUE_MAX - 1] = 0;
 
-        ASSERT_EQ(0, __system_property_add(prop_name, PROP_NAME_MAX - 1, prop_value, PROP_VALUE_MAX - 1));
+        ret = __system_property_add(prop_name, PROP_NAME_MAX - 1, prop_value, PROP_VALUE_MAX - 1);
+        if (ret < 0)
+            break;
+
+        count++;
     }
 
-    for (int i = 0; i < 247; i++) {
+    // For historical reasons at least 247 properties must be supported
+    ASSERT_GE(count, 247);
+
+    for (int i = 0; i < count; i++) {
         ret = snprintf(prop_name, PROP_NAME_MAX - 1, "property_%d", i);
         memset(prop_name + ret, 'a', PROP_NAME_MAX - 1 - ret);
         ret = snprintf(prop_value, PROP_VALUE_MAX - 1, "value_%d", i);
@@ -134,6 +171,7 @@
 
 TEST(properties, foreach) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
     size_t count = 0;
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
@@ -146,6 +184,7 @@
 
 TEST(properties, find_nth) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
     ASSERT_EQ(0, __system_property_add("other_property", 14, "value2", 6));
@@ -165,6 +204,7 @@
 
 TEST(properties, errors) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
     char prop_value[PROP_NAME_MAX];
 
     ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
@@ -181,6 +221,7 @@
 
 TEST(properties, serial) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
     const prop_info *pi;
     unsigned int serial;
 
@@ -206,6 +247,7 @@
 
 TEST(properties, wait) {
     LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
     unsigned int serial;
     prop_info *pi;
     pthread_t t;