am b0739c66: Fix run-as which was broken in Android 4.3

* commit 'b0739c662db6a19b49c0912b865edb2853156bda':
  Fix run-as which was broken in Android 4.3
diff --git a/adb/adb.c b/adb/adb.c
index d2a2d27..ec74b49 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -401,6 +401,10 @@
         return "bootloader";
     case CS_DEVICE:
         return "device";
+    case CS_RECOVERY:
+        return "recovery";
+    case CS_SIDELOAD:
+        return "sideload";
     case CS_OFFLINE:
         return "offline";
     case CS_UNAUTHORIZED:
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index 64e393c..354d0fb 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -32,7 +32,7 @@
 #include "file_sync_service.h"
 
 
-static unsigned total_bytes;
+static unsigned long long total_bytes;
 static long long start_time;
 
 static long long NOW()
@@ -58,8 +58,8 @@
         t = 1000000;
 
     fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n",
-            ((((long long) total_bytes) * 1000000LL) / t) / 1024LL,
-            (long long) total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+            ((total_bytes * 1000000LL) / t) / 1024LL,
+            total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
 }
 
 void sync_quit(int fd)
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index df9b1d1..68bb232 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -137,6 +137,24 @@
 #define VENDOR_ID_HARRIS        0x19A5
 // OPPO's USB Vendor ID
 #define VENDOR_ID_OPPO          0x22D9
+// Xiaomi's USB Vendor ID
+#define VENDOR_ID_XIAOMI        0x2717
+// BYD's USB Vendor ID
+#define VENDOR_ID_BYD           0x19D1
+// OUYA's USB Vendor ID
+#define VENDOR_ID_OUYA          0x2836
+// Haier's USB Vendor ID
+#define VENDOR_ID_HAIER         0x201E
+// Hisense's USB Vendor ID
+#define VENDOR_ID_HISENSE       0x109b
+// MTK's USB Vendor ID
+#define VENDOR_ID_MTK           0x0e8d
+// B&N Nook's USB Vendor ID
+#define VENDOR_ID_NOOK          0x2080
+// Qisda's USB Vendor ID
+#define VENDOR_ID_QISDA         0x1D45
+// ECS's USB Vendor ID
+#define VENDOR_ID_ECS           0x03fc
 
 
 /** built-in vendor list */
@@ -192,6 +210,15 @@
     VENDOR_ID_ANYDATA,
     VENDOR_ID_HARRIS,
     VENDOR_ID_OPPO,
+    VENDOR_ID_XIAOMI,
+    VENDOR_ID_BYD,
+    VENDOR_ID_OUYA,
+    VENDOR_ID_HAIER,
+    VENDOR_ID_HISENSE,
+    VENDOR_ID_MTK,
+    VENDOR_ID_NOOK,
+    VENDOR_ID_QISDA,
+    VENDOR_ID_ECS,
 };
 
 #define BUILT_IN_VENDOR_COUNT    (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index 630d980..5ecb1a5 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -17,10 +17,11 @@
 
 #include <cutils/sockets.h>
 
+extern const char* __progname;
+
 void crash1(void);
 void crashnostack(void);
-void maybeabort(void);
-int do_action(const char* arg);
+static int do_action(const char* arg);
 
 static void debuggerd_connect()
 {
@@ -28,42 +29,46 @@
     int s;
     sprintf(tmp, "%d", gettid());
     s = socket_local_client("android:debuggerd",
-            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);    
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
     if(s >= 0) {
         read(s, tmp, 1);
         close(s);
     }
 }
 
