Merge "Update the touch command to take a human readable timestamp"
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 698f8a9..e132c67 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -143,6 +143,16 @@
 #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
 
 
 /** built-in vendor list */
@@ -201,6 +211,11 @@
     VENDOR_ID_XIAOMI,
     VENDOR_ID_BYD,
     VENDOR_ID_OUYA,
+    VENDOR_ID_HAIER,
+    VENDOR_ID_HISENSE,
+    VENDOR_ID_MTK,
+    VENDOR_ID_NOOK,
+    VENDOR_ID_QISDA,
 };
 
 #define BUILT_IN_VENDOR_COUNT    (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index d88ef88..8c225cb 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -21,8 +21,7 @@
 
 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()
 {
@@ -30,14 +29,20 @@
     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.
@@ -49,19 +54,19 @@
     return *(int*)(&buf[0]);
 }
 
-__attribute__((noinline)) void overflow_stack(void* p) {
+__attribute__((noinline)) static void overflow_stack(void* p) {
     fprintf(stderr, "p = %p\n", p);
     void* buf[1];
     buf[0] = p;
     overflow_stack(&buf);
 }
 
-void test_call1()
+static void test_call1()
 {
     *((int*) 32) = 1;
 }
 
-void *noisy(void *x)
+static void *noisy(void *x)
 {
     char c = (unsigned) x;
     for(;;) {
@@ -72,7 +77,7 @@
     return 0;
 }
 
-int ctest()
+static int ctest()
 {
     pthread_t thr;
     pthread_attr_t attr;
@@ -90,7 +95,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);
@@ -99,22 +104,27 @@
     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)
 {
     fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
 
@@ -134,12 +144,16 @@
         return crash(42);
     } else if (!strcmp(arg,"abort")) {
         maybeabort();
+    } else if (!strcmp(arg, "heap-usage")) {
+        abuse_heap();
     }
 
     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");
@@ -159,11 +173,6 @@
     } else {
         crash1();
     }
-    
-    return 0;
-}
 
-void maybeabort()
-{
-    if(time(0) != 42) abort();
+    return 0;
 }
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index da2e9b0..0028bda 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -435,11 +435,13 @@
     signal(SIGBUS, SIG_DFL);
     signal(SIGFPE, SIG_DFL);
     signal(SIGSEGV, SIG_DFL);
-    signal(SIGPIPE, SIG_DFL);
 #ifdef SIGSTKFLT
     signal(SIGSTKFLT, SIG_DFL);
 #endif
 
+    // Ignore failed writes to closed sockets
+    signal(SIGPIPE, SIG_IGN);
+
     logsocket = socket_local_client("logd",
             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
     if(logsocket < 0) {
diff --git a/include/cutils/properties.h b/include/cutils/properties.h
index 228b73e..2c70165 100644
--- a/include/cutils/properties.h
+++ b/include/cutils/properties.h
@@ -54,8 +54,7 @@
 
 extern int __property_get_real(const char *, char *, const char *)
     __asm__(__USER_LABEL_PREFIX__ "property_get");
-extern void __property_get_too_small_error()
-    __attribute__((__error__("property_get() called with too small of a buffer")));
+__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
 
 __BIONIC_FORTIFY_INLINE
 int property_get(const char *key, char *value, const char *default_value) {
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index fbb6077..a0dd1e0 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -263,7 +263,7 @@
 
 #define ATRACE_INIT()
 #define ATRACE_GET_ENABLED_TAGS()
-#define ATRACE_ENABLED()
+#define ATRACE_ENABLED() 0
 #define ATRACE_BEGIN(name)
 #define ATRACE_END()
 #define ATRACE_ASYNC_BEGIN(name, cookie)
diff --git a/init/builtins.c b/init/builtins.c
index 9ae9ba3..6e37d08 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -594,8 +594,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 69f5fc8..de27a7a 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -785,6 +785,7 @@
 file_free_out:
     free(file1);
     free(file2);
+    free(file3);
 data_free_out:
     free(data);
 loading_free_out:
diff --git a/init/init.c b/init/init.c
old mode 100755
new mode 100644
index f8b21e6..4196620
--- a/init/init.c
+++ b/init/init.c
@@ -39,6 +39,7 @@
 #include <libgen.h>
 
 #include <cutils/list.h>
+#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
 #include <cutils/iosched_policy.h>
 #include <private/android_filesystem_config.h>
@@ -73,8 +74,6 @@
 static unsigned revision = 0;
 static char qemu[32];
 
-static int selinux_enabled = 1;
-
 static struct action *cur_action = NULL;
 static struct command *cur_command = NULL;
 static struct listnode *command_queue = NULL;
@@ -164,7 +163,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 +358,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 +397,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)
@@ -467,6 +476,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")) {
@@ -474,8 +494,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);
     }
