toolbox: reimplement watchprops using __system_property_foreach()

Internally, replace the watchlist array with a hashmap since the array
assumes properties are enumerated in a consistent order and foreach()
probably won't.  (find_nth() never guaranteed this either but it usually
worked in practice.)

Signed-off-by: Greg Hackmann <ghackmann@google.com>

(cherry picked from commit 389e358017ad15485f2bd9120feebfffa489131b)

Change-Id: I1db3137b130474f4bb205f01f0bdcf37cf974764
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
index 3418d62..bf82882 100644
--- a/toolbox/watchprops.c
+++ b/toolbox/watchprops.c
@@ -1,32 +1,30 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
+#include <errno.h>
 
 #include <cutils/properties.h>
+#include <cutils/hashmap.h>
 
 #include <sys/atomics.h>
 
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-typedef struct pwatch pwatch;
-
-struct pwatch
+static int str_hash(void *key)
 {
-    const prop_info *pi;
-    unsigned serial;
-};
+    return hashmapHash(key, strlen(key));
+}
 
-static pwatch watchlist[1024];
-
-static void announce(const prop_info *pi)
+static bool str_equals(void *keyA, void *keyB)
 {
-    char name[PROP_NAME_MAX];
-    char value[PROP_VALUE_MAX];
+    return strcmp(keyA, keyB) == 0;
+}
+
+static void announce(char *name, char *value)
+{
     char *x;
     
-    __system_property_read(pi, name, value);
-
     for(x = value; *x; x++) {
         if((*x < 32) || (*x > 127)) *x = '.';
     }
@@ -34,42 +32,64 @@
     fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
 }
 
+static void add_to_watchlist(Hashmap *watchlist, const char *name,
+        const prop_info *pi)
+{
+    char *key = strdup(name);
+    unsigned *value = malloc(sizeof(unsigned));
+    if (!key || !value)
+        exit(1);
+
+    *value = __system_property_serial(pi);
+    hashmapPut(watchlist, key, value);
+}
+
+static void populate_watchlist(const prop_info *pi, void *cookie)
+{
+    Hashmap *watchlist = cookie;
+    char name[PROP_NAME_MAX];
+    char value_unused[PROP_VALUE_MAX];
+
+    __system_property_read(pi, name, value_unused);
+    add_to_watchlist(watchlist, name, pi);
+}
+
+static void update_watchlist(const prop_info *pi, void *cookie)
+{
+    Hashmap *watchlist = cookie;
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+    unsigned *serial;
+
+    __system_property_read(pi, name, value);
+    serial = hashmapGet(watchlist, name);
+    if (!serial) {
+        add_to_watchlist(watchlist, name, pi);
+        announce(name, value);
+    } else {
+        unsigned tmp = __system_property_serial(pi);
+        if (*serial != tmp) {
+            *serial = tmp;
+            announce(name, value);
+        }
+    }
+}
+
 int watchprops_main(int argc, char *argv[])
 {
     unsigned serial = 0;
-    unsigned count;
+    unsigned count = 0;
     unsigned n;
     
-    for(n = 0; n < 1024; n++) {
-        watchlist[n].pi = __system_property_find_nth(n);
-        if (watchlist[n].pi == 0)
-            break;
-        watchlist[n].serial = __system_property_serial(watchlist[n].pi);
-    }
-
-    count = n;
-    if (count == 1024)
+    Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
+    if (!watchlist)
         exit(1);
 
+    __system_property_foreach(populate_watchlist, watchlist);
+
     for(;;) {
         serial = __system_property_wait_any(serial);
-        while(count < 1024){
-            watchlist[count].pi = __system_property_find_nth(count);
-            if (watchlist[count].pi == 0)
-                break;
-            watchlist[count].serial = __system_property_serial(watchlist[n].pi);
-            announce(watchlist[count].pi);
-            count++;
-            if(count == 1024) exit(1);
-        }
-
-        for(n = 0; n < count; n++){
-            unsigned tmp = __system_property_serial(watchlist[n].pi);
-            if(watchlist[n].serial != tmp) {
-                announce(watchlist[n].pi);
-                watchlist[n].serial = tmp;
-            }
-        }
+        __system_property_foreach(update_watchlist, watchlist);
     }
     return 0;
 }