-int smash_stack(int i) {
+static void maybeabort() {
+    if(time(0) != 42) {
+        abort();
+    }
+}
+
+static int smash_stack(int i) {
     printf("crasher: deliberately corrupting stack...\n");
     // Unless there's a "big enough" buffer on the stack, gcc
     // doesn't bother inserting checks.
     char buf[8];
-    // If we don't write something relatively unpredicatable
+    // If we don't write something relatively unpredictable
     // into the buffer and then do something with it, gcc
     // optimizes everything away and just returns a constant.
     *(int*)(&buf[7]) = (uintptr_t) &buf[0];
     return *(int*)(&buf[0]);
 }
 
-void test_call1()
+static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
+
+__attribute__((noinline)) static void overflow_stack(void* p) {
+    void* buf[1];
+    buf[0] = p;
+    global = buf;
+    overflow_stack(&buf);
+}
+
+static void test_call1()
 {
     *((int*) 32) = 1;
 }
 
-void *test_thread(void *x)
-{
-    printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid());
-
-    sleep(1);
-    test_call1();
-    printf("goodbye\n");
-
-    return 0;
-}
-
-void *noisy(void *x)
+static void *noisy(void *x)
 {
     char c = (unsigned) x;
     for(;;) {
@@ -74,7 +79,7 @@
     return 0;
 }
 
-int ctest()
+static int ctest()
 {
     pthread_t thr;
     pthread_attr_t attr;
@@ -92,7 +97,7 @@
     return (void*) do_action((const char*) raw_arg);
 }
 
-int do_action_on_thread(const char* arg)
+static int do_action_on_thread(const char* arg)
 {
     pthread_t t;
     pthread_create(&t, NULL, thread_callback, (void*) arg);
@@ -101,58 +106,75 @@
     return (int) result;
 }
 
-__attribute__((noinline)) int crash3(int a) {
-   *((int*) 0xdead) = a;
-   return a*4;
+__attribute__((noinline)) static int crash3(int a) {
+    *((int*) 0xdead) = a;
+    return a*4;
 }
 
-__attribute__((noinline)) int crash2(int a) {
-   a = crash3(a) + 2;
-   return a*3;
+__attribute__((noinline)) static int crash2(int a) {
+    a = crash3(a) + 2;
+    return a*3;
 }
 
-__attribute__((noinline)) int crash(int a) {
-   a = crash2(a) + 1;
-   return a*2;
+__attribute__((noinline)) static int crash(int a) {
+    a = crash2(a) + 1;
+    return a*2;
 }
 
-int do_action(const char* arg)
+static void abuse_heap() {
+    char buf[16];
+    free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
+}
+
+static int do_action(const char* arg)
 {
-    if(!strncmp(arg, "thread-", strlen("thread-"))) {
+    fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
+
+    if (!strncmp(arg, "thread-", strlen("thread-"))) {
         return do_action_on_thread(arg + strlen("thread-"));
+    } else if (!strcmp(arg,"smash-stack")) {
+        return smash_stack(42);
+    } else if (!strcmp(arg,"stack-overflow")) {
+        overflow_stack(NULL);
+    } else if (!strcmp(arg,"nostack")) {
+        crashnostack();
+    } else if (!strcmp(arg,"ctest")) {
+        return ctest();
+    } else if (!strcmp(arg,"exit")) {
+        exit(1);
+    } else if (!strcmp(arg,"crash")) {
+        return crash(42);
+    } else if (!strcmp(arg,"abort")) {
+        maybeabort();
+    } else if (!strcmp(arg, "heap-usage")) {
+        abuse_heap();
     }
 
-    if(!strcmp(arg,"smash-stack")) return smash_stack(42);
-    if(!strcmp(arg,"nostack")) crashnostack();
-    if(!strcmp(arg,"ctest")) return ctest();
-    if(!strcmp(arg,"exit")) exit(1);
-    if(!strcmp(arg,"crash")) return crash(42);
-    if(!strcmp(arg,"abort")) maybeabort();
-
-    pthread_t thr;
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create(&thr, &attr, test_thread, 0);
-    while(1) sleep(1);
+    fprintf(stderr, "%s OP\n", __progname);
+    fprintf(stderr, "where OP is:\n");
+    fprintf(stderr, "  smash-stack     overwrite a stack-guard canary\n");
+    fprintf(stderr, "  stack-overflow  recurse until the stack overflows\n");
+    fprintf(stderr, "  heap-corruption cause a libc abort by corrupting the heap\n");
+    fprintf(stderr, "  heap-usage      cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "  nostack         crash with a NULL stack pointer\n");
+    fprintf(stderr, "  ctest           (obsoleted by thread-crash?)\n");
+    fprintf(stderr, "  exit            call exit(1)\n");
+    fprintf(stderr, "  crash           cause a SIGSEGV\n");
+    fprintf(stderr, "  abort           call abort()\n");
+    fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
+    fprintf(stderr, "on the process' main thread.\n");
+    return EXIT_SUCCESS;
 }
 
 int main(int argc, char **argv)
 {
     fprintf(stderr,"crasher: built at " __TIME__ "!@\n");
-    fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
 
     if(argc > 1) {
         return do_action(argv[1]);
     } else {
         crash1();
-//        *((int*) 0) = 42;
     }
-    
-    return 0;
-}
 
-void maybeabort()
-{
-    if(time(0) != 42) abort();
+    return 0;
 }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 75dad49..f961b39 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -25,7 +25,7 @@
 
 #define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000"
 
-#define WAIT_TIMEOUT 5
+#define WAIT_TIMEOUT 20
 
 /* fstab has the following format:
  *
diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h
index c9b241d..14bfad6 100644
--- a/include/corkscrew/map_info.h
+++ b/include/corkscrew/map_info.h
@@ -63,6 +63,10 @@
  * previous acquired using acquire_my_map_info_list(). */
 void release_my_map_info_list(map_info_t* milist);
 
+/* Flushes the cached memory map so the next call to
+ * acquire_my_map_info_list() gets fresh data. */
+void flush_my_map_info_list();
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/init/builtins.c b/init/builtins.c
index 0f9f131..576f0d9 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -593,8 +593,7 @@
     struct service *svc;
     svc = service_find_by_name(args[1]);
     if (svc) {
-        service_stop(svc);
-        service_start(svc, NULL);
+        service_restart(svc);
     }
     return 0;
 }
diff --git a/init/devices.c b/init/devices.c
index cc350ef..1893642 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -127,6 +127,7 @@
     char buf[512];
     struct listnode *node;
     struct perms_ *dp;
+    char *secontext;
 
         /* upaths omit the "/sys" that paths in this list
          * contain, so we add 4 when comparing...
@@ -148,6 +149,14 @@
         INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
         chown(buf, dp->uid, dp->gid);
         chmod(buf, dp->perm);
+        if (sehandle) {
+            secontext = NULL;
+            selabel_lookup(sehandle, &secontext, buf, 0);
+            if (secontext) {
+                setfilecon(buf, secontext);
+                freecon(secontext);
+           }
+        }
     }
 }
 
@@ -442,6 +451,8 @@
     if (uevent->partition_name) {
         p = strdup(uevent->partition_name);
         sanitize(p);
+        if (strcmp(uevent->partition_name, p))
+            NOTICE("Linking partition '%s' as '%s'\n", uevent->partition_name, p);
         if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
             link_num++;
         else
diff --git a/init/init.c b/init/init.c
index 93b5997..28d2863 100755
--- a/init/init.c
+++ b/init/init.c
@@ -90,7 +90,7 @@
 }
 
 static int have_console;
-static char *console_name = "/dev/console";
+static char console_name[PROP_VALUE_MAX] = "/dev/console";
 static time_t process_needs_restart;
 
 static const char *ENV[32];
@@ -164,7 +164,7 @@
          * state and immediately takes it out of the restarting
          * state if it was in there
          */
-    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
+    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));
     svc->time_started = 0;
 
         /* running processes require no additional work -- if
@@ -359,15 +359,14 @@
         notify_service_state(svc->name, "running");
 }
 
-/* The how field should be either SVC_DISABLED or SVC_RESET */
+/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
 static void service_stop_or_reset(struct service *svc, int how)
 {
-        /* we are no longer running, nor should we
-         * attempt to restart
-         */
-    svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING));
+    /* The service is still SVC_RUNNING until its process exits, but if it has
+     * already exited it shoudn't attempt a restart yet. */
+    svc->flags &= (~SVC_RESTARTING);
 
-    if ((how != SVC_DISABLED) && (how != SVC_RESET)) {
+    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
         /* Hrm, an illegal flag.  Default to SVC_DISABLED */
         how = SVC_DISABLED;
     }
@@ -399,6 +398,17 @@
     service_stop_or_reset(svc, SVC_DISABLED);
 }
 
+void service_restart(struct service *svc)
+{
+    if (svc->flags & SVC_RUNNING) {
+        /* Stop, wait, then start the service. */
+        service_stop_or_reset(svc, SVC_RESTART);
+    } else if (!(svc->flags & SVC_RESTARTING)) {
+        /* Just start the service since it's not running. */
+        service_start(svc, NULL);
+    } /* else: Service is restarting anyways. */
+}
+
 void property_changed(const char *name, const char *value)
 {
     if (property_triggers_enabled)
@@ -430,7 +440,7 @@
 
 static void msg_start(const char *name)
 {
-    struct service *svc;
+    struct service *svc = NULL;
     char *tmp = NULL;
     char *args = NULL;
 
@@ -438,11 +448,13 @@
         svc = service_find_by_name(name);
     else {
         tmp = strdup(name);
-        args = strchr(tmp, ':');
-        *args = '\0';
-        args++;
+        if (tmp) {
+            args = strchr(tmp, ':');
+            *args = '\0';
+            args++;
 
-        svc = service_find_by_name(tmp);
+            svc = service_find_by_name(tmp);
+        }
     }
 
     if (svc) {
@@ -465,6 +477,17 @@
     }
 }
 
+static void msg_restart(const char *name)
+{
+    struct service *svc = service_find_by_name(name);
+
+    if (svc) {
+        service_restart(svc);
+    } else {
+        ERROR("no such service '%s'\n", name);
+    }
+}
+
 void handle_control_message(const char *msg, const char *arg)
 {
     if (!strcmp(msg,"start")) {
@@ -472,8 +495,7 @@
     } else if (!strcmp(msg,"stop")) {
         msg_stop(arg);
     } else if (!strcmp(msg,"restart")) {
-        msg_stop(arg);
-        msg_start(arg);
+        msg_restart(arg);
     } else {
         ERROR("unknown control msg '%s'\n", msg);
     }
@@ -547,11 +569,9 @@
 static int console_init_action(int nargs, char **args)
 {
     int fd;
-    char tmp[PROP_VALUE_MAX];
 
     if (console[0]) {
-        snprintf(tmp, sizeof(tmp), "/dev/%s", console);
-        console_name = strdup(tmp);
+        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
     }
 
     fd = open(console_name, O_RDWR);
@@ -625,7 +645,7 @@
 static void export_kernel_boot_props(void)
 {
     char tmp[PROP_VALUE_MAX];
-    const char *pval;
+    int ret;
     unsigned i;
     struct {
         const char *src_prop;
@@ -639,22 +659,26 @@
     };
 
     for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
-        pval = property_get(prop_map[i].src_prop);
-        property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);
+        ret = property_get(prop_map[i].src_prop, tmp);
+        if (ret > 0)
+            property_set(prop_map[i].dest_prop, tmp);
+        else
+            property_set(prop_map[i].dest_prop, prop_map[i].def_val);
     }
 
-    pval = property_get("ro.boot.console");
-    if (pval)
-        strlcpy(console, pval, sizeof(console));
+    ret = property_get("ro.boot.console", tmp);
+    if (ret)
+        strlcpy(console, tmp, sizeof(console));
 
     /* save a copy for init's usage during boot */
-    strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));
+    property_get("ro.bootmode", tmp);
+    strlcpy(bootmode, tmp, sizeof(bootmode));
 
     /* if this was given on kernel command line, override what we read
      * before (e.g. from /proc/cpuinfo), if anything */
-    pval = property_get("ro.boot.hardware");
-    if (pval)
-        strlcpy(hardware, pval, sizeof(hardware));
+    ret = property_get("ro.boot.hardware", tmp);
+    if (ret)
+        strlcpy(hardware, tmp, sizeof(hardware));
     property_set("ro.hardware", hardware);
 
     snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
diff --git a/init/init.h b/init/init.h
index 955e1f0..aa6a4ab 100644
--- a/init/init.h
+++ b/init/init.h
@@ -72,6 +72,7 @@
 #define SVC_RESET       0x40  /* Use when stopping a process, but not disabling
                                  so it can be restarted with its class */
 #define SVC_RC_DISABLED 0x80  /* Remember if the disabled flag was set in the rc script */
+#define SVC_RESTART     0x100 /* Use to safely restart (stop, wait, start) a service */
 
 #define NR_SVC_SUPP_GIDS 12    /* twelve supplementary groups */
 
@@ -127,6 +128,7 @@
                             void (*func)(struct service *svc));
 void service_stop(struct service *svc);
 void service_reset(struct service *svc);