@@ -594,10 +613,6 @@
     *value++ = 0;
     if (name_len == 0) return;
 
-    if (!strcmp(name,"selinux")) {
-        selinux_enabled = atoi(value);
-    }
-
     if (for_emulator) {
         /* in the emulator, export any kernel option with the
          * ro.kernel. prefix */
@@ -625,7 +640,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 +654,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);
@@ -776,10 +795,6 @@
 
 int selinux_reload_policy(void)
 {
-    if (!selinux_enabled) {
-        return -1;
-    }
-
     INFO("SELinux: Attempting to reload policy files\n");
 
     if (selinux_android_reload_policy() == -1) {
@@ -802,6 +817,24 @@
     return 0;
 }
 
+static void selinux_initialize(void)
+{
+    if (access("/sys/fs/selinux", F_OK) != 0) {
+        // SELinux is not compiled into this kernel. Fail gracefully.
+        return;
+    }
+
+    INFO("loading selinux policy\n");
+    if (selinux_android_load_policy() < 0) {
+        ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
+        android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+        while (1) { pause(); }  // never reached
+    }
+
+    selinux_init_all_handles();
+    security_setenforce(1);
+}
+
 int main(int argc, char **argv)
 {
     int fd_count = 0;
@@ -862,17 +895,7 @@
     cb.func_audit = audit_callback;
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
-    INFO("loading selinux policy\n");
-    if (selinux_enabled) {
-        if (selinux_android_load_policy() < 0) {
-            selinux_enabled = 0;
-            INFO("SELinux: Disabled due to failed policy load\n");
-        } else {
-            selinux_init_all_handles();
-        }
-    } else {
-        INFO("SELinux:  Disabled by command line option\n");
-    }
+    selinux_initialize();
     /* These directories were necessarily created before initial policy load
      * and therefore need their security context restored to the proper value.
      * This must happen before /dev is populated by ueventd.
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 a1d2423..28bf30c 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -208,8 +208,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) {
@@ -267,14 +268,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;
@@ -545,7 +546,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);
@@ -554,9 +555,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 33ec146..9afc756 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -112,7 +112,6 @@
 };
 
 typedef struct {
-    void *data;
     size_t size;
     int fd;
 } workspace;
@@ -120,83 +119,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 + 247 toc words) = 1020 bytes */
-/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
-
-#define PA_COUNT_MAX  247
-#define PA_INFO_START 1024
-#define PA_SIZE       32768
-
 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)
@@ -299,19 +249,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)
@@ -338,8 +278,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);
@@ -354,29 +294,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) {
-            ERROR("Failed to set '%s'='%s',  property pool is exhausted at %d entries",
-                    name, value, PA_COUNT_MAX);
-            return -1;
+        ret = __system_property_add(name, namelen, value, valuelen);
+        if (ret < 0) {
+            ERROR("Failed to set '%s'='%s'", name, value);
+            return ret;
         }
-
-        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);
     }
     /* If name starts with "net." treat as a DNS property. */
     if (strncmp("net.", name, strlen("net.")) == 0)  {
@@ -598,8 +522,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/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/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;
 }