+void service_restart(struct service *svc);
 void service_start(struct service *svc, const char *dynamic_args);
 void property_changed(const char *name, const char *value);
 
diff --git a/init/init_parser.c b/init/init_parser.c
index 686640e..cce1093 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -206,8 +206,9 @@
     while (*src_ptr && left > 0) {
         char *c;
         char prop[PROP_NAME_MAX + 1];
-        const char *prop_val;
+        char prop_val[PROP_VALUE_MAX];
         int prop_len = 0;
+        int prop_val_len;
 
         c = strchr(src_ptr, '$');
         if (!c) {
@@ -265,14 +266,14 @@
             goto err;
         }
 
-        prop_val = property_get(prop);
-        if (!prop_val) {
+        prop_val_len = property_get(prop, prop_val);
+        if (!prop_val_len) {
             ERROR("property '%s' doesn't exist while expanding '%s'\n",
                   prop, src);
             goto err;
         }
 
-        ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val));
+        ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len);
         if (ret < 0)
             goto err_nospace;
         src_ptr = c;
@@ -543,7 +544,7 @@
             const char* equals = strchr(name, '=');
             if (equals) {
                 char prop_name[PROP_NAME_MAX + 1];
-                const char* value;
+                char value[PROP_VALUE_MAX];
                 int length = equals - name;
                 if (length > PROP_NAME_MAX) {
                     ERROR("property name too long in trigger %s", act->name);
@@ -552,9 +553,8 @@
                     prop_name[length] = 0;
 
                     /* does the property exist, and match the trigger value? */
-                    value = property_get(prop_name);
-                    if (value && (!strcmp(equals + 1, value) ||
-                                  !strcmp(equals + 1, "*"))) {
+                    property_get(prop_name, value);
+                    if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) {
                         action_add_queue_tail(act);
                     }
                 }
diff --git a/init/keychords.c b/init/keychords.c
index 061d157..4a64042 100644
--- a/init/keychords.c
+++ b/init/keychords.c
@@ -95,20 +95,19 @@
 void handle_keychord()
 {
     struct service *svc;
-    const char* debuggable;
-    const char* adb_enabled;
+    char adb_enabled[PROP_VALUE_MAX];
     int ret;
     __u16 id;
 
     // Only handle keychords if adb is enabled.
-    adb_enabled = property_get("init.svc.adbd");
+    property_get("init.svc.adbd", adb_enabled);
     ret = read(keychord_fd, &id, sizeof(id));
     if (ret != sizeof(id)) {
         ERROR("could not read keychord id\n");
         return;
     }
 
-    if ((adb_enabled && !strcmp(adb_enabled, "running"))) {
+    if (!strcmp(adb_enabled, "running")) {
         svc = service_find_by_keychord(id);
         if (svc) {
             INFO("starting service %s from keychord\n", svc->name);
diff --git a/init/property_service.c b/init/property_service.c
index 64f6b40..6c71807 100755
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -111,7 +111,6 @@
 };
 
 typedef struct {
-    void *data;
     size_t size;
     int fd;
 } workspace;
@@ -119,83 +118,34 @@
 static int init_workspace(workspace *w, size_t size)
 {
     void *data;
-    int fd;
-
-        /* dev is a tmpfs that we can use to carve a shared workspace
-         * out of, so let's do that...
-         */
-    fd = open(PROP_FILENAME, O_RDWR | O_CREAT | O_NOFOLLOW, 0644);
+    int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
     if (fd < 0)
         return -1;
 
-    if (ftruncate(fd, size) < 0)
-        goto out;
-
-    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-    if(data == MAP_FAILED)
-        goto out;
-
-    close(fd);
-
-    fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW);
-    if (fd < 0)
-        return -1;
-
-    w->data = data;
     w->size = size;
     w->fd = fd;
     return 0;
-
-out:
-    close(fd);
-    return -1;
 }
 
-/* (8 header words + 372 toc words) = 1520 bytes */
-/* 1536 bytes header and toc + 372 prop_infos @ 128 bytes = 49152 bytes */
-
-#define PA_COUNT_MAX  372
-#define PA_INFO_START 1536
-#define PA_SIZE       49152
-
 static workspace pa_workspace;
-static prop_info *pa_info_array;
-
-extern prop_area *__system_property_area__;
 
 static int init_property_area(void)
 {
-    prop_area *pa;
-
-    if(pa_info_array)
+    if (property_area_inited)
         return -1;
 
-    if(init_workspace(&pa_workspace, PA_SIZE))
+    if(__system_property_area_init())
+        return -1;
+
+    if(init_workspace(&pa_workspace, 0))
         return -1;
 
     fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
 
-    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
-
-    pa = pa_workspace.data;
-    memset(pa, 0, PA_SIZE);
-    pa->magic = PROP_AREA_MAGIC;
-    pa->version = PROP_AREA_VERSION;
-
-        /* plug into the lib property services */
-    __system_property_area__ = pa;
     property_area_inited = 1;
     return 0;
 }
 
-static void update_prop_info(prop_info *pi, const char *value, unsigned len)
-{
-    pi->serial = pi->serial | 1;
-    memcpy(pi->value, value, len + 1);
-    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
-    __futex_wake(&pi->serial, INT32_MAX);
-}
-
 static int check_mac_perms(const char *name, char *sctx)
 {
     if (is_selinux_enabled() <= 0)
@@ -298,19 +248,9 @@
     return 0;
 }
 
-const char* property_get(const char *name)
+int __property_get(const char *name, char *value)
 {
-    prop_info *pi;
-
-    if(strlen(name) >= PROP_NAME_MAX) return 0;
-
-    pi = (prop_info*) __system_property_find(name);
-
-    if(pi != 0) {
-        return pi->value;
-    } else {
-        return 0;
-    }
+    return __system_property_get(name, value);
 }
 
 static void write_persistent_property(const char *name, const char *value)
@@ -337,8 +277,8 @@
 
 int property_set(const char *name, const char *value)
 {
-    prop_area *pa;
     prop_info *pi;
+    int ret;
 
     size_t namelen = strlen(name);
     size_t valuelen = strlen(value);
@@ -353,25 +293,13 @@
         /* ro.* properties may NEVER be modified once set */
         if(!strncmp(name, "ro.", 3)) return -1;
 
-        pa = __system_property_area__;
-        update_prop_info(pi, value, valuelen);
-        pa->serial++;
-        __futex_wake(&pa->serial, INT32_MAX);
+        __system_property_update(pi, value, valuelen);
     } else {
-        pa = __system_property_area__;
-        if(pa->count == PA_COUNT_MAX) return -1;
-
-        pi = pa_info_array + pa->count;
-        pi->serial = (valuelen << 24);
-        memcpy(pi->name, name, namelen + 1);
-        memcpy(pi->value, value, valuelen + 1);
-
-        pa->toc[pa->count] =
-            (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
-
-        pa->count++;
-        pa->serial++;
-        __futex_wake(&pa->serial, INT32_MAX);
+        ret = __system_property_add(name, namelen, value, valuelen);
+        if (ret < 0) {
+            ERROR("Failed to set '%s'='%s'", name, value);
+            return ret;
+        }
     }
     /* If name starts with "net." treat as a DNS property. */
     if (strncmp("net.", name, strlen("net.")) == 0)  {
@@ -593,8 +521,11 @@
 
 static void load_override_properties() {
 #ifdef ALLOW_LOCAL_PROP_OVERRIDE
-    const char *debuggable = property_get("ro.debuggable");
-    if (debuggable && (strcmp(debuggable, "1") == 0)) {
+    char debuggable[PROP_VALUE_MAX];
+    int ret;
+
+    ret = property_get("ro.debuggable", debuggable);
+    if (ret && (strcmp(debuggable, "1") == 0)) {
         load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
     }
 #endif /* ALLOW_LOCAL_PROP_OVERRIDE */
diff --git a/init/property_service.h b/init/property_service.h
index b9d1bf6..46cbd8f 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,6 +18,7 @@
 #define _INIT_PROPERTY_H
 
 #include <stdbool.h>
+#include <sys/system_properties.h>
 
 extern void handle_property_set_fd(void);
 extern void property_init(void);
@@ -25,9 +26,25 @@
 extern void load_persist_props(void);
 extern void start_property_service(void);
 void get_property_workspace(int *fd, int *sz);
-extern const char* property_get(const char *name);
+extern int __property_get(const char *name, char *value);
 extern int property_set(const char *name, const char *value);
 extern int properties_inited();
 int get_property_set_fd(void);
 
+extern void __property_get_size_error()
+    __attribute__((__error__("property_get called with too small buffer")));
+
+static inline
+__attribute__ ((always_inline))
+__attribute__ ((gnu_inline))
+__attribute__ ((artificial))
+int property_get(const char *name, char *value)
+{
+    size_t value_len = __builtin_object_size(value, 0);
+    if (value_len != PROP_VALUE_MAX)
+        __property_get_size_error();
+
+    return __property_get(name, value);
+}
+
 #endif	/* _INIT_PROPERTY_H */
diff --git a/init/signal_handler.c b/init/signal_handler.c
index abccb40..d31ad63 100644
--- a/init/signal_handler.c
+++ b/init/signal_handler.c
@@ -63,7 +63,7 @@
 
     NOTICE("process '%s', pid %d exited\n", svc->name, pid);
 
-    if (!(svc->flags & SVC_ONESHOT)) {
+    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
         kill(-pid, SIGKILL);
         NOTICE("process '%s' killing any children in process group\n", svc->name);
     }
@@ -78,8 +78,9 @@
     svc->pid = 0;
     svc->flags &= (~SVC_RUNNING);
 
-        /* oneshot processes go into the disabled state on exit */
-    if (svc->flags & SVC_ONESHOT) {
+        /* oneshot processes go into the disabled state on exit,
+         * except when manually restarted. */
+    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
         svc->flags |= SVC_DISABLED;
     }
 
@@ -90,7 +91,7 @@
     }
 
     now = gettime();
-    if (svc->flags & SVC_CRITICAL) {
+    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
         if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
             if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
                 ERROR("critical process '%s' exited %d times in %d minutes; "
@@ -105,6 +106,7 @@
         }
     }
 
+    svc->flags &= (~SVC_RESTART);
     svc->flags |= SVC_RESTARTING;
 
     /* Execute all onrestart commands for this service. */
diff --git a/init/util.c b/init/util.c
index 918bc05..76af9e5 100755
--- a/init/util.c
+++ b/init/util.c
@@ -305,14 +305,27 @@
     return 0;
 }
 
+/*
+ * replaces any unacceptable characters with '_', the
+ * length of the resulting string is equal to the input string
+ */
 void sanitize(char *s)
 {
+    const char* accept =
+            "abcdefghijklmnopqrstuvwxyz"
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            "0123456789"
+            "_-.";
+
     if (!s)
         return;
-    while (isalnum(*s))
-        s++;
-    *s = 0;
+
+    for (; *s; s++) {
+        s += strspn(s, accept);
+        if (*s) *s = '_';
+    }
 }
+
 void make_link(const char *oldpath, const char *newpath)
 {
     int ret;
diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk
index 8370789..d62c2d5 100644
--- a/libcorkscrew/Android.mk
+++ b/libcorkscrew/Android.mk
@@ -59,23 +59,28 @@
 
 # Build test.
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := test.c
-LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions
+LOCAL_SRC_FILES := test.cpp
+LOCAL_CFLAGS += -Werror -fno-inline-small-functions
 LOCAL_SHARED_LIBRARIES := libcorkscrew
 LOCAL_MODULE := libcorkscrew_test
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
 
 
+# TODO: reenable darwin-x86
+# ifeq ($(HOST_ARCH),x86)
 ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
 
 # Build libcorkscrew.
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files)
 LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
-LOCAL_SHARED_LIBRARIES += libgccdemangle
 LOCAL_STATIC_LIBRARIES += libcutils liblog
-LOCAL_LDLIBS += -ldl -lrt
+LOCAL_LDLIBS += -ldl
+ifeq ($(HOST_OS),linux)
+  LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux?
+  LOCAL_LDLIBS += -lrt
+endif
 LOCAL_CFLAGS += -std=gnu99 -Werror
 LOCAL_MODULE := libcorkscrew
 LOCAL_MODULE_TAGS := optional
@@ -83,11 +88,11 @@
 
 # Build test.
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := test.c
-LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions
+LOCAL_SRC_FILES := test.cpp
+LOCAL_CFLAGS += -Werror
 LOCAL_SHARED_LIBRARIES := libcorkscrew
 LOCAL_MODULE := libcorkscrew_test
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_HOST_EXECUTABLE)
 
-endif # linux-x86
+endif # HOST_ARCH == x86
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index 29159ed..e133ab6 100755
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -75,13 +75,18 @@
 
 #endif /* __BIONIC_HAVE_UCONTEXT_T */
 
-#else /* __BIONIC__ */
+#elif defined(__APPLE__)
+
+#define _XOPEN_SOURCE
+#include <ucontext.h>
+
+#else
 
 // glibc has its own renaming of the Linux kernel's structures.
 #define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP.
 #include <ucontext.h>
 
-#endif /* __ BIONIC__ */
+#endif
 
 /* Unwind state. */
 typedef struct {
@@ -819,9 +824,15 @@
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
     unwind_state_t state;
+#if defined(__APPLE__)
+    state.reg[DWARF_EBP] = uc->uc_mcontext->__ss.__ebp;
+    state.reg[DWARF_ESP] = uc->uc_mcontext->__ss.__esp;
+    state.reg[DWARF_EIP] = uc->uc_mcontext->__ss.__eip;
+#else
     state.reg[DWARF_EBP] = uc->uc_mcontext.gregs[REG_EBP];
     state.reg[DWARF_ESP] = uc->uc_mcontext.gregs[REG_ESP];
     state.reg[DWARF_EIP] = uc->uc_mcontext.gregs[REG_EIP];
+#endif
 
     memory_t memory;
     init_memory(&memory, map_info_list);
@@ -831,6 +842,9 @@
 
 ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
         backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
+#if defined(__APPLE__)
+    return -1;
+#else
     pt_regs_x86_t regs;
     if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
         return -1;
@@ -845,4 +859,5 @@
     init_memory_ptrace(&memory, tid);
     return unwind_backtrace_common(&memory, context->map_info_list,
             &state, backtrace, ignore_depth, max_depth);
+#endif
 }
diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c
index 03dbd53..b365e5b 100644
--- a/libcorkscrew/backtrace.c
+++ b/libcorkscrew/backtrace.c
@@ -33,7 +33,6 @@
 #include <unwind.h>
 #include <cutils/log.h>
 #include <cutils/atomic.h>
-#include <elf.h>
 
 #define __USE_GNU // For dladdr(3) in glibc.
 #include <dlfcn.h>
@@ -43,6 +42,15 @@
 // Bionic implements and exports gettid but only implements tgkill.
 extern int tgkill(int tgid, int tid, int sig);
 
+#elif defined(__APPLE__)
+
+#include <sys/syscall.h>
+
+// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
+static pid_t gettid() {
+  return syscall(SYS_thread_selfid);
+}
+
 #else
 
 // glibc doesn't implement or export either gettid or tgkill.
@@ -97,7 +105,7 @@
     state.returned_frames = 0;
     init_memory(&state.memory, milist);
 
-    _Unwind_Reason_Code rc =_Unwind_Backtrace(unwind_backtrace_callback, &state);
+    _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, &state);
 
     release_my_map_info_list(milist);
 
@@ -146,7 +154,9 @@
 
     ALOGV("Unwinding thread %d from thread %d.", tid, gettid());
 
-#ifdef CORKSCREW_HAVE_ARCH
+    // TODO: there's no tgkill(2) on Mac OS, so we'd either need the
+    // mach_port_t or the pthread_t rather than the tid.
+#if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__)
     struct sigaction act;
     struct sigaction oact;
     memset(&act, 0, sizeof(act));
@@ -305,20 +315,20 @@
         const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize) {
     const char* mapName = symbol->map_name ? symbol->map_name : "<unknown>";
     const char* symbolName = symbol->demangled_name ? symbol->demangled_name : symbol->symbol_name;
-    size_t fieldWidth = (bufferSize - 80) / 2;
+    int fieldWidth = (bufferSize - 80) / 2;
     if (symbolName) {
         uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
         if (pc_offset) {
-            snprintf(buffer, bufferSize, "#%02d  pc %08x  %.*s (%.*s+%u)",
-                    frameNumber, symbol->relative_pc, fieldWidth, mapName,
+            snprintf(buffer, bufferSize, "#%02u  pc %p  %.*s (%.*s+%u)",
+                    frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName,
                     fieldWidth, symbolName, pc_offset);
         } else {
-            snprintf(buffer, bufferSize, "#%02d  pc %08x  %.*s (%.*s)",
-                    frameNumber, symbol->relative_pc, fieldWidth, mapName,
+            snprintf(buffer, bufferSize, "#%02u  pc %p  %.*s (%.*s)",
+                    frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName,
                     fieldWidth, symbolName);
         }
     } else {
-        snprintf(buffer, bufferSize, "#%02d  pc %08x  %.*s",
-                frameNumber, symbol->relative_pc, fieldWidth, mapName);
+        snprintf(buffer, bufferSize, "#%02u  pc %p  %.*s",
+                frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName);
     }
 }
diff --git a/libcorkscrew/demangle.c b/libcorkscrew/demangle.c
index 54247cb..30ab1b0 100644
--- a/libcorkscrew/demangle.c
+++ b/libcorkscrew/demangle.c
@@ -25,6 +25,12 @@
                              int *status);
 
 char* demangle_symbol_name(const char* name) {
+#if defined(__APPLE__)
+    // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
+    if (name != NULL && name[0] != '_') {
+        return NULL;
+    }
+#endif
     // __cxa_demangle handles NULL by returning NULL
     return __cxa_demangle(name, 0, 0, 0);
 }
diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c
index 6a27664..93dffbf 100644
--- a/libcorkscrew/map_info.c
+++ b/libcorkscrew/map_info.c
@@ -29,6 +29,67 @@
 #include <cutils/log.h>
 #include <sys/time.h>
 
+#if defined(__APPLE__)
+
+// Mac OS vmmap(1) output:
+// __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW  /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+// 012345678901234567890123456789012345678901234567890123456789
+// 0         1         2         3         4         5
+static map_info_t* parse_vmmap_line(const char* line) {
+    unsigned long int start;
+    unsigned long int end;
+    char permissions[4];
+    int name_pos;
+    if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c  %n",
+               &start, &end, permissions, &name_pos) != 3) {
+        return NULL;
+    }
+
+    const char* name = line + name_pos;
+    size_t name_len = strlen(name);
+
+    map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len);
+    if (mi != NULL) {
+        mi->start = start;
+        mi->end = end;
+        mi->is_readable = permissions[0] == 'r';
+        mi->is_writable = permissions[1] == 'w';
+        mi->is_executable = permissions[2] == 'x';
+        mi->data = NULL;
+        memcpy(mi->name, name, name_len);
+        mi->name[name_len - 1] = '\0';
+        ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
+              "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
+              mi->start, mi->end,
+              mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+    }
+    return mi;
+}
+
+map_info_t* load_map_info_list(pid_t pid) {
+    char cmd[1024];
+    snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
+    FILE* fp = popen(cmd, "r");
+    if (fp == NULL) {
+        return NULL;
+    }
+
+    char line[1024];
+    map_info_t* milist = NULL;
+    while (fgets(line, sizeof(line), fp) != NULL) {
+        map_info_t* mi = parse_vmmap_line(line);
+        if (mi != NULL) {
+            mi->next = milist;
+            milist = mi;
+        }
+    }
+    pclose(fp);
+    return milist;
+}
+
+#else
+
+// Linux /proc/<pid>/maps lines:
 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so\n
 // 012345678901234567890123456789012345678901234567890123456789
 // 0         1         2         3         4         5
@@ -63,9 +124,9 @@
         memcpy(mi->name, name, name_len);
         mi->name[name_len] = '\0';
         ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
-                "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
-                mi->start, mi->end,
-                mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
+              "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
+              mi->start, mi->end,
+              mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
     }
     return mi;
 }
@@ -91,6 +152,8 @@
     return milist;
 }
 
+#endif
+
 void free_map_info_list(map_info_t* milist) {
     while (milist) {
         map_info_t* next = milist->next;
@@ -132,11 +195,17 @@
     int64_t timestamp;
 } my_map_info_data_t;
 
-static int64_t now() {
+static int64_t now_ns() {
+#if defined(HAVE_POSIX_CLOCKS)
     struct timespec t;
     t.tv_sec = t.tv_nsec = 0;
     clock_gettime(CLOCK_MONOTONIC, &t);
     return t.tv_sec * 1000000000LL + t.tv_nsec;
+#else
+    struct timeval t;
+    gettimeofday(&t, NULL);
+    return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL;
+#endif
 }
 
 static void dec_ref(map_info_t* milist, my_map_info_data_t* data) {
@@ -150,8 +219,8 @@
 map_info_t* acquire_my_map_info_list() {
     pthread_mutex_lock(&g_my_map_info_list_mutex);
 
-    int64_t time = now();
-    if (g_my_map_info_list) {
+    int64_t time = now_ns();
+    if (g_my_map_info_list != NULL) {
         my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
         int64_t age = time - data->timestamp;
         if (age >= MAX_CACHE_AGE) {
@@ -163,10 +232,10 @@
         }
     }
 
-    if (!g_my_map_info_list) {
+    if (g_my_map_info_list == NULL) {
         my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t));
         g_my_map_info_list = load_map_info_list(getpid());
-        if (g_my_map_info_list) {
+        if (g_my_map_info_list != NULL) {
             ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list);
             g_my_map_info_list->data = data;
             data->refs = 1;
@@ -196,3 +265,15 @@
         pthread_mutex_unlock(&g_my_map_info_list_mutex);
     }
 }
+
+void flush_my_map_info_list() {
+    pthread_mutex_lock(&g_my_map_info_list_mutex);
+
+    if (g_my_map_info_list != NULL) {
+        my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data;
+        dec_ref(g_my_map_info_list, data);
+        g_my_map_info_list = NULL;
+    }
+
+    pthread_mutex_unlock(&g_my_map_info_list_mutex);
+}
diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c
index 776ef69..be58f7f 100644
--- a/libcorkscrew/ptrace.c
+++ b/libcorkscrew/ptrace.c
@@ -46,21 +46,25 @@
 }
 
 bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) {
-    ALOGV("try_get_word: reading word at 0x%08x", ptr);
+    ALOGV("try_get_word: reading word at %p", (void*) ptr);
     if (ptr & 3) {
-        ALOGV("try_get_word: invalid pointer 0x%08x", ptr);
+        ALOGV("try_get_word: invalid pointer %p", (void*) ptr);
         *out_value = 0xffffffffL;
         return false;
     }
     if (memory->tid < 0) {
         if (!is_readable_map(memory->map_info_list, ptr)) {
-            ALOGV("try_get_word: pointer 0x%08x not in a readable map", ptr);
+            ALOGV("try_get_word: pointer %p not in a readable map", (void*) ptr);
             *out_value = 0xffffffffL;
             return false;
         }
         *out_value = *(uint32_t*)ptr;
         return true;
     } else {
+#if defined(__APPLE__)
+        ALOGV("no ptrace on Mac OS");
+        return false;
+#else
         // ptrace() returns -1 and sets errno when the operation fails.
         // To disambiguate -1 from a valid result, we clear errno beforehand.
         errno = 0;
@@ -71,6 +75,7 @@
             return false;
         }
         return true;
+#endif
     }
 }
 
diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c
index 29e4a79..982ccc8 100644
--- a/libcorkscrew/symbol_table.c
+++ b/libcorkscrew/symbol_table.c
@@ -21,13 +21,17 @@
 
 #include <stdbool.h>
 #include <stdlib.h>
-#include <elf.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <cutils/log.h>
 
+#if defined(__APPLE__)
+#else
+
+#include <elf.h>
+
 static bool is_elf(Elf32_Ehdr* e) {
     return (e->e_ident[EI_MAG0] == ELFMAG0 &&
             e->e_ident[EI_MAG1] == ELFMAG1 &&
@@ -35,6 +39,8 @@
             e->e_ident[EI_MAG3] == ELFMAG3);
 }
 
+#endif
+
 // Compare function for qsort
 static int qcompar(const void *a, const void *b) {
     const symbol_t* asym = (const symbol_t*)a;
@@ -55,6 +61,7 @@
 
 symbol_table_t* load_symbol_table(const char *filename) {
     symbol_table_t* table = NULL;
+#if !defined(__APPLE__)
     ALOGV("Loading symbol table from '%s'.", filename);
 
     int fd = open(filename, O_RDONLY);
@@ -197,6 +204,7 @@
 
 out_close:
     close(fd);
+#endif
 
 out:
     return table;
diff --git a/libcorkscrew/test.c b/libcorkscrew/test.cpp
similarity index 77%
rename from libcorkscrew/test.c
rename to libcorkscrew/test.cpp
index af34c03..22dfa7d 100644
--- a/libcorkscrew/test.c
+++ b/libcorkscrew/test.cpp
@@ -3,11 +3,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-void do_backtrace() {
+int do_backtrace(float /* just to test demangling */) {
   const size_t MAX_DEPTH = 32;
   backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH);
   ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH);
   fprintf(stderr, "frame_count=%d\n", (int) frame_count);
+  if (frame_count <= 0) {
+    return frame_count;
+  }
 
   backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count);
   get_backtrace_symbols(frames, frame_count, backtrace_symbols);
@@ -31,7 +34,7 @@
         symbol = find_symbol(symbols, frames[i].absolute_pc);
       }
       if (symbol != NULL) {
-        uintptr_t offset = frames[i].absolute_pc - symbol->start;
+        int offset = frames[i].absolute_pc - symbol->start;
         fprintf(stderr, "  %s (%s%+d)\n", line, symbol->name, offset);
       } else {
         fprintf(stderr, "  %s (\?\?\?)\n", line);
@@ -43,22 +46,31 @@
   free_backtrace_symbols(backtrace_symbols, frame_count);
   free(backtrace_symbols);
   free(frames);
+  return frame_count;
 }
 
-__attribute__ ((noinline)) void g() {
-  fprintf(stderr, "g()\n");
-  do_backtrace();
-}
+struct C {
+  int g(int i);
+};
 
-__attribute__ ((noinline)) int f(int i) {
-  fprintf(stderr, "f(%i)\n", i);
+__attribute__ ((noinline)) int C::g(int i) {
   if (i == 0) {
-    g();
-    return 0;
+    return do_backtrace(0.1);
   }
-  return f(i - 1);
+  return g(i - 1);
+}
+
+extern "C" __attribute__ ((noinline)) int f() {
+  C c;
+  return c.g(5);
 }
 
 int main() {
-  return f(5);
+  flush_my_map_info_list();
+  f();
+
+  flush_my_map_info_list();
+  f();
+
+  return 0;
 }
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 4056e8a..5037705 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -131,7 +131,11 @@
 LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32
 LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c
 else # !x86-atom
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_SRC_FILES += arch-mips/android_memset.c
+else # !mips
 LOCAL_SRC_FILES += memory.c
+endif # !mips
 endif # !x86-atom
 endif # !arm
 
diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c
new file mode 100644
index 0000000..bbc99fe
--- /dev/null
+++ b/libcutils/arch-mips/android_memset.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 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 <cutils/memory.h>
+
+/* Use mips-assembler versions supplied by bionic/libc/arch-mips/string/memset.S: */
+void _memset16(uint16_t* dst, uint16_t value, size_t size);
+void _memset32(uint32_t* dst, uint32_t value, size_t size);
+
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+    _memset16(dst, value, size);
+}
+
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+    _memset32(dst, value, size);
+}
diff --git a/libcutils/properties.c b/libcutils/properties.c
index f732ec0..28d8b2f 100644
--- a/libcutils/properties.c
+++ b/libcutils/properties.c
@@ -52,19 +52,28 @@
     return len;
 }
 
-int property_list(void (*propfn)(const char *key, const char *value, void *cookie), 
-                  void *cookie)
+struct property_list_callback_data
+{
+    void (*propfn)(const char *key, const char *value, void *cookie);
+    void *cookie;
+};
+
+static void property_list_callback(const prop_info *pi, void *cookie)
 {
     char name[PROP_NAME_MAX];
     char value[PROP_VALUE_MAX];
-    const prop_info *pi;
-    unsigned n;
-    
-    for(n = 0; (pi = __system_property_find_nth(n)); n++) {
-        __system_property_read(pi, name, value);
-        propfn(name, value, cookie);
-    }
-    return 0;
+    struct property_list_callback_data *data = cookie;
+
+    __system_property_read(pi, name, value);
+    data->propfn(name, value, data->cookie);
+}
+
+int property_list(
+        void (*propfn)(const char *key, const char *value, void *cookie),
+        void *cookie)
+{
+    struct property_list_callback_data data = { propfn, cookie };
+    return __system_property_foreach(property_list_callback, &data);
 }
 
 #elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
index bd48eff..da00f74 100644
--- a/libnetutils/dhcp_utils.c
+++ b/libnetutils/dhcp_utils.c
@@ -365,9 +365,8 @@
         return -1;
     }
     if (strcmp(prop_value, "ok") == 0) {
-        fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
+        return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
                 server, lease, vendorInfo, domain);
-        return 0;
     } else {
         snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
         return -1;
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index b5ae419..5014e4a 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -675,6 +675,9 @@
 	} else {
 		out = output_file_new_normal();
 	}
+	if (!out) {
+		return NULL;
+	}
 
 	out->ops->open(out, fd);
 
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 3d4984d..ae0e077 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -112,6 +112,12 @@
     char *result = (char *)malloc(len * 2 + 3);
     char *current = result;
     const char *end = arg + len;
+    char *oldresult;
+
+    if(result == NULL) {
+        SLOGW("malloc error (%s)", strerror(errno));
+        return NULL;
+    }
 
     *(current++) = '"';
     while (arg < end) {
@@ -125,8 +131,9 @@
     }
     *(current++) = '"';
     *(current++) = '\0';
+    oldresult = result; // save pointer in case realloc fails
     result = (char *)realloc(result, current-result);
-    return result;
+    return result ? result : oldresult;
 }
 
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1e6ad67..a7057c8 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -37,7 +37,7 @@
     export ANDROID_STORAGE /storage
     export ASEC_MOUNTPOINT /mnt/asec
     export LOOP_MOUNTPOINT /mnt/obb
-    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar
+    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar
 
 # Backward compatibility
     symlink /system/etc /etc
@@ -131,14 +131,6 @@
 # This is needed by any process that uses socket tagging.
     chmod 0644 /dev/xt_qtaguid
 
-on fs
-# mount mtd partitions
-    # Mount /system rw first to give the filesystem a chance to save a checkpoint
-    mount yaffs2 mtd@system /system
-    mount yaffs2 mtd@system /system ro remount
-    mount yaffs2 mtd@userdata /data nosuid nodev
-    mount yaffs2 mtd@cache /cache nosuid nodev
-
 on post-fs
     # once everything is setup, no need to modify /
     mount rootfs rootfs / ro remount
@@ -252,7 +244,7 @@
     symlink /data/data/com.android.shell/files/bugreports /data/bugreports
 
     # Separate location for storing security policy files on data
-    mkdir /data/security 0700 system system
+    mkdir /data/security 0711 system system
 
     # If there is no fs-post-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
@@ -337,12 +329,6 @@
     chown system system /sys/class/leds/red/device/grpfreq
     chown system system /sys/class/leds/red/device/grppwm
     chown system system /sys/class/leds/red/device/blink
-    chown system system /sys/class/leds/red/brightness
-    chown system system /sys/class/leds/green/brightness
-    chown system system /sys/class/leds/blue/brightness
-    chown system system /sys/class/leds/red/device/grpfreq
-    chown system system /sys/class/leds/red/device/grppwm
-    chown system system /sys/class/leds/red/device/blink
     chown system system /sys/class/timed_output/vibrator/enable
     chown system system /sys/module/sco/parameters/disable_esco
     chown system system /sys/kernel/ipv4/tcp_wmem_min
diff --git a/toolbox/dd.c b/toolbox/dd.c
index 350f1d2..a8c12d2 100644
--- a/toolbox/dd.c
+++ b/toolbox/dd.c
@@ -590,8 +590,8 @@
 
 	/* If not a pipe or tape device, try to seek on it. */
 	if (!(in.flags & (ISPIPE|ISTAPE))) {
-		if (lseek(in.fd,
-		    (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+		if (lseek64(in.fd,
+		    (off64_t)in.offset * (off64_t)in.dbsz, SEEK_CUR) == -1) {
 			fprintf(stderr, "%s: seek error: %s",
 				in.name, strerror(errno));
 			exit(1);
@@ -661,8 +661,8 @@
 	 * have specified the seek operand.
 	 */
 	if (!(out.flags & ISTAPE)) {
-		if (lseek(out.fd,
-		    (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) {
+		if (lseek64(out.fd,
+		    (off64_t)out.offset * (off64_t)out.dbsz, SEEK_SET) == -1) {
 			fprintf(stderr, "%s: seek error: %s\n",
 				out.name, strerror(errno));
 			exit(1);
diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c
index e57f607..9c73b00 100644
--- a/toolbox/dmesg.c
+++ b/toolbox/dmesg.c
@@ -5,15 +5,30 @@
 #include <sys/klog.h>
 #include <string.h>
 
-#define KLOG_BUF_SHIFT	17	/* CONFIG_LOG_BUF_SHIFT from our kernel */
-#define KLOG_BUF_LEN	(1 << KLOG_BUF_SHIFT)
+#define FALLBACK_KLOG_BUF_SHIFT	17	/* CONFIG_LOG_BUF_SHIFT from our kernel */
+#define FALLBACK_KLOG_BUF_LEN	(1 << FALLBACK_KLOG_BUF_SHIFT)
 
 int dmesg_main(int argc, char **argv)
 {
-    char buffer[KLOG_BUF_LEN + 1];
-    char *p = buffer;
+    char *buffer;
+    char *p;
     ssize_t ret;
-    int n, op;
+    int n, op, klog_buf_len;
+
+    klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
+
+    if (klog_buf_len <= 0) {
+        klog_buf_len = FALLBACK_KLOG_BUF_LEN;
+    }
+
+    buffer = (char *)malloc(klog_buf_len + 1);
+
+    if (!buffer) {
+        perror("malloc");
+        return EXIT_FAILURE;
+    }
+
+    p = buffer;
 
     if((argc == 2) && (!strcmp(argv[1],"-c"))) {
         op = KLOG_READ_CLEAR;
@@ -21,7 +36,7 @@
         op = KLOG_READ_ALL;
     }
 
-    n = klogctl(op, buffer, KLOG_BUF_LEN);
+    n = klogctl(op, buffer, klog_buf_len);
     if (n < 0) {
         perror("klogctl");
         return EXIT_FAILURE;
diff --git a/toolbox/ls.c b/toolbox/ls.c
index e530521..5324511 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -28,6 +28,7 @@
 #define LIST_LONG_NUMERIC   (1 << 5)
 #define LIST_CLASSIFY       (1 << 6)
 #define LIST_MACLABEL       (1 << 7)
+#define LIST_INODE          (1 << 8)
 
 // fwd
 static int listpath(const char *name, int flags);
@@ -127,22 +128,20 @@
     return 0;
 }
 
-static int listfile_size(const char *path, const char *filename, int flags)
+static int listfile_size(const char *path, const char *filename, struct stat *s,
+                         int flags)
 {
-    struct stat s;
-
-    if (lstat(path, &s) < 0) {
-        fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno));
+    if(!s || !path) {
         return -1;
     }
 
     /* blocks are 512 bytes, we want output to be KB */
     if ((flags & LIST_SIZE) != 0) {
-        printf("%lld ", s.st_blocks / 2);
+        printf("%lld ", s->st_blocks / 2);
     }
 
     if ((flags & LIST_CLASSIFY) != 0) {
-        char filetype = mode2kind(s.st_mode);
+        char filetype = mode2kind(s->st_mode);
         if (filetype != 'l') {
             printf("%c ", filetype);
         } else {
@@ -161,15 +160,18 @@
     return 0;
 }
 
-static int listfile_long(const char *path, int flags)
+static int listfile_long(const char *path, struct stat *s, int flags)
 {
-    struct stat s;
     char date[32];
     char mode[16];
     char user[16];
     char group[16];
     const char *name;
 
+    if(!s || !path) {
+        return -1;
+    }
+
     /* name is anything after the final '/', or the whole path if none*/
     name = strrchr(path, '/');
     if(name == 0) {
@@ -178,36 +180,32 @@
         name++;
     }
 
-    if(lstat(path, &s) < 0) {
-        return -1;
-    }
-
-    mode2str(s.st_mode, mode);
+    mode2str(s->st_mode, mode);
     if (flags & LIST_LONG_NUMERIC) {
-        sprintf(user, "%ld", s.st_uid);
-        sprintf(group, "%ld", s.st_gid);
+        sprintf(user, "%ld", s->st_uid);
+        sprintf(group, "%ld", s->st_gid);
     } else {
-        user2str(s.st_uid, user);
-        group2str(s.st_gid, group);
+        user2str(s->st_uid, user);
+        group2str(s->st_gid, group);
     }
 
-    strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
+    strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
     date[31] = 0;
 
 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
 // MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
 
-    switch(s.st_mode & S_IFMT) {
+    switch(s->st_mode & S_IFMT) {
     case S_IFBLK:
     case S_IFCHR:
         printf("%s %-8s %-8s %3d, %3d %s %s\n",
                mode, user, group,
-               (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
+               (int) MAJOR(s->st_rdev), (int) MINOR(s->st_rdev),
                date, name);
         break;
     case S_IFREG:
         printf("%s %-8s %-8s %8lld %s %s\n",
-               mode, user, group, s.st_size, date, name);
+               mode, user, group, s->st_size, date, name);
         break;
     case S_IFLNK: {
         char linkto[256];
@@ -237,15 +235,18 @@
     return 0;
 }
 
-static int listfile_maclabel(const char *path, int flags)
+static int listfile_maclabel(const char *path, struct stat *s, int flags)
 {
-    struct stat s;
     char mode[16];
     char user[16];
     char group[16];
     char *maclabel = NULL;
     const char *name;
 
+    if(!s || !path) {
+        return -1;
+    }
+
     /* name is anything after the final '/', or the whole path if none*/
     name = strrchr(path, '/');
     if(name == 0) {
@@ -254,20 +255,16 @@
         name++;
     }
 
-    if(lstat(path, &s) < 0) {
-        return -1;
-    }
-
     lgetfilecon(path, &maclabel);
     if (!maclabel) {
         return -1;
     }
 
-    mode2str(s.st_mode, mode);
-    user2str(s.st_uid, user);
-    group2str(s.st_gid, group);
+    mode2str(s->st_mode, mode);
+    user2str(s->st_uid, user);
+    group2str(s->st_gid, group);
 
-    switch(s.st_mode & S_IFMT) {
+    switch(s->st_mode & S_IFMT) {
     case S_IFLNK: {
         char linkto[256];
         ssize_t len;
@@ -301,7 +298,9 @@
 
 static int listfile(const char *dirname, const char *filename, int flags)
 {
-    if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL)) == 0) {
+    struct stat s;
+
+    if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) {
         printf("%s\n", filename);
         return 0;
     }
@@ -316,12 +315,20 @@
         pathname = filename;
     }
 
+    if(lstat(pathname, &s) < 0) {
+        return -1;
+    }
+
+    if(flags & LIST_INODE) {
+        printf("%8llu ", s.st_ino);
+    }
+
     if ((flags & LIST_MACLABEL) != 0) {
-        return listfile_maclabel(pathname, flags);
+        return listfile_maclabel(pathname, &s, flags);
     } else if ((flags & LIST_LONG) != 0) {
-        return listfile_long(pathname, flags);
+        return listfile_long(pathname, &s, flags);
     } else /*((flags & LIST_SIZE) != 0)*/ {
-        return listfile_size(pathname, filename, flags);
+        return listfile_size(pathname, filename, &s, flags);
     }
 }
 
@@ -456,6 +463,7 @@
                     case 'Z': flags |= LIST_MACLABEL; break;
                     case 'a': flags |= LIST_ALL; break;
                     case 'F': flags |= LIST_CLASSIFY; break;
+                    case 'i': flags |= LIST_INODE; break;
                     default:
                         fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
                         exit(1);
diff --git a/toolbox/mount.c b/toolbox/mount.c
index b7adce2..bcda2a2 100644
--- a/toolbox/mount.c
+++ b/toolbox/mount.c
@@ -19,7 +19,7 @@
 #define LOOPDEV_MAXLEN 64
 
 struct mount_opts {
-	const char str[8];
+	const char str[16];
 	unsigned long rwmask;
 	unsigned long rwset;
 	unsigned long rwnoset;
@@ -65,10 +65,11 @@
 static void add_extra_option(struct extra_opts *extra, char *s)
 {
 	int len = strlen(s);
-	int newlen = extra->used_size + len;
+	int newlen;
 
 	if (extra->str)
 	       len++;			/* +1 for ',' */
+	newlen = extra->used_size + len;
 
 	if (newlen >= extra->alloc_size) {
 		char *new;
@@ -79,7 +80,7 @@
 
 		extra->str = new;
 		extra->end = extra->str + extra->used_size;
-		extra->alloc_size = newlen;
+		extra->alloc_size = newlen + 1;
 	}
 
 	if (extra->used_size) {
diff --git a/toolbox/netstat.c b/toolbox/netstat.c
index 5768599..05dc640 100644
--- a/toolbox/netstat.c
+++ b/toolbox/netstat.c
@@ -108,7 +108,7 @@
             addr2str(AF_INET, &raddr, rport, rip);
 
             printf("%4s  %6d %6d %-22s %-22s %s\n",
-                   label, txq, rxq, lip, rip,
+                   label, rxq, txq, lip, rip,
                    state2str(state));
         }
     }
@@ -136,7 +136,7 @@
             addr2str(AF_INET6, &raddr6, rport, rip);
 
             printf("%4s  %6d %6d %-22s %-22s %s\n",
-                   label, txq, rxq, lip, rip,
+                   label, rxq, txq, lip, rip,
                    state2str(state));
         }
     }
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
index d311992..bf82882 100644
--- a/toolbox/watchprops.c
+++ b/toolbox/watchprops.c
@@ -1,35 +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>
 
-
-extern prop_area *__system_property_area__;
-
-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 = '.';
     }
@@ -37,40 +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[])
 {
-    prop_area *pa = __system_property_area__;
-    unsigned serial = pa->serial;
-    unsigned count = pa->count;
+    unsigned serial = 0;
+    unsigned count = 0;
     unsigned n;
     
-    if(count >= 1024) exit(1);
+    Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
+    if (!watchlist)
+        exit(1);
 
-    for(n = 0; n < count; n++) {
-        watchlist[n].pi = __system_property_find_nth(n);
-        watchlist[n].serial = watchlist[n].pi->serial;
-    }
+    __system_property_foreach(populate_watchlist, watchlist);
 
     for(;;) {
-        do {
-            __futex_wait(&pa->serial, serial, 0);
-        } while(pa->serial == serial);
-
-        while(count < pa->count){
-            watchlist[count].pi = __system_property_find_nth(count);
-            watchlist[count].serial = watchlist[n].pi->serial;
-            announce(watchlist[count].pi);
-            count++;
-            if(count == 1024) exit(1);
-        }
-
-        for(n = 0; n < count; n++){
-            unsigned tmp = watchlist[n].pi->serial;
-            if(watchlist[n].serial != tmp) {
-                announce(watchlist[n].pi);
-                watchlist[n].serial = tmp;
-            }
-        }
+        serial = __system_property_wait_any(serial);
+        __system_property_foreach(update_watchlist, watchlist);
     }
     return 0;
 }