am 0d4a4cb8: fastboot: allow format on devices with small buffers

* commit '0d4a4cb8aa16fdb3897eaa89a62cb055d8dcbdf9':
  fastboot: allow format on devices with small buffers
diff --git a/adb/adb.c b/adb/adb.c
index e116414..018dd3c 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -318,6 +318,15 @@
 #endif
 }
 
+static void send_msg_with_okay(int fd, char* msg, size_t msglen) {
+    char header[9];
+    if (msglen > 0xffff)
+        msglen = 0xffff;
+    snprintf(header, sizeof(header), "OKAY%04x", (unsigned)msglen);
+    writex(fd, header, 8);
+    writex(fd, msg, msglen);
+}
+
 static void send_connect(atransport *t)
 {
     D("Calling send_connect \n");
@@ -1421,7 +1430,6 @@
 int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
 {
     atransport *transport = NULL;
-    char buf[4096];
 
     if(!strcmp(service, "kill")) {
         fprintf(stderr,"adb server killed by remote request\n");
@@ -1467,13 +1475,11 @@
         char buffer[4096];
         int use_long = !strcmp(service+7, "-l");
         if (use_long || service[7] == 0) {
-            memset(buf, 0, sizeof(buf));
             memset(buffer, 0, sizeof(buffer));
             D("Getting device list \n");
             list_transports(buffer, sizeof(buffer), use_long);
-            snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
             D("Wrote device list \n");
-            writex(reply_fd, buf, strlen(buf));
+            send_msg_with_okay(reply_fd, buffer, strlen(buffer));
             return 0;
         }
     }
@@ -1502,8 +1508,7 @@
             }
         }
 
-        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
-        writex(reply_fd, buf, strlen(buf));
+        send_msg_with_okay(reply_fd, buffer, strlen(buffer));
         return 0;
     }
 
@@ -1511,8 +1516,7 @@
     if (!strcmp(service, "version")) {
         char version[12];
         snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION);
-        snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version);
-        writex(reply_fd, buf, strlen(buf));
+        send_msg_with_okay(reply_fd, version, strlen(version));
         return 0;
     }
 
@@ -1522,8 +1526,7 @@
        if (transport && transport->serial) {
             out = transport->serial;
         }
-        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
-        writex(reply_fd, buf, strlen(buf));
+        send_msg_with_okay(reply_fd, out, strlen(out));
         return 0;
     }
     if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
@@ -1532,8 +1535,7 @@
        if (transport && transport->devpath) {
             out = transport->devpath;
         }
-        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
-        writex(reply_fd, buf, strlen(buf));
+        send_msg_with_okay(reply_fd, out, strlen(out));
         return 0;
     }
     // indicates a new emulator instance has started
@@ -1547,14 +1549,11 @@
 
     if(!strcmp(service,"list-forward")) {
         // Create the list of forward redirections.
-        char header[9];
         int buffer_size = format_listeners(NULL, 0);
         // Add one byte for the trailing zero.
         char* buffer = malloc(buffer_size+1);
         (void) format_listeners(buffer, buffer_size+1);
-        snprintf(header, sizeof header, "OKAY%04x", buffer_size);
-        writex(reply_fd, header, 8);
-        writex(reply_fd, buffer, buffer_size);
+        send_msg_with_okay(reply_fd, buffer, buffer_size);
         free(buffer);
         return 0;
     }
@@ -1643,8 +1642,7 @@
     if(!strncmp(service,"get-state",strlen("get-state"))) {
         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
         char *state = connection_state_name(transport);
-        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
-        writex(reply_fd, buf, strlen(buf));
+        send_msg_with_okay(reply_fd, state, strlen(state));
         return 0;
     }
     return -1;
diff --git a/adb/adb_client.c b/adb/adb_client.c
index 586cd7b..1e47486 100644
--- a/adb/adb_client.c
+++ b/adb/adb_client.c
@@ -324,7 +324,10 @@
 
     buf[4] = 0;
     n = strtoul(buf, 0, 16);
-    if(n > 1024) goto oops;
+    if(n >= 0xffff) {
+        strcpy(__adb_error, "reply is too long (>= 64kB)");
+        goto oops;
+    }
 
     tmp = malloc(n + 1);
     if(tmp == 0) goto oops;
diff --git a/adb/commandline.c b/adb/commandline.c
index c9bb437..83b568d 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -107,8 +107,12 @@
         "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
-        "  adb push <local> <remote>    - copy file/dir to device\n"
-        "  adb pull <remote> [<local>]  - copy file/dir from device\n"
+        "  adb push [-p] <local> <remote>\n"
+        "                               - copy file/dir to device\n"
+        "                                 ('-p' to display the transfer progress)\n"
+        "  adb pull [-p] <remote> [<local>]\n"
+        "                               - copy file/dir from device\n"
+        "                                 ('-p' to display the transfer progress)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
         "                                 (see 'adb help all')\n"
@@ -921,6 +925,28 @@
     return path_buf;
 }
 
+
+static void parse_push_pull_args(char** arg, int narg, char const** path1, char const** path2,
+                                 int* show_progress) {
+    *show_progress = 0;
+
+    if ((narg > 0) && !strcmp(*arg, "-p")) {
+        *show_progress = 1;
+        ++arg;
+        --narg;
+    }
+
+    if (narg > 0) {
+        *path1 = *arg;
+        ++arg;
+        --narg;
+    }
+
+    if (narg > 0) {
+        *path2 = *arg;
+    }
+}
+
 int adb_commandline(int argc, char **argv)
 {
     char buf[4096];
@@ -1368,18 +1394,29 @@
     }
 
     if(!strcmp(argv[0], "push")) {
-        if(argc != 3) return usage();
-        return do_sync_push(argv[1], argv[2], 0 /* no verify APK */);
+        int show_progress = 0;
+        const char* lpath = NULL, *rpath = NULL;
+
+        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress);
+
+        if ((lpath != NULL) && (rpath != NULL)) {
+            return do_sync_push(lpath, rpath, 0 /* no verify APK */, show_progress);
+        }
+
+        return usage();
     }
 
     if(!strcmp(argv[0], "pull")) {
-        if (argc == 2) {
-            return do_sync_pull(argv[1], ".");
-        } else if (argc == 3) {
-            return do_sync_pull(argv[1], argv[2]);
-        } else {
-            return usage();
+        int show_progress = 0;
+        const char* rpath = NULL, *lpath = ".";
+
+        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress);
+
+        if (rpath != NULL) {
+            return do_sync_pull(rpath, lpath, show_progress);
         }
+
+        return usage();
     }
 
     if(!strcmp(argv[0], "install")) {
@@ -1717,7 +1754,7 @@
         }
     }
 
-    err = do_sync_push(apk_file, apk_dest, verify_apk);
+    err = do_sync_push(apk_file, apk_dest, verify_apk, 0 /* no show progress */);
     if (err) {
         goto cleanup_apk;
     } else {
@@ -1725,7 +1762,8 @@
     }
 
     if (verification_file != NULL) {
-        err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */);
+        err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */,
+                           0 /* no show progress */);
         if (err) {
             goto cleanup_apk;
         } else {
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
index 9fec081..8fad50e 100644
--- a/adb/file_sync_client.c
+++ b/adb/file_sync_client.c
@@ -62,6 +62,22 @@
             total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
 }
 
+static const char* transfer_progress_format = "\rTransferring: %llu/%llu (%d%%)";
+
+static void print_transfer_progress(unsigned long long bytes_current,
+                                    unsigned long long bytes_total) {
+    if (bytes_total == 0) return;
+
+    fprintf(stderr, transfer_progress_format, bytes_current, bytes_total,
+            (int) (bytes_current * 100 / bytes_total));
+
+    if (bytes_current == bytes_total) {
+        fputc('\n', stderr);
+    }
+
+    fflush(stderr);
+}
+
 void sync_quit(int fd)
 {
     syncmsg msg;
@@ -207,9 +223,10 @@
     return 0;
 }
 
-static int write_data_file(int fd, const char *path, syncsendbuf *sbuf)
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress)
 {
     int lfd, err = 0;
+    unsigned long long size = 0;
 
     lfd = adb_open(path, O_RDONLY);
     if(lfd < 0) {
@@ -217,6 +234,17 @@
         return -1;
     }
 
+    if (show_progress) {
+        // Determine local file size.
+        struct stat st;
+        if (lstat(path, &st)) {
+            fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno));
+            return -1;
+        }
+
+        size = st.st_size;
+    }
+
     sbuf->id = ID_DATA;
     for(;;) {
         int ret;
@@ -238,13 +266,18 @@
             break;
         }
         total_bytes += ret;
+
+        if (show_progress) {
+            print_transfer_progress(total_bytes, size);
+        }
     }
 
     adb_close(lfd);
     return err;
 }
 
-static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf)
+static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
+                             int show_progress)
 {
     int err = 0;
     int total = 0;
@@ -264,6 +297,10 @@
         }
         total += count;
         total_bytes += count;
+
+        if (show_progress) {
+            print_transfer_progress(total, size);
+        }
     }
 
     return err;
@@ -295,7 +332,7 @@
 #endif
 
 static int sync_send(int fd, const char *lpath, const char *rpath,
-                     unsigned mtime, mode_t mode, int verifyApk)
+                     unsigned mtime, mode_t mode, int verifyApk, int show_progress)
 {
     syncmsg msg;
     int len, r;
@@ -377,10 +414,10 @@
     }
 
     if (file_buffer) {
-        write_data_buffer(fd, file_buffer, size, sbuf);
+        write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
         free(file_buffer);
     } else if (S_ISREG(mode))
-        write_data_file(fd, lpath, sbuf);
+        write_data_file(fd, lpath, sbuf, show_progress);
 #ifdef HAVE_SYMLINKS
     else if (S_ISLNK(mode))
         write_data_link(fd, lpath, sbuf);
@@ -438,17 +475,38 @@
     return 0;
 }
 
-int sync_recv(int fd, const char *rpath, const char *lpath)
+int sync_recv(int fd, const char *rpath, const char *lpath, int show_progress)
 {
     syncmsg msg;
     int len;
     int lfd = -1;
     char *buffer = send_buffer.data;
     unsigned id;
+    unsigned long long size = 0;
 
     len = strlen(rpath);
     if(len > 1024) return -1;
 
+    if (show_progress) {
+        // Determine remote file size.
+        syncmsg stat_msg;
+        stat_msg.req.id = ID_STAT;
+        stat_msg.req.namelen = htoll(len);
+
+        if (writex(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
+            writex(fd, rpath, len)) {
+            return -1;
+        }
+
+        if (readx(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
+            return -1;
+        }
+
+        if (stat_msg.stat.id != ID_STAT) return -1;
+
+        size = ltohl(stat_msg.stat.size);
+    }
+
     msg.req.id = ID_RECV;
     msg.req.namelen = htoll(len);
     if(writex(fd, &msg.req, sizeof(msg.req)) ||
@@ -502,6 +560,10 @@
         }
 
         total_bytes += len;
+
+        if (show_progress) {
+            print_transfer_progress(total_bytes, size);
+        }
     }
 
     adb_close(lfd);
@@ -721,7 +783,8 @@
         if(ci->flag == 0) {
             fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
             if(!listonly &&
-               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
+               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */,
+                         0 /* no show progress */)) {
                 return 1;
             }
             pushed++;
@@ -739,7 +802,7 @@
 }
 
 
-int do_sync_push(const char *lpath, const char *rpath, int verifyApk)
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress)
 {
     struct stat st;
     unsigned mode;
@@ -786,7 +849,7 @@
             rpath = tmp;
         }
         BEGIN();
-        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) {
+        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk, show_progress)) {
             return 1;
         } else {
             END();
@@ -923,7 +986,7 @@
         next = ci->next;
         if (ci->flag == 0) {
             fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
-            if (sync_recv(fd, ci->src, ci->dst)) {
+            if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) {
                 return 1;
             }
             pulled++;
@@ -940,7 +1003,7 @@
     return 0;
 }
 
-int do_sync_pull(const char *rpath, const char *lpath)
+int do_sync_pull(const char *rpath, const char *lpath, int show_progress)
 {
     unsigned mode;
     struct stat st;
@@ -981,7 +1044,7 @@
             }
         }
         BEGIN();
-        if(sync_recv(fd, rpath, lpath)) {
+        if (sync_recv(fd, rpath, lpath, show_progress)) {
             return 1;
         } else {
             END();
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c
index 577fb8f..1d80d26 100644
--- a/adb/file_sync_service.c
+++ b/adb/file_sync_service.c
@@ -68,7 +68,7 @@
                 *x = '/';
                 return ret;
             }
-            selinux_android_restorecon(name);
+            selinux_android_restorecon(name, 0);
         }
         *x++ = '/';
     }
@@ -172,7 +172,7 @@
 }
 
 static int handle_send_file(int s, char *path, uid_t uid,
-        gid_t gid, mode_t mode, char *buffer)
+        gid_t gid, mode_t mode, char *buffer, bool do_unlink)
 {
     syncmsg msg;
     unsigned int timestamp = 0;
@@ -236,7 +236,7 @@
         if(writex(fd, buffer, len)) {
             int saved_errno = errno;
             adb_close(fd);
-            adb_unlink(path);
+            if (do_unlink) adb_unlink(path);
             fd = -1;
             errno = saved_errno;
             if(fail_errno(s)) return -1;
@@ -246,7 +246,7 @@
     if(fd >= 0) {
         struct utimbuf u;
         adb_close(fd);
-        selinux_android_restorecon(path);
+        selinux_android_restorecon(path, 0);
         u.actime = timestamp;
         u.modtime = timestamp;
         utime(path, &u);
@@ -261,7 +261,7 @@
 fail:
     if(fd >= 0)
         adb_close(fd);
-    adb_unlink(path);
+    if (do_unlink) adb_unlink(path);
     return -1;
 }
 
@@ -323,6 +323,7 @@
     char *tmp;
     unsigned int mode;
     int is_link, ret;
+    bool do_unlink;
 
     tmp = strrchr(path,',');
     if(tmp) {
@@ -339,11 +340,16 @@
     if(!tmp || errno) {
         mode = 0644;
         is_link = 0;
+        do_unlink = true;
+    } else {
+        struct stat st;
+        /* Don't delete files before copying if they are not "regular" */
+        do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+        if (do_unlink) {
+            adb_unlink(path);
+        }
     }
 
-    adb_unlink(path);
-
-
 #ifdef HAVE_SYMLINKS
     if(is_link)
         ret = handle_send_link(s, path, buffer);
@@ -366,7 +372,7 @@
         if (is_on_system(path)) {
             fs_config(tmp, 0, &uid, &gid, &mode, &cap);
         }
-        ret = handle_send_file(s, path, uid, gid, mode, buffer);
+        ret = handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
     }
 
     return ret;
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index e402e06..3e7e096 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -72,15 +72,15 @@
     struct {
         unsigned id;
         unsigned msglen;
-    } status;    
+    } status;
 } syncmsg;
 
 
 void file_sync_service(int fd, void *cookie);
 int do_sync_ls(const char *path);
-int do_sync_push(const char *lpath, const char *rpath, int verifyApk);
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int show_progress);
 int do_sync_sync(const char *lpath, const char *rpath, int listonly);
-int do_sync_pull(const char *rpath, const char *lpath);
+int do_sync_pull(const char *rpath, const char *lpath, int show_progress);
 
 #define SYNC_DATA_MAX (64*1024)
 
diff --git a/adb/transport.c b/adb/transport.c
index 6002530..f35880c 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -1188,6 +1188,10 @@
                 D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
                 if (errno == EINTR)
                     continue;
+                if (errno == EAGAIN) {
+                    adb_sleep_ms(1); // just yield some cpu time
+                    continue;
+                }
             } else {
                 D("writex: fd=%d disconnected\n", fd);
             }
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
index dee3cae..57e09eb 100644
--- a/adf/libadfhwc/adfhwc.cpp
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -257,7 +257,7 @@
 
         ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1);
         if (ret < 0 && ret != -EINVAL) {
-            ALOGE("failed to enable hotplug event on display %u: %s",
+            ALOGE("failed to enable hotplug event on display %zu: %s",
                     i, strerror(errno));
             goto err;
         }
diff --git a/charger/Android.mk b/charger/Android.mk
index b9d3473..40c7d78 100644
--- a/charger/Android.mk
+++ b/charger/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_C_INCLUDES := bootable/recovery
 
-LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng
+LOCAL_STATIC_LIBRARIES := libminui libpng
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
 LOCAL_STATIC_LIBRARIES += libsuspend
 endif
diff --git a/charger/charger.c b/charger/charger.c
index 66ddeaf..402d0e8 100644
--- a/charger/charger.c
+++ b/charger/charger.c
@@ -47,6 +47,8 @@
 
 #include "minui/minui.h"
 
+char *locale;
+
 #ifndef max
 #define max(a,b) ((a) > (b) ? (a) : (b))
 #endif
@@ -89,7 +91,6 @@
 };
 
 struct frame {
-    const char *name;
     int disp_time;
     int min_capacity;
     bool level_only;
@@ -140,33 +141,27 @@
 
 static struct frame batt_anim_frames[] = {
     {
-        .name = "charger/battery_0",
         .disp_time = 750,
         .min_capacity = 0,
     },
     {
-        .name = "charger/battery_1",
         .disp_time = 750,
         .min_capacity = 20,
     },
     {
-        .name = "charger/battery_2",
         .disp_time = 750,
         .min_capacity = 40,
     },
     {
-        .name = "charger/battery_3",
         .disp_time = 750,
         .min_capacity = 60,
     },
     {
-        .name = "charger/battery_4",
         .disp_time = 750,
         .min_capacity = 80,
         .level_only = true,
     },
     {
-        .name = "charger/battery_5",
         .disp_time = 750,
         .min_capacity = BATTERY_FULL_THRESH,
     },
@@ -196,8 +191,8 @@
 static void clear_screen(void)
 {
     gr_color(0, 0, 0, 255);
-    gr_fill(0, 0, gr_fb_width(), gr_fb_height());
-};
+    gr_clear();
+}
 
 #define MAX_KLOG_WRITE_BUF_SZ 256
 
@@ -657,8 +652,8 @@
 
     if (batt_anim->num_frames != 0) {
         draw_surface_centered(charger, frame->surface);
-        LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n",
-             batt_anim->cur_frame, frame->name, frame->min_capacity,
+        LOGV("drawing frame #%d min_cap=%d time=%d\n",
+             batt_anim->cur_frame, frame->min_capacity,
              frame->disp_time);
     }
 }
@@ -980,20 +975,27 @@
 
     ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
     if (ret < 0) {
-        LOGE("Cannot load image\n");
+        LOGE("Cannot load battery_fail image\n");
         charger->surf_unknown = NULL;
     }
 
-    for (i = 0; i < charger->batt_anim->num_frames; i++) {
-        struct frame *frame = &charger->batt_anim->frames[i];
+    charger->batt_anim = &battery_animation;
 
-        ret = res_create_surface(frame->name, &frame->surface);
-        if (ret < 0) {
-            LOGE("Cannot load image %s\n", frame->name);
-            /* TODO: free the already allocated surfaces... */
-            charger->batt_anim->num_frames = 0;
-            charger->batt_anim->num_cycles = 1;
-            break;
+    gr_surface* scale_frames;
+    int scale_count;
+    ret = res_create_multi_surface("charger/battery_scale", &scale_count, &scale_frames);
+    if (ret < 0) {
+        LOGE("Cannot load battery_scale image\n");
+        charger->batt_anim->num_frames = 0;
+        charger->batt_anim->num_cycles = 1;
+    } else if (scale_count != charger->batt_anim->num_frames) {
+        LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
+             scale_count, charger->batt_anim->num_frames);
+        charger->batt_anim->num_frames = 0;
+        charger->batt_anim->num_cycles = 1;
+    } else {
+        for (i = 0; i < charger->batt_anim->num_frames; i++) {
+            charger->batt_anim->frames[i].surface = scale_frames[i];
         }
     }
 
diff --git a/charger/images/battery_0.png b/charger/images/battery_0.png
deleted file mode 100644
index 2347074..0000000
--- a/charger/images/battery_0.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_1.png b/charger/images/battery_1.png
deleted file mode 100644
index cd34620..0000000
--- a/charger/images/battery_1.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_2.png b/charger/images/battery_2.png
deleted file mode 100644
index 3e4095e..0000000
--- a/charger/images/battery_2.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_3.png b/charger/images/battery_3.png
deleted file mode 100644
index 08c1551..0000000
--- a/charger/images/battery_3.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_4.png b/charger/images/battery_4.png
deleted file mode 100644
index 3a678da..0000000
--- a/charger/images/battery_4.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_5.png b/charger/images/battery_5.png
deleted file mode 100644
index d8dc40e..0000000
--- a/charger/images/battery_5.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_charge.png b/charger/images/battery_charge.png
deleted file mode 100644
index b501933..0000000
--- a/charger/images/battery_charge.png
+++ /dev/null
Binary files differ
diff --git a/charger/images/battery_fail.png b/charger/images/battery_fail.png
index 36fc254..aded88a 100644
--- a/charger/images/battery_fail.png
+++ b/charger/images/battery_fail.png
Binary files differ
diff --git a/charger/images/battery_scale.png b/charger/images/battery_scale.png
new file mode 100644
index 0000000..2ae8f0f
--- /dev/null
+++ b/charger/images/battery_scale.png
Binary files differ
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 422a86a..77fcbe0 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -1,55 +1,12 @@
-# Copyright 2005 The Android Open Source Project
-
-ifneq ($(filter arm mips x86,$(TARGET_ARCH)),)
-
 LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= \
-	backtrace.cpp \
-	debuggerd.cpp \
-	getevent.cpp \
-	tombstone.cpp \
-	utility.cpp \
-	$(TARGET_ARCH)/machine.cpp \
+debuggerd_2nd_arch_var_prefix :=
+include $(LOCAL_PATH)/debuggerd.mk
 
-LOCAL_CONLYFLAGS := -std=gnu99
-LOCAL_CPPFLAGS := -std=gnu++11
-LOCAL_CFLAGS := \
-	-Wall \
-	-Wno-array-bounds \
-	-Werror \
-
-LOCAL_MODULE := debuggerd
-
-ifeq ($(ARCH_ARM_HAVE_VFP),true)
-LOCAL_CFLAGS += -DWITH_VFP
-endif # ARCH_ARM_HAVE_VFP
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_CFLAGS += -DWITH_VFP_D32
-endif # ARCH_ARM_HAVE_VFP_D32
-
-LOCAL_SHARED_LIBRARIES := \
-	libbacktrace \
-	libc \
-	libcutils \
-	liblog \
-	libselinux \
-
-include external/stlport/libstlport.mk
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.c
-LOCAL_SRC_FILES += $(TARGET_ARCH)/crashglue.S
-LOCAL_MODULE := crasher
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-include $(BUILD_EXECUTABLE)
+ifdef TARGET_2ND_ARCH
+debuggerd_2nd_arch_var_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)
+include $(LOCAL_PATH)/debuggerd.mk
+endif
 
 ifeq ($(ARCH_ARM_HAVE_VFP),true)
 include $(CLEAR_VARS)
@@ -59,12 +16,20 @@
 LOCAL_CFLAGS += -DWITH_VFP_D32
 endif # ARCH_ARM_HAVE_VFP_D32
 
-LOCAL_SRC_FILES := vfp-crasher.c vfp.S
+LOCAL_SRC_FILES := vfp-crasher.c arm/vfp.S
 LOCAL_MODULE := vfp-crasher
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+LOCAL_MODULE_TARGET_ARCH := arm
 include $(BUILD_EXECUTABLE)
 endif # ARCH_ARM_HAVE_VFP == true
 
-endif # arm or x86 in TARGET_ARCH
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := vfp-crasher.c arm64/vfp.S
+LOCAL_MODULE := vfp-crasher64
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+LOCAL_MODULE_TARGET_ARCH := arm64
+include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp
index 3fba6db..fd2f69b 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/arm/machine.cpp
@@ -38,65 +38,6 @@
 #endif
 #endif
 
-static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) {
-  char code_buffer[64];       // actual 8+1+((8+1)*4) + 1 == 45
-  char ascii_buffer[32];      // actual 16 + 1 == 17
-  uintptr_t p, end;
-
-  p = addr & ~3;
-  p -= 32;
-  if (p > addr) {
-    // catch underflow
-    p = 0;
-  }
-  // Dump more memory content for the crashing thread.
-  end = p + 256;
-  // catch overflow; 'end - p' has to be multiples of 16
-  while (end < p)
-    end -= 16;
-
-  // Dump the code around PC as:
-  //  addr     contents                             ascii
-  //  00008d34 ef000000 e8bd0090 e1b00000 512fff1e  ............../Q
-  //  00008d44 ea00b1f9 e92d0090 e3a070fc ef000000  ......-..p......
-  while (p < end) {
-    char* asc_out = ascii_buffer;
-
-    sprintf(code_buffer, "%08x ", p);
-
-    int i;
-    for (i = 0; i < 4; i++) {
-      // If we see (data == -1 && errno != 0), we know that the ptrace
-      // call failed, probably because we're dumping memory in an
-      // unmapped or inaccessible page.  I don't know if there's
-      // value in making that explicit in the output -- it likely
-      // just complicates parsing and clarifies nothing for the
-      // enlightened reader.
-      long data = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(p), NULL);
-      sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
-
-      // Enable the following code blob to dump ASCII values
-#if 0
-      int j;
-      for (j = 0; j < 4; j++) {
-        // Our isprint() allows high-ASCII characters that display
-        // differently (often badly) in different viewers, so we
-        // just use a simpler test.
-        char val = (data >> (j*8)) & 0xff;
-        if (val >= 0x20 && val < 0x7f) {
-          *asc_out++ = val;
-        } else {
-          *asc_out++ = '.';
-        }
-      }
-#endif
-      p += 4;
-    }
-    *asc_out = '\0';
-    _LOG(log, scope_flags, "    %s %s\n", code_buffer, ascii_buffer);
-  }
-}
-
 // If configured to do so, dump memory around *all* registers
 // for the crashing thread.
 void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) {
diff --git a/debuggerd/vfp.S b/debuggerd/arm/vfp.S
similarity index 100%
rename from debuggerd/vfp.S
rename to debuggerd/arm/vfp.S
diff --git a/debuggerd/arm64/crashglue.S b/debuggerd/arm64/crashglue.S
new file mode 100644
index 0000000..b06b67c
--- /dev/null
+++ b/debuggerd/arm64/crashglue.S
@@ -0,0 +1,47 @@
+.globl crash1
+.type crash1, %function
+.globl crashnostack
+.type crashnostack, %function
+
+crash1:
+	ldr x0, =0xa5a50000
+	ldr x1, =0xa5a50001
+	ldr x2, =0xa5a50002
+	ldr x3, =0xa5a50003
+	ldr x4, =0xa5a50004
+	ldr x5, =0xa5a50005
+	ldr x6, =0xa5a50006
+	ldr x7, =0xa5a50007
+	ldr x8, =0xa5a50008
+	ldr x9, =0xa5a50009
+	ldr x10, =0xa5a50010
+	ldr x11, =0xa5a50011
+	ldr x12, =0xa5a50012
+	ldr x13, =0xa5a50013
+	ldr x14, =0xa5a50014
+	ldr x15, =0xa5a50015
+	ldr x16, =0xa5a50016
+	ldr x17, =0xa5a50017
+	ldr x18, =0xa5a50018
+	ldr x19, =0xa5a50019
+	ldr x20, =0xa5a50020
+	ldr x21, =0xa5a50021
+	ldr x22, =0xa5a50022
+	ldr x23, =0xa5a50023
+	ldr x24, =0xa5a50024
+	ldr x25, =0xa5a50025
+	ldr x26, =0xa5a50026
+	ldr x27, =0xa5a50027
+	ldr x28, =0xa5a50028
+	ldr x29, =0xa5a50029
+
+	mov x30, xzr
+	ldr x30, [x30]
+	b .
+
+
+crashnostack:
+	mov x0, xzr
+	add sp, x0, xzr
+	ldr x0, [x0]
+	b .
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp
new file mode 100644
index 0000000..7159228
--- /dev/null
+++ b/debuggerd/arm64/machine.cpp
@@ -0,0 +1,123 @@
+/*
+ *
+ * Copyright 2014, 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/uio.h>
+#include <linux/elf.h>
+
+#include "../utility.h"
+#include "../machine.h"
+
+/* enable to dump memory pointed to by every register */
+#define DUMP_MEMORY_FOR_ALL_REGISTERS 1
+
+/*
+ * If configured to do so, dump memory around *all* registers
+ * for the crashing thread.
+ */
+void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) {
+    struct user_pt_regs regs;
+    struct iovec io;
+    io.iov_base = &regs;
+    io.iov_len = sizeof(regs);
+
+    if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, &io) == -1) {
+        _LOG(log, scope_flags, "%s: ptrace failed to get registers: %s\n",
+             __func__, strerror(errno));
+        return;
+    }
+
+    if (IS_AT_FAULT(scope_flags) && DUMP_MEMORY_FOR_ALL_REGISTERS) {
+        for (int reg = 0; reg < 31; reg++) {
+            uintptr_t addr = regs.regs[reg];
+
+            /*
+             * Don't bother if it looks like a small int or ~= null, or if
+             * it's in the kernel area.
+             */
+            if (addr < 4096 || addr >= (1UL<<63)) {
+                continue;
+            }
+
+            _LOG(log, scope_flags | SCOPE_SENSITIVE, "\nmemory near x%d:\n", reg);
+            dump_memory(log, tid, addr, scope_flags | SCOPE_SENSITIVE);
+        }
+    }
+
+    _LOG(log, scope_flags, "\ncode around pc:\n");
+    dump_memory(log, tid, (uintptr_t)regs.pc, scope_flags);
+
+    if (regs.pc != regs.sp) {
+        _LOG(log, scope_flags, "\ncode around sp:\n");
+        dump_memory(log, tid, (uintptr_t)regs.sp, scope_flags);
+    }
+}
+
+void dump_registers(log_t* log, pid_t tid, int scope_flags)
+{
+  struct user_pt_regs r;
+  struct iovec io;
+  io.iov_base = &r;
+  io.iov_len = sizeof(r);
+
+  bool only_in_tombstone = !IS_AT_FAULT(scope_flags);
+
+  if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
+    _LOG(log, scope_flags, "ptrace error: %s\n", strerror(errno));
+    return;
+  }
+
+  for (int i = 0; i < 28; i += 4) {
+    _LOG(log, scope_flags, "    x%-2d  %016lx  x%-2d  %016lx  x%-2d  %016lx  x%-2d  %016lx\n",
+         i, (uint64_t)r.regs[i],
+         i+1, (uint64_t)r.regs[i+1],
+         i+2, (uint64_t)r.regs[i+2],
+         i+3, (uint64_t)r.regs[i+3]);
+  }
+
+  _LOG(log, scope_flags, "    x28  %016lx  x29  %016lx  x30  %016lx\n",
+       (uint64_t)r.regs[28], (uint64_t)r.regs[29], (uint64_t)r.regs[30]);
+
+  _LOG(log, scope_flags, "    sp   %016lx  pc   %016lx\n",
+       (uint64_t)r.sp, (uint64_t)r.pc);
+
+
+  struct user_fpsimd_state f;
+  io.iov_base = &f;
+  io.iov_len = sizeof(f);
+
+  if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
+    _LOG(log, scope_flags, "ptrace error: %s\n", strerror(errno));
+    return;
+  }
+
+  for (int i = 0; i < 32; i += 4) {
+    _LOG(log, scope_flags, "    v%-2d  %016lx  v%-2d  %016lx  v%-2d  %016lx  v%-2d  %016lx\n",
+         i, (uint64_t)f.vregs[i],
+         i+1, (uint64_t)f.vregs[i+1],
+         i+2, (uint64_t)f.vregs[i+2],
+         i+3, (uint64_t)f.vregs[i+3]);
+  }
+}
diff --git a/debuggerd/arm64/vfp.S b/debuggerd/arm64/vfp.S
new file mode 100644
index 0000000..bf12c22
--- /dev/null
+++ b/debuggerd/arm64/vfp.S
@@ -0,0 +1,42 @@
+    .text
+    .align 2
+    .global crash
+    .type crash, %function
+crash:
+    fmov   d0, XZR
+    fmov   d1, 1.0
+    fmov   d2, 2.0
+    fmov   d3, 3.0
+    fmov   d4, 4.0
+    fmov   d5, 5.0
+    fmov   d6, 6.0
+    fmov   d7, 7.0
+    fmov   d8, 8.0
+    fmov   d9, 9.0
+    fmov   d10, 10.0
+    fmov   d11, 11.0
+    fmov   d12, 12.0
+    fmov   d13, 13.0
+    fmov   d14, 14.0
+    fmov   d15, 15.0
+    fmov   d16, 16.0
+    fmov   d17, 17.0
+    fmov   d18, 18.0
+    fmov   d19, 19.0
+    fmov   d20, 20.0
+    fmov   d21, 21.0
+    fmov   d22, 22.0
+    fmov   d23, 23.0
+    fmov   d24, 24.0
+    fmov   d25, 25.0
+    fmov   d26, 26.0
+    fmov   d27, 27.0
+    fmov   d28, 28.0
+    fmov   d29, 29.0
+    fmov   d30, 30.0
+    fmov   d31, 31.0
+
+    mov       x0, xzr
+    str       x0, [x0]
+    br        x30
+
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index 5ecb1a5..5a2bc3c 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -23,19 +23,6 @@
 void crashnostack(void);
 static int do_action(const char* arg);
 
-static void debuggerd_connect()
-{
-    char tmp[1];
-    int s;
-    sprintf(tmp, "%d", gettid());
-    s = socket_local_client("android:debuggerd",
-            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    if(s >= 0) {
-        read(s, tmp, 1);
-        close(s);
-    }
-}
-
 static void maybeabort() {
     if(time(0) != 42) {
         abort();
@@ -70,13 +57,13 @@
 
 static void *noisy(void *x)
 {
-    char c = (unsigned) x;
+    char c = (uintptr_t) x;
     for(;;) {
         usleep(250*1000);
         write(2, &c, 1);
         if(c == 'C') *((unsigned*) 0) = 42;
     }
-    return 0;
+    return NULL;
 }
 
 static int ctest()
@@ -94,7 +81,7 @@
 
 static void* thread_callback(void* raw_arg)
 {
-    return (void*) do_action((const char*) raw_arg);
+    return (void*) (uintptr_t) do_action((const char*) raw_arg);
 }
 
 static int do_action_on_thread(const char* arg)
@@ -103,7 +90,7 @@
     pthread_create(&t, NULL, thread_callback, (void*) arg);
     void* result = NULL;
     pthread_join(t, &result);
-    return (int) result;
+    return (int) (uintptr_t) result;
 }
 
 __attribute__((noinline)) static int crash3(int a) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index de8ba9d..a2b164e 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -46,12 +46,12 @@
 #include "tombstone.h"
 #include "utility.h"
 
-typedef struct {
+struct debugger_request_t {
   debugger_action_t action;
   pid_t pid, tid;
   uid_t uid, gid;
   uintptr_t abort_msg_address;
-} debugger_request_t;
+};
 
 static int write_string(const char* file, const char* string) {
   int len;
@@ -116,7 +116,7 @@
       dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
     };
     size_t s = 0;
-    struct input_event e;
+    input_event e;
     bool done = false;
     init_debug_led();
     enable_debug_led();
@@ -176,8 +176,8 @@
 }
 
 static int read_request(int fd, debugger_request_t* out_request) {
-  struct ucred cr;
-  int len = sizeof(cr);
+  ucred cr;
+  socklen_t len = sizeof(cr);
   int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
   if (status != 0) {
     LOG("cannot get credentials\n");
@@ -187,7 +187,7 @@
   XLOG("reading tid\n");
   fcntl(fd, F_SETFL, O_NONBLOCK);
 
-  struct pollfd pollfds[1];
+  pollfd pollfds[1];
   pollfds[0].fd = fd;
   pollfds[0].events = POLLIN;
   pollfds[0].revents = 0;
@@ -205,7 +205,8 @@
     return -1;
   }
   if (status == sizeof(debugger_msg_t)) {
-    XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address);
+    XLOG("crash request of size %d abort_msg_address=0x%" PRIPTR "\n",
+         status, msg.abort_msg_address);
   } else {
     LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid);
     return -1;
@@ -440,13 +441,11 @@
   LOG("debuggerd: " __DATE__ " " __TIME__ "\n");
 
   for (;;) {
-    struct sockaddr addr;
-    socklen_t alen;
-    int fd;
+    sockaddr addr;
+    socklen_t alen = sizeof(addr);
 
-    alen = sizeof(addr);
     XLOG("waiting for connection\n");
-    fd = accept(s, &addr, &alen);
+    int fd = accept(s, &addr, &alen);
     if (fd < 0) {
       XLOG("accept failed: %s\n", strerror(errno));
       continue;
diff --git a/debuggerd/debuggerd.mk b/debuggerd/debuggerd.mk
new file mode 100644
index 0000000..a3982c1
--- /dev/null
+++ b/debuggerd/debuggerd.mk
@@ -0,0 +1,75 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	backtrace.cpp \
+	debuggerd.cpp \
+	getevent.cpp \
+	tombstone.cpp \
+	utility.cpp \
+
+LOCAL_SRC_FILES_arm    := arm/machine.cpp
+LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
+LOCAL_SRC_FILES_mips   := mips/machine.cpp
+LOCAL_SRC_FILES_x86    := x86/machine.cpp
+LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
+
+LOCAL_CONLYFLAGS := -std=gnu99
+LOCAL_CPPFLAGS := -std=gnu++11
+LOCAL_CFLAGS := \
+	-Wall \
+	-Wno-array-bounds \
+	-Werror \
+	-Wno-unused-parameter \
+
+ifeq ($(ARCH_ARM_HAVE_VFP),true)
+LOCAL_CFLAGS_arm += -DWITH_VFP
+endif # ARCH_ARM_HAVE_VFP
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_CFLAGS_arm += -DWITH_VFP_D32
+endif # ARCH_ARM_HAVE_VFP_D32
+
+LOCAL_SHARED_LIBRARIES := \
+	libbacktrace \
+	libc \
+	libcutils \
+	liblog \
+	libselinux \
+
+include external/stlport/libstlport.mk
+
+ifeq ($(TARGET_IS_64_BIT)|$(debuggerd_2nd_arch_var_prefix),true|)
+LOCAL_MODULE := debuggerd64
+LOCAL_NO_2ND_ARCH := true
+else
+LOCAL_MODULE := debuggerd
+LOCAL_32_BIT_ONLY := true
+endif
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.c
+LOCAL_SRC_FILES_arm    := arm/crashglue.S
+LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
+LOCAL_SRC_FILES_mips   := mips/crashglue.S
+LOCAL_SRC_FILES_x86    := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+
+LOCAL_2ND_ARCH_VAR_PREFIX := $(debuggerd_2nd_arch_var_prefix)
+
+ifeq ($(TARGET_IS_64_BIT)|$(debuggerd_2nd_arch_var_prefix),true|)
+LOCAL_MODULE := crasher64
+LOCAL_NO_2ND_ARCH := true
+else
+LOCAL_MODULE := crasher
+LOCAL_32_BIT_ONLY := true
+endif
+include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
index d1a7f2d..7b4e29e 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/mips/machine.cpp
@@ -34,61 +34,6 @@
 
 #define R(x) (static_cast<unsigned int>(x))
 
-static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) {
-  char code_buffer[64];       // actual 8+1+((8+1)*4) + 1 == 45
-  char ascii_buffer[32];      // actual 16 + 1 == 17
-  uintptr_t p, end;
-
-  p = addr & ~3;
-  p -= 32;
-  if (p > addr) {
-    // catch underflow
-    p = 0;
-  }
-  end = p + 80;
-  // catch overflow; 'end - p' has to be multiples of 16
-  while (end < p)
-    end -= 16;
-
-  // Dump the code around PC as:
-  //  addr     contents                             ascii
-  //  00008d34 ef000000 e8bd0090 e1b00000 512fff1e  ............../Q
-  //  00008d44 ea00b1f9 e92d0090 e3a070fc ef000000  ......-..p......
-  while (p < end) {
-    char* asc_out = ascii_buffer;
-
-    sprintf(code_buffer, "%08x ", p);
-
-    int i;
-    for (i = 0; i < 4; i++) {
-      // If we see (data == -1 && errno != 0), we know that the ptrace
-      // call failed, probably because we're dumping memory in an
-      // unmapped or inaccessible page.  I don't know if there's
-      // value in making that explicit in the output -- it likely
-      // just complicates parsing and clarifies nothing for the
-      // enlightened reader.
-      long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
-      sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
-
-      int j;
-      for (j = 0; j < 4; j++) {
-        // Our isprint() allows high-ASCII characters that display
-        // differently (often badly) in different viewers, so we
-        // just use a simpler test.
-        char val = (data >> (j*8)) & 0xff;
-        if (val >= 0x20 && val < 0x7f) {
-          *asc_out++ = val;
-        } else {
-          *asc_out++ = '.';
-        }
-      }
-      p += 4;
-    }
-    *asc_out = '\0';
-    _LOG(log, scope_flags, "    %s %s\n", code_buffer, ascii_buffer);
-  }
-}
-
 // If configured to do so, dump memory around *all* registers
 // for the crashing thread.
 void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) {
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
old mode 100644
new mode 100755
index 11e9af5..6a1b963
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -14,18 +14,20 @@
  * limitations under the License.
  */
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <time.h>
 #include <sys/ptrace.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
-#include <inttypes.h>
+#include <sys/un.h>
 
 #include <private/android_filesystem_config.h>
 
@@ -36,9 +38,6 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
-#include <sys/socket.h>
-#include <linux/un.h>
-
 #include <selinux/android.h>
 
 #include <UniquePtr.h>
@@ -51,16 +50,11 @@
 
 #define MAX_TOMBSTONES  10
 #define TOMBSTONE_DIR   "/data/tombstones"
+#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
 
 // Must match the path defined in NativeCrashListener.java
 #define NCRASH_SOCKET_PATH "/data/system/ndebugsocket"
 
-#define typecheck(x,y) {    \
-  typeof(x) __dummy1;     \
-  typeof(y) __dummy2;     \
-  (void)(&__dummy1 == &__dummy2); }
-
-
 static bool signal_has_address(int sig) {
   switch (sig) {
     case SIGILL:
@@ -180,9 +174,9 @@
   if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
     _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno));
   } else if (signal_has_address(sig)) {
-    _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %0*" PRIxPTR "\n",
+    _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %" PRIPTR "\n",
          sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code),
-         sizeof(uintptr_t)*2, reinterpret_cast<uintptr_t>(si.si_addr));
+         reinterpret_cast<uintptr_t>(si.si_addr));
   } else {
     _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n",
          sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code));
@@ -227,7 +221,7 @@
 static void dump_stack_segment(
     Backtrace* backtrace, log_t* log, int scope_flags, uintptr_t* sp, size_t words, int label) {
   for (size_t i = 0; i < words; i++) {
-    uint32_t stack_content;
+    word_t stack_content;
     if (!backtrace->ReadWord(*sp, &stack_content)) {
       break;
     }
@@ -244,32 +238,32 @@
     if (!func_name.empty()) {
       if (!i && label >= 0) {
         if (offset) {
-          _LOG(log, scope_flags, "    #%02d  %08x  %08x  %s (%s+%u)\n",
+          _LOG(log, scope_flags, "    #%02d  %" PRIPTR "  %" PRIPTR "  %s (%s+%" PRIuPTR ")\n",
                label, *sp, stack_content, map_name, func_name.c_str(), offset);
         } else {
-          _LOG(log, scope_flags, "    #%02d  %08x  %08x  %s (%s)\n",
+          _LOG(log, scope_flags, "    #%02d  %" PRIPTR "  %" PRIPTR "  %s (%s)\n",
                label, *sp, stack_content, map_name, func_name.c_str());
         }
       } else {
         if (offset) {
-          _LOG(log, scope_flags, "         %08x  %08x  %s (%s+%u)\n",
+          _LOG(log, scope_flags, "         %" PRIPTR "  %" PRIPTR "  %s (%s+%" PRIuPTR ")\n",
                *sp, stack_content, map_name, func_name.c_str(), offset);
         } else {
-          _LOG(log, scope_flags, "         %08x  %08x  %s (%s)\n",
+          _LOG(log, scope_flags, "         %" PRIPTR "  %" PRIPTR "  %s (%s)\n",
                *sp, stack_content, map_name, func_name.c_str());
         }
       }
     } else {
       if (!i && label >= 0) {
-        _LOG(log, scope_flags, "    #%02d  %08x  %08x  %s\n",
+        _LOG(log, scope_flags, "    #%02d  %" PRIPTR "  %" PRIPTR "  %s\n",
              label, *sp, stack_content, map_name);
       } else {
-        _LOG(log, scope_flags, "         %08x  %08x  %s\n",
+        _LOG(log, scope_flags, "         %" PRIPTR "  %" PRIPTR "  %s\n",
              *sp, stack_content, map_name);
       }
     }
 
-    *sp += sizeof(uint32_t);
+    *sp += sizeof(word_t);
   }
 }
 
@@ -292,7 +286,7 @@
   scope_flags |= SCOPE_SENSITIVE;
 
   // Dump a few words before the first frame.
-  uintptr_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(uint32_t);
+  word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
   dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, -1);
 
   // Dump a few words from all successive frames.
@@ -312,7 +306,7 @@
         _LOG(log, scope_flags, "         ........  ........\n");
       }
     } else {
-      size_t words = frame->stack_size / sizeof(uint32_t);
+      size_t words = frame->stack_size / sizeof(word_t);
       if (words == 0) {
         words = 1;
       } else if (words > STACK_WORDS) {
@@ -335,7 +329,7 @@
 
 static void dump_map(log_t* log, const backtrace_map_t* map, const char* what, int scope_flags) {
   if (map != NULL) {
-    _LOG(log, scope_flags, "    %08x-%08x %c%c%c %s\n", map->start, map->end,
+    _LOG(log, scope_flags, "    %" PRIPTR "-%" PRIPTR " %c%c%c %s\n", map->start, map->end,
          (map->flags & PROT_READ) ? 'r' : '-', (map->flags & PROT_WRITE) ? 'w' : '-',
          (map->flags & PROT_EXEC) ? 'x' : '-', map->name.c_str());
   } else {
@@ -361,7 +355,7 @@
     return;
   }
 
-  _LOG(log, scope_flags, "\nmemory map around fault addr %" PRIxPTR ":\n",
+  _LOG(log, scope_flags, "\nmemory map around fault addr %" PRIPTR ":\n",
        reinterpret_cast<uintptr_t>(si.si_addr));
 
   // Search for a match, or for a hole where the match would be.  The list
@@ -574,24 +568,15 @@
   memset(msg, 0, sizeof(msg));
   char* p = &msg[0];
   while (p < &msg[sizeof(msg)]) {
-    uint32_t data;
+    word_t data;
+    size_t len = sizeof(word_t);
     if (!backtrace->ReadWord(address, &data)) {
       break;
     }
-    address += sizeof(uint32_t);
+    address += sizeof(word_t);
 
-    if ((*p++ = (data >>  0) & 0xff) == 0) {
-      break;
-    }
-    if ((*p++ = (data >>  8) & 0xff) == 0) {
-      break;
-    }
-    if ((*p++ = (data >> 16) & 0xff) == 0) {
-      break;
-    }
-    if ((*p++ = (data >> 24) & 0xff) == 0) {
-      break;
-    }
+    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0)
+       len--;
   }
   msg[sizeof(msg) - 1] = '\0';
 
@@ -663,24 +648,19 @@
 //
 // Returns the path of the tombstone file, allocated using malloc().  Caller must free() it.
 static char* find_and_open_tombstone(int* fd) {
-  unsigned long mtime = ULONG_MAX;
-  struct stat sb;
-
-  // XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
-  // to, our logic breaks. This check will generate a warning if that happens.
-  typecheck(mtime, sb.st_mtime);
-
-  // In a single wolf-like pass, find an available slot and, in case none
+  // In a single pass, find an available slot and, in case none
   // exist, find and record the least-recently-modified file.
   char path[128];
-  int oldest = 0;
+  int oldest = -1;
+  struct stat oldest_sb;
   for (int i = 0; i < MAX_TOMBSTONES; i++) {
-    snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
+    snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
 
+    struct stat sb;
     if (!stat(path, &sb)) {
-      if (sb.st_mtime < mtime) {
+      if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
         oldest = i;
-        mtime = sb.st_mtime;
+        oldest_sb.st_mtime = sb.st_mtime;
       }
       continue;
     }
@@ -695,8 +675,13 @@
     return strdup(path);
   }
 
+  if (oldest < 0) {
+    LOG("Failed to find a valid tombstone, default to using tombstone 0.\n");
+    oldest = 0;
+  }
+
   // we didn't find an available file, so we clobber the oldest one
-  snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
+  snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
   *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
   if (*fd < 0) {
     LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno));
@@ -739,10 +724,15 @@
 char* engrave_tombstone(
     pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, bool dump_sibling_threads,
     bool quiet, bool* detach_failed, int* total_sleep_time_usec) {
-  mkdir(TOMBSTONE_DIR, 0755);
-  chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
+  if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
+      LOG("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+  }
 
-  if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) {
+  if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
+      LOG("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+  }
+
+  if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == -1) {
     *detach_failed = false;
     return NULL;
   }
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index 35f061e..9b20914 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
+#include "utility.h"
+
 #include <errno.h>
-#include <unistd.h>
 #include <signal.h>
-#include <log/logd.h>
+#include <string.h>
+#include <unistd.h>
 #include <sys/ptrace.h>
 #include <sys/wait.h>
-#include <arpa/inet.h>
-#include <assert.h>
 
-#include "utility.h"
+#include <backtrace/Backtrace.h>
+#include <log/logd.h>
 
 const int sleep_time_usec = 50000;         // 0.05 seconds
 const int max_total_sleep_usec = 10000000; // 10 seconds
@@ -34,7 +32,7 @@
 static int write_to_am(int fd, const char* buf, int len) {
   int to_write = len;
   while (to_write > 0) {
-    int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) );
+    int written = TEMP_FAILURE_RETRY(write(fd, buf + len - to_write, to_write));
     if (written < 0) {
       // hard failure
       LOG("AM write failure (%d / %s)\n", errno, strerror(errno));
@@ -46,34 +44,28 @@
 }
 
 void _LOG(log_t* log, int scopeFlags, const char* fmt, ...) {
-  char buf[512];
-  bool want_tfd_write;
-  bool want_log_write;
-  bool want_amfd_write;
-  int len = 0;
+  bool want_tfd_write = log && log->tfd >= 0;
+  bool want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet);
+  bool want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0;
 
+  char buf[512];
   va_list ap;
   va_start(ap, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
 
-  // where is the information going to go?
-  want_tfd_write = log && log->tfd >= 0;
-  want_log_write = IS_AT_FAULT(scopeFlags) && (!log || !log->quiet);
-  want_amfd_write = IS_AT_FAULT(scopeFlags) && !IS_SENSITIVE(scopeFlags) && log && log->amfd >= 0;
-
-  // if we're going to need the literal string, generate it once here
-  if (want_tfd_write || want_amfd_write) {
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    len = strlen(buf);
+  size_t len = strlen(buf);
+  if (len <= 0) {
+    return;
   }
 
   if (want_tfd_write) {
-    write(log->tfd, buf, len);
+    TEMP_FAILURE_RETRY(write(log->tfd, buf, len));
   }
 
   if (want_log_write) {
-    // whatever goes to logcat also goes to the Activity Manager
-    __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
-    if (want_amfd_write && len > 0) {
+    __android_log_write(ANDROID_LOG_INFO, "DEBUG", buf);
+    if (want_amfd_write) {
       int written = write_to_am(log->amfd, buf, len);
       if (written <= 0) {
         // timeout or other failure on write; stop informing the activity manager
@@ -81,7 +73,6 @@
       }
     }
   }
-  va_end(ap);
 }
 
 int wait_for_signal(pid_t tid, int* total_sleep_time_usec) {
@@ -127,3 +118,77 @@
     *total_sleep_time_usec += sleep_time_usec;
   }
 }
+
+#if defined (__mips__)
+#define DUMP_MEMORY_AS_ASCII 1
+#else
+#define DUMP_MEMORY_AS_ASCII 0
+#endif
+
+void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags) {
+    char code_buffer[64];
+    char ascii_buffer[32];
+    uintptr_t p, end;
+
+    p = addr & ~(sizeof(long) - 1);
+    /* Dump 32 bytes before addr */
+    p -= 32;
+    if (p > addr) {
+        /* catch underflow */
+        p = 0;
+    }
+    /* Dump 256 bytes */
+    end = p + 256;
+    /* catch overflow; 'end - p' has to be multiples of 16 */
+    while (end < p) {
+        end -= 16;
+    }
+
+    /* Dump the code around PC as:
+     *  addr             contents                           ascii
+     *  0000000000008d34 ef000000e8bd0090 e1b00000512fff1e  ............../Q
+     *  0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000  ......-..p......
+     * On 32-bit machines, there are still 16 bytes per line but addresses and
+     * words are of course presented differently.
+     */
+    while (p < end) {
+        char* asc_out = ascii_buffer;
+
+        int len = snprintf(code_buffer, sizeof(code_buffer), "%" PRIPTR " ", p);
+
+        for (size_t i = 0; i < 16/sizeof(long); i++) {
+            long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
+            if (data == -1 && errno != 0) {
+                // ptrace failed, probably because we're dumping memory in an
+                // unmapped or inaccessible page.
+#ifdef __LP64__
+                len += sprintf(code_buffer + len, "---------------- ");
+#else
+                len += sprintf(code_buffer + len, "-------- ");
+#endif
+            } else {
+                len += sprintf(code_buffer + len, "%" PRIPTR " ",
+                               static_cast<uintptr_t>(data));
+            }
+
+#if DUMP_MEMORY_AS_ASCII
+            for (size_t j = 0; j < sizeof(long); j++) {
+                /*
+                 * Our isprint() allows high-ASCII characters that display
+                 * differently (often badly) in different viewers, so we
+                 * just use a simpler test.
+                 */
+                char val = (data >> (j*8)) & 0xff;
+                if (val >= 0x20 && val < 0x7f) {
+                    *asc_out++ = val;
+                } else {
+                    *asc_out++ = '.';
+                }
+            }
+#endif
+            p += sizeof(long);
+        }
+        *asc_out = '\0';
+        _LOG(log, scope_flags, "    %s %s\n", code_buffer, ascii_buffer);
+    }
+}
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 1f006ed..0f88605 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -18,8 +18,8 @@
 #ifndef _DEBUGGERD_UTILITY_H
 #define _DEBUGGERD_UTILITY_H
 
-#include <stddef.h>
 #include <stdbool.h>
+#include <sys/types.h>
 
 typedef struct {
     /* tombstone file descriptor */
@@ -63,4 +63,6 @@
 int wait_for_signal(pid_t tid, int* total_sleep_time_usec);
 void wait_for_stop(pid_t tid, int* total_sleep_time_usec);
 
+void dump_memory(log_t* log, pid_t tid, uintptr_t addr, int scope_flags);
+
 #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86_64/crashglue.S b/debuggerd/x86_64/crashglue.S
new file mode 100644
index 0000000..4d2a5c0
--- /dev/null
+++ b/debuggerd/x86_64/crashglue.S
@@ -0,0 +1,15 @@
+.globl crash1
+.globl crashnostack
+
+crash1:
+	movl $0xa5a50000, %eax
+	movl $0xa5a50001, %ebx
+	movl $0xa5a50002, %ecx
+
+	movl $0, %edx
+	jmp *%rdx
+
+
+crashnostack:
+	movl $0, %ebp
+	jmp *%rbp
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
new file mode 100755
index 0000000..406851a
--- /dev/null
+++ b/debuggerd/x86_64/machine.cpp
@@ -0,0 +1,51 @@
+/*
+** Copyright 2013, 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 <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+
+#include "../utility.h"
+#include "../machine.h"
+
+void dump_memory_and_code(log_t* log, pid_t tid, int scope_flags) {
+}
+
+void dump_registers(log_t* log, pid_t tid, int scope_flags) {
+    struct user_regs_struct r;
+    if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
+        _LOG(log, scope_flags, "cannot get registers: %s\n", strerror(errno));
+        return;
+    }
+    _LOG(log, scope_flags, "    rax %016lx  rbx %016lx  rcx %016lx  rdx %016lx\n",
+         r.rax, r.rbx, r.rcx, r.rdx);
+    _LOG(log, scope_flags, "    rsi %016lx  rdi %016lx\n",
+         r.rsi, r.rdi);
+    _LOG(log, scope_flags, "    r8  %016lx  r9  %016lx  r10 %016lx  r11 %016lx\n",
+         r.r8, r.r9, r.r10, r.r11);
+    _LOG(log, scope_flags, "    r12 %016lx  r13 %016lx  r14 %016lx  r15 %016lx\n",
+         r.r12, r.r13, r.r14, r.r15);
+    _LOG(log, scope_flags, "    cs  %016lx  ss  %016lx\n",
+         r.cs, r.ss);
+    _LOG(log, scope_flags, "    rip %016lx  rbp %016lx  rsp %016lx  eflags %016lx\n",
+         r.rip, r.rbp, r.rsp, r.eflags);
+}
diff --git a/fastbootd/commands.c b/fastbootd/commands.c
index 063e1a6..98b7866 100644
--- a/fastbootd/commands.c
+++ b/fastbootd/commands.c
@@ -124,9 +124,9 @@
         goto error;
     }
 
-    kernel_ptr = (void *)((unsigned) ptr + hdr->page_size);
-    ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual);
-    second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual);
+    kernel_ptr = (void *)((uintptr_t) ptr + hdr->page_size);
+    ramdisk_ptr = (void *)((uintptr_t) kernel_ptr + kernel_actual);
+    second_ptr = (void *)((uintptr_t) ramdisk_ptr + ramdisk_actual);
 
     D(INFO, "preparing to boot");
     // Prepares boot physical address. Addresses from header are ignored
diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c
index 8da9a28..922914b 100644
--- a/fastbootd/commands/boot.c
+++ b/fastbootd/commands/boot.c
@@ -89,10 +89,10 @@
  * Kernel address is not set into kernel_phys
  * Ramdisk is set to position relative to kernel
  */
-int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
-                       unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
-                       unsigned second_phys, void *second_addr, int second_size,
-                       unsigned atags_phys, void *atags_addr, int atags_size) {
+int prepare_boot_linux(uintptr_t kernel_phys, void *kernel_addr, int kernel_size,
+                       uintptr_t ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
+                       uintptr_t second_phys, void *second_addr, int second_size,
+                       uintptr_t atags_phys, void *atags_addr, int atags_size) {
     struct kexec_segment segment[4];
     int segment_count = 2;
     unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h
index 901c38a..a5efd01 100644
--- a/fastbootd/commands/boot.h
+++ b/fastbootd/commands/boot.h
@@ -40,8 +40,8 @@
 #define KEXEC_TYPE_DEFAULT 0
 #define KEXEC_TYPE_CRASH   1
 
-int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int,
-                       unsigned, void *, int, unsigned, void *, int);
+int prepare_boot_linux(uintptr_t, void *, int, uintptr_t, void *, int,
+                       uintptr_t, void *, int, uintptr_t, void *, int);
 unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *);
 long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long);
 char *read_atags(const char *, int *);
diff --git a/fastbootd/protocol.c b/fastbootd/protocol.c
index 0086b4a..3908020 100644
--- a/fastbootd/protocol.c
+++ b/fastbootd/protocol.c
@@ -142,7 +142,7 @@
     char response[64];
     ssize_t ret;
 
-    snprintf(response, 64, "DATA%08x", len);
+    snprintf(response, 64, "DATA%08zx", len);
     ret = protocol_handle_write(phandle, response, strlen(response));
     if (ret < 0)
         return;
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 790598a..165ebd4 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -3,7 +3,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c
+LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c
 
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 24ce806..dcda005 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -54,55 +54,6 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
-struct flag_list {
-    const char *name;
-    unsigned flag;
-};
-
-static struct flag_list mount_flags[] = {
-    { "noatime",    MS_NOATIME },
-    { "noexec",     MS_NOEXEC },
-    { "nosuid",     MS_NOSUID },
-    { "nodev",      MS_NODEV },
-    { "nodiratime", MS_NODIRATIME },
-    { "ro",         MS_RDONLY },
-    { "rw",         0 },
-    { "remount",    MS_REMOUNT },
-    { "bind",       MS_BIND },
-    { "rec",        MS_REC },
-    { "unbindable", MS_UNBINDABLE },
-    { "private",    MS_PRIVATE },
-    { "slave",      MS_SLAVE },
-    { "shared",     MS_SHARED },
-    { "defaults",   0 },
-    { 0,            0 },
-};
-
-static struct flag_list fs_mgr_flags[] = {
-    { "wait",        MF_WAIT },
-    { "check",       MF_CHECK },
-    { "encryptable=",MF_CRYPT },
-    { "nonremovable",MF_NONREMOVABLE },
-    { "voldmanaged=",MF_VOLDMANAGED},
-    { "length=",     MF_LENGTH },
-    { "recoveryonly",MF_RECOVERYONLY },
-    { "swapprio=",   MF_SWAPPRIO },
-    { "zramsize=",   MF_ZRAMSIZE },
-    { "verify",      MF_VERIFY },
-    { "noemulatedsd", MF_NOEMULATEDSD },
-    { "defaults",    0 },
-    { 0,             0 },
-};
-
-struct fs_mgr_flag_values {
-    char *key_loc;
-    long long part_length;
-    char *label;
-    int partnum;
-    int swap_prio;
-    unsigned int zram_size;
-};
-
 /*
  * gettime() - returns the time in seconds of the system's monotonic clock or
  * zero on error.
@@ -133,269 +84,6 @@
     return ret;
 }
 
-static int parse_flags(char *flags, struct flag_list *fl,
-                       struct fs_mgr_flag_values *flag_vals,
-                       char *fs_options, int fs_options_len)
-{
-    int f = 0;
-    int i;
-    char *p;
-    char *savep;
-
-    /* initialize flag values.  If we find a relevant flag, we'll
-     * update the value */
-    if (flag_vals) {
-        memset(flag_vals, 0, sizeof(*flag_vals));
-        flag_vals->partnum = -1;
-        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
-    }
-
-    /* initialize fs_options to the null string */
-    if (fs_options && (fs_options_len > 0)) {
-        fs_options[0] = '\0';
-    }
-
-    p = strtok_r(flags, ",", &savep);
-    while (p) {
-        /* Look for the flag "p" in the flag list "fl"
-         * If not found, the loop exits with fl[i].name being null.
-         */
-        for (i = 0; fl[i].name; i++) {
-            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
-                f |= fl[i].flag;
-                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
-                    /* The encryptable flag is followed by an = and the
-                     * location of the keys.  Get it and return it.
-                     */
-                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
-                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
-                    /* The length flag is followed by an = and the
-                     * size of the partition.  Get it and return it.
-                     */
-                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
-                    /* The voldmanaged flag is followed by an = and the
-                     * label, a colon and the partition number or the
-                     * word "auto", e.g.
-                     *   voldmanaged=sdcard:3
-                     * Get and return them.
-                     */
-                    char *label_start;
-                    char *label_end;
-                    char *part_start;
-
-                    label_start = strchr(p, '=') + 1;
-                    label_end = strchr(p, ':');
-                    if (label_end) {
-                        flag_vals->label = strndup(label_start,
-                                                   (int) (label_end - label_start));
-                        part_start = strchr(p, ':') + 1;
-                        if (!strcmp(part_start, "auto")) {
-                            flag_vals->partnum = -1;
-                        } else {
-                            flag_vals->partnum = strtol(part_start, NULL, 0);
-                        }
-                    } else {
-                        ERROR("Warning: voldmanaged= flag malformed\n");
-                    }
-                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
-                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
-                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
-                    flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
-                }
-                break;
-            }
-        }
-
-        if (!fl[i].name) {
-            if (fs_options) {
-                /* It's not a known flag, so it must be a filesystem specific
-                 * option.  Add it to fs_options if it was passed in.
-                 */
-                strlcat(fs_options, p, fs_options_len);
-                strlcat(fs_options, ",", fs_options_len);
-            } else {
-                /* fs_options was not passed in, so if the flag is unknown
-                 * it's an error.
-                 */
-                ERROR("Warning: unknown flag %s\n", p);
-            }
-        }
-        p = strtok_r(NULL, ",", &savep);
-    }
-
-out:
-    if (fs_options && fs_options[0]) {
-        /* remove the last trailing comma from the list of options */
-        fs_options[strlen(fs_options) - 1] = '\0';
-    }
-
-    return f;
-}
-
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
-    FILE *fstab_file;
-    int cnt, entries;
-    ssize_t len;
-    size_t alloc_len = 0;
-    char *line = NULL;
-    const char *delim = " \t";
-    char *save_ptr, *p;
-    struct fstab *fstab = NULL;
-    struct fstab_rec *recs;
-    struct fs_mgr_flag_values flag_vals;
-#define FS_OPTIONS_LEN 1024
-    char tmp_fs_options[FS_OPTIONS_LEN];
-
-    fstab_file = fopen(fstab_path, "r");
-    if (!fstab_file) {
-        ERROR("Cannot open file %s\n", fstab_path);
-        return 0;
-    }
-
-    entries = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-        entries++;
-    }
-
-    if (!entries) {
-        ERROR("No entries found in fstab\n");
-        goto err;
-    }
-
-    /* Allocate and init the fstab structure */
-    fstab = calloc(1, sizeof(struct fstab));
-    fstab->num_entries = entries;
-    fstab->fstab_filename = strdup(fstab_path);
-    fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
-
-    fseek(fstab_file, 0, SEEK_SET);
-
-    cnt = 0;
-    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-
-        /* Skip any leading whitespace */
-        p = line;
-        while (isspace(*p)) {
-            p++;
-        }
-        /* ignore comments or empty lines */
-        if (*p == '#' || *p == '\0')
-            continue;
-
-        /* If a non-comment entry is greater than the size we allocated, give an
-         * error and quit.  This can happen in the unlikely case the file changes
-         * between the two reads.
-         */
-        if (cnt >= entries) {
-            ERROR("Tried to process more entries than counted\n");
-            break;
-        }
-
-        if (!(p = strtok_r(line, delim, &save_ptr))) {
-            ERROR("Error parsing mount source\n");
-            goto err;
-        }
-        fstab->recs[cnt].blk_device = strdup(p);
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing mount_point\n");
-            goto err;
-        }
-        fstab->recs[cnt].mount_point = strdup(p);
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing fs_type\n");
-            goto err;
-        }
-        fstab->recs[cnt].fs_type = strdup(p);
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing mount_flags\n");
-            goto err;
-        }
-        tmp_fs_options[0] = '\0';
-        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
-                                       tmp_fs_options, FS_OPTIONS_LEN);
-
-        /* fs_options are optional */
-        if (tmp_fs_options[0]) {
-            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
-        } else {
-            fstab->recs[cnt].fs_options = NULL;
-        }
-
-        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
-            ERROR("Error parsing fs_mgr_options\n");
-            goto err;
-        }
-        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
-                                                    &flag_vals, NULL, 0);
-        fstab->recs[cnt].key_loc = flag_vals.key_loc;
-        fstab->recs[cnt].length = flag_vals.part_length;
-        fstab->recs[cnt].label = flag_vals.label;
-        fstab->recs[cnt].partnum = flag_vals.partnum;
-        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
-        fstab->recs[cnt].zram_size = flag_vals.zram_size;
-        cnt++;
-    }
-    fclose(fstab_file);
-    free(line);
-    return fstab;
-
-err:
-    fclose(fstab_file);
-    free(line);
-    if (fstab)
-        fs_mgr_free_fstab(fstab);
-    return NULL;
-}
-
-void fs_mgr_free_fstab(struct fstab *fstab)
-{
-    int i;
-
-    if (!fstab) {
-        return;
-    }
-
-    for (i = 0; i < fstab->num_entries; i++) {
-        /* Free the pointers return by strdup(3) */
-        free(fstab->recs[i].blk_device);
-        free(fstab->recs[i].mount_point);
-        free(fstab->recs[i].fs_type);
-        free(fstab->recs[i].fs_options);
-        free(fstab->recs[i].key_loc);
-        free(fstab->recs[i].label);
-    }
-
-    /* Free the fstab_recs array created by calloc(3) */
-    free(fstab->recs);
-
-    /* Free the fstab filename */
-    free(fstab->fstab_filename);
-
-    /* Free fstab */
-    free(fstab);
-}
-
 static void check_fs(char *blk_device, char *fs_type, char *target)
 {
     int status;
@@ -428,15 +116,24 @@
             umount(target);
         }
 
-        INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
+        /*
+         * Some system images do not have e2fsck for licensing reasons
+         * (e.g. recent SDK system images). Detect these and skip the check.
+         */
+        if (access(E2FSCK_BIN, X_OK)) {
+            INFO("Not running %s on %s (executable not in system image)\n",
+                 E2FSCK_BIN, blk_device);
+        } else {
+            INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
 
-        ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
-                                      &status, true, LOG_KLOG | LOG_FILE,
-                                      true, FSCK_LOG_FILE);
+            ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
+                                        &status, true, LOG_KLOG | LOG_FILE,
+                                        true, FSCK_LOG_FILE);
 
-        if (ret < 0) {
-            /* No need to check for error in fork, we can't really handle it now */
-            ERROR("Failed trying to run %s\n", E2FSCK_BIN);
+            if (ret < 0) {
+                /* No need to check for error in fork, we can't really handle it now */
+                ERROR("Failed trying to run %s\n", E2FSCK_BIN);
+            }
         }
     }
 
@@ -823,71 +520,3 @@
 
     return 0;
 }
-
-/* Add an entry to the fstab, and return 0 on success or -1 on error */
-int fs_mgr_add_entry(struct fstab *fstab,
-                     const char *mount_point, const char *fs_type,
-                     const char *blk_device, long long length)
-{
-    struct fstab_rec *new_fstab_recs;
-    int n = fstab->num_entries;
-
-    new_fstab_recs = (struct fstab_rec *)
-                     realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
-
-    if (!new_fstab_recs) {
-        return -1;
-    }
-
-    /* A new entry was added, so initialize it */
-     memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
-     new_fstab_recs[n].mount_point = strdup(mount_point);
-     new_fstab_recs[n].fs_type = strdup(fs_type);
-     new_fstab_recs[n].blk_device = strdup(blk_device);
-     new_fstab_recs[n].length = 0;
-
-     /* Update the fstab struct */
-     fstab->recs = new_fstab_recs;
-     fstab->num_entries++;
-
-     return 0;
-}
-
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
-{
-    int i;
-
-    if (!fstab) {
-        return NULL;
-    }
-
-    for (i = 0; i < fstab->num_entries; i++) {
-        int len = strlen(fstab->recs[i].mount_point);
-        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
-            (path[len] == '\0' || path[len] == '/')) {
-            return &fstab->recs[i];
-        }
-    }
-
-    return NULL;
-}
-
-int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_VOLDMANAGED;
-}
-
-int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NONREMOVABLE;
-}
-
-int fs_mgr_is_encryptable(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_CRYPT;
-}
-
-int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
-{
-    return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
-}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
new file mode 100644
index 0000000..6145771
--- /dev/null
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+
+#include "fs_mgr_priv.h"
+
+struct fs_mgr_flag_values {
+    char *key_loc;
+    long long part_length;
+    char *label;
+    int partnum;
+    int swap_prio;
+    unsigned int zram_size;
+};
+
+struct flag_list {
+    const char *name;
+    unsigned flag;
+};
+
+static struct flag_list mount_flags[] = {
+    { "noatime",    MS_NOATIME },
+    { "noexec",     MS_NOEXEC },
+    { "nosuid",     MS_NOSUID },
+    { "nodev",      MS_NODEV },
+    { "nodiratime", MS_NODIRATIME },
+    { "ro",         MS_RDONLY },
+    { "rw",         0 },
+    { "remount",    MS_REMOUNT },
+    { "bind",       MS_BIND },
+    { "rec",        MS_REC },
+    { "unbindable", MS_UNBINDABLE },
+    { "private",    MS_PRIVATE },
+    { "slave",      MS_SLAVE },
+    { "shared",     MS_SHARED },
+    { "defaults",   0 },
+    { 0,            0 },
+};
+
+static struct flag_list fs_mgr_flags[] = {
+    { "wait",        MF_WAIT },
+    { "check",       MF_CHECK },
+    { "encryptable=",MF_CRYPT },
+    { "nonremovable",MF_NONREMOVABLE },
+    { "voldmanaged=",MF_VOLDMANAGED},
+    { "length=",     MF_LENGTH },
+    { "recoveryonly",MF_RECOVERYONLY },
+    { "swapprio=",   MF_SWAPPRIO },
+    { "zramsize=",   MF_ZRAMSIZE },
+    { "verify",      MF_VERIFY },
+    { "noemulatedsd", MF_NOEMULATEDSD },
+    { "defaults",    0 },
+    { 0,             0 },
+};
+
+static int parse_flags(char *flags, struct flag_list *fl,
+                       struct fs_mgr_flag_values *flag_vals,
+                       char *fs_options, int fs_options_len)
+{
+    int f = 0;
+    int i;
+    char *p;
+    char *savep;
+
+    /* initialize flag values.  If we find a relevant flag, we'll
+     * update the value */
+    if (flag_vals) {
+        memset(flag_vals, 0, sizeof(*flag_vals));
+        flag_vals->partnum = -1;
+        flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+    }
+
+    /* initialize fs_options to the null string */
+    if (fs_options && (fs_options_len > 0)) {
+        fs_options[0] = '\0';
+    }
+
+    p = strtok_r(flags, ",", &savep);
+    while (p) {
+        /* Look for the flag "p" in the flag list "fl"
+         * If not found, the loop exits with fl[i].name being null.
+         */
+        for (i = 0; fl[i].name; i++) {
+            if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
+                f |= fl[i].flag;
+                if ((fl[i].flag == MF_CRYPT) && flag_vals) {
+                    /* The encryptable flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
+                    /* The length flag is followed by an = and the
+                     * size of the partition.  Get it and return it.
+                     */
+                    flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
+                    /* The voldmanaged flag is followed by an = and the
+                     * label, a colon and the partition number or the
+                     * word "auto", e.g.
+                     *   voldmanaged=sdcard:3
+                     * Get and return them.
+                     */
+                    char *label_start;
+                    char *label_end;
+                    char *part_start;
+
+                    label_start = strchr(p, '=') + 1;
+                    label_end = strchr(p, ':');
+                    if (label_end) {
+                        flag_vals->label = strndup(label_start,
+                                                   (int) (label_end - label_start));
+                        part_start = strchr(p, ':') + 1;
+                        if (!strcmp(part_start, "auto")) {
+                            flag_vals->partnum = -1;
+                        } else {
+                            flag_vals->partnum = strtol(part_start, NULL, 0);
+                        }
+                    } else {
+                        ERROR("Warning: voldmanaged= flag malformed\n");
+                    }
+                } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
+                    flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
+                } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
+                    flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0);
+                }
+                break;
+            }
+        }
+
+        if (!fl[i].name) {
+            if (fs_options) {
+                /* It's not a known flag, so it must be a filesystem specific
+                 * option.  Add it to fs_options if it was passed in.
+                 */
+                strlcat(fs_options, p, fs_options_len);
+                strlcat(fs_options, ",", fs_options_len);
+            } else {
+                /* fs_options was not passed in, so if the flag is unknown
+                 * it's an error.
+                 */
+                ERROR("Warning: unknown flag %s\n", p);
+            }
+        }
+        p = strtok_r(NULL, ",", &savep);
+    }
+
+out:
+    if (fs_options && fs_options[0]) {
+        /* remove the last trailing comma from the list of options */
+        fs_options[strlen(fs_options) - 1] = '\0';
+    }
+
+    return f;
+}
+
+struct fstab *fs_mgr_read_fstab(const char *fstab_path)
+{
+    FILE *fstab_file;
+    int cnt, entries;
+    ssize_t len;
+    size_t alloc_len = 0;
+    char *line = NULL;
+    const char *delim = " \t";
+    char *save_ptr, *p;
+    struct fstab *fstab = NULL;
+    struct fstab_rec *recs;
+    struct fs_mgr_flag_values flag_vals;
+#define FS_OPTIONS_LEN 1024
+    char tmp_fs_options[FS_OPTIONS_LEN];
+
+    fstab_file = fopen(fstab_path, "r");
+    if (!fstab_file) {
+        ERROR("Cannot open file %s\n", fstab_path);
+        return 0;
+    }
+
+    entries = 0;
+    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+        entries++;
+    }
+
+    if (!entries) {
+        ERROR("No entries found in fstab\n");
+        goto err;
+    }
+
+    /* Allocate and init the fstab structure */
+    fstab = calloc(1, sizeof(struct fstab));
+    fstab->num_entries = entries;
+    fstab->fstab_filename = strdup(fstab_path);
+    fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
+
+    fseek(fstab_file, 0, SEEK_SET);
+
+    cnt = 0;
+    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+
+        /* If a non-comment entry is greater than the size we allocated, give an
+         * error and quit.  This can happen in the unlikely case the file changes
+         * between the two reads.
+         */
+        if (cnt >= entries) {
+            ERROR("Tried to process more entries than counted\n");
+            break;
+        }
+
+        if (!(p = strtok_r(line, delim, &save_ptr))) {
+            ERROR("Error parsing mount source\n");
+            goto err;
+        }
+        fstab->recs[cnt].blk_device = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing mount_point\n");
+            goto err;
+        }
+        fstab->recs[cnt].mount_point = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing fs_type\n");
+            goto err;
+        }
+        fstab->recs[cnt].fs_type = strdup(p);
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing mount_flags\n");
+            goto err;
+        }
+        tmp_fs_options[0] = '\0';
+        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
+                                       tmp_fs_options, FS_OPTIONS_LEN);
+
+        /* fs_options are optional */
+        if (tmp_fs_options[0]) {
+            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
+        } else {
+            fstab->recs[cnt].fs_options = NULL;
+        }
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            ERROR("Error parsing fs_mgr_options\n");
+            goto err;
+        }
+        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
+                                                    &flag_vals, NULL, 0);
+        fstab->recs[cnt].key_loc = flag_vals.key_loc;
+        fstab->recs[cnt].length = flag_vals.part_length;
+        fstab->recs[cnt].label = flag_vals.label;
+        fstab->recs[cnt].partnum = flag_vals.partnum;
+        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
+        fstab->recs[cnt].zram_size = flag_vals.zram_size;
+        cnt++;
+    }
+    fclose(fstab_file);
+    free(line);
+    return fstab;
+
+err:
+    fclose(fstab_file);
+    free(line);
+    if (fstab)
+        fs_mgr_free_fstab(fstab);
+    return NULL;
+}
+
+void fs_mgr_free_fstab(struct fstab *fstab)
+{
+    int i;
+
+    if (!fstab) {
+        return;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        /* Free the pointers return by strdup(3) */
+        free(fstab->recs[i].blk_device);
+        free(fstab->recs[i].mount_point);
+        free(fstab->recs[i].fs_type);
+        free(fstab->recs[i].fs_options);
+        free(fstab->recs[i].key_loc);
+        free(fstab->recs[i].label);
+    }
+
+    /* Free the fstab_recs array created by calloc(3) */
+    free(fstab->recs);
+
+    /* Free the fstab filename */
+    free(fstab->fstab_filename);
+
+    /* Free fstab */
+    free(fstab);
+}
+
+/* Add an entry to the fstab, and return 0 on success or -1 on error */
+int fs_mgr_add_entry(struct fstab *fstab,
+                     const char *mount_point, const char *fs_type,
+                     const char *blk_device, long long length)
+{
+    struct fstab_rec *new_fstab_recs;
+    int n = fstab->num_entries;
+
+    new_fstab_recs = (struct fstab_rec *)
+                     realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
+
+    if (!new_fstab_recs) {
+        return -1;
+    }
+
+    /* A new entry was added, so initialize it */
+     memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
+     new_fstab_recs[n].mount_point = strdup(mount_point);
+     new_fstab_recs[n].fs_type = strdup(fs_type);
+     new_fstab_recs[n].blk_device = strdup(blk_device);
+     new_fstab_recs[n].length = 0;
+
+     /* Update the fstab struct */
+     fstab->recs = new_fstab_recs;
+     fstab->num_entries++;
+
+     return 0;
+}
+
+struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
+{
+    int i;
+
+    if (!fstab) {
+        return NULL;
+    }
+
+    for (i = 0; i < fstab->num_entries; i++) {
+        int len = strlen(fstab->recs[i].mount_point);
+        if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
+            (path[len] == '\0' || path[len] == '/')) {
+            return &fstab->recs[i];
+        }
+    }
+
+    return NULL;
+}
+
+int fs_mgr_is_voldmanaged(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+}
+
+int fs_mgr_is_nonremovable(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+}
+
+int fs_mgr_is_encryptable(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_CRYPT;
+}
+
+int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
+}
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index 1549316..40bc2ec 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -36,8 +36,7 @@
 #include "mincrypt/sha.h"
 #include "mincrypt/sha256.h"
 
-#include "ext4_utils.h"
-#include "ext4.h"
+#include "ext4_sb.h"
 
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_verity.h"
@@ -122,6 +121,7 @@
 {
     int data_device;
     struct ext4_super_block sb;
+    struct fs_info info = {0};
 
     data_device = open(blk_device, O_RDONLY);
     if (data_device < 0) {
@@ -141,7 +141,7 @@
         return -1;
     }
 
-    ext4_parse_sb(&sb);
+    ext4_parse_sb(&sb, &info);
     *device_size = info.len;
 
     close(data_device);
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index bd4134c..3c3a482 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -17,6 +17,7 @@
 #ifndef _BACKTRACE_BACKTRACE_H
 #define _BACKTRACE_BACKTRACE_H
 
+#include <inttypes.h>
 #include <stdint.h>
 
 #include <string>
@@ -25,6 +26,14 @@
 #include <backtrace/backtrace_constants.h>
 #include <backtrace/BacktraceMap.h>
 
+#if __LP64__
+#define PRIPTR "016" PRIxPTR
+typedef uint64_t word_t;
+#else
+#define PRIPTR "08" PRIxPTR
+typedef uint32_t word_t;
+#endif
+
 struct backtrace_frame_data_t {
   size_t num;             // The current fame number.
   uintptr_t pc;           // The absolute pc.
@@ -65,7 +74,7 @@
   virtual const backtrace_map_t* FindMap(uintptr_t pc);
 
   // Read the data at a specific address.
-  virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0;
+  virtual bool ReadWord(uintptr_t ptr, word_t* out_value) = 0;
 
   // Create a string representing the formatted line of backtrace information
   // for a single frame.
@@ -96,7 +105,7 @@
 protected:
   Backtrace(BacktraceImpl* impl, pid_t pid, BacktraceMap* map);
 
-  virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value);
+  virtual bool VerifyReadWordArgs(uintptr_t ptr, word_t* out_value);
 
   bool BuildMap();
 
diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h
index ae79e00..4f90ef1 100644
--- a/include/cutils/atomic-inline.h
+++ b/include/cutils/atomic-inline.h
@@ -47,8 +47,10 @@
 #include <cutils/atomic-arm64.h>
 #elif defined(__arm__)
 #include <cutils/atomic-arm.h>
-#elif defined(__i386__) || defined(__x86_64__)
+#elif defined(__i386__)
 #include <cutils/atomic-x86.h>
+#elif defined(__x86_64__)
+#include <cutils/atomic-x86_64.h>
 #elif defined(__mips__)
 #include <cutils/atomic-mips.h>
 #else
diff --git a/include/cutils/atomic-x86_64.h b/include/cutils/atomic-x86_64.h
new file mode 100644
index 0000000..5b5c203
--- /dev/null
+++ b/include/cutils/atomic-x86_64.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_X86_64_H
+#define ANDROID_CUTILS_ATOMIC_X86_64_H
+
+#include <stdint.h>
+
+#ifndef ANDROID_ATOMIC_INLINE
+#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline))
+#endif
+
+extern ANDROID_ATOMIC_INLINE
+void android_compiler_barrier(void)
+{
+    __asm__ __volatile__ ("" : : : "memory");
+}
+
+#if ANDROID_SMP == 0
+extern ANDROID_ATOMIC_INLINE
+void android_memory_barrier(void)
+{
+    android_compiler_barrier();
+}
+extern ANDROID_ATOMIC_INLINE
+void android_memory_store_barrier(void)
+{
+    android_compiler_barrier();
+}
+#else
+extern ANDROID_ATOMIC_INLINE
+void android_memory_barrier(void)
+{
+    __asm__ __volatile__ ("mfence" : : : "memory");
+}
+extern ANDROID_ATOMIC_INLINE
+void android_memory_store_barrier(void)
+{
+    android_compiler_barrier();
+}
+#endif
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_acquire_load(volatile const int32_t *ptr)
+{
+    int32_t value = *ptr;
+    android_compiler_barrier();
+    return value;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int64_t android_atomic_acquire_load64(volatile const int64_t *ptr)
+{
+    int64_t value = *ptr;
+    android_compiler_barrier();
+    return value;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_release_load(volatile const int32_t *ptr)
+{
+    android_memory_barrier();
+    return *ptr;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int64_t android_atomic_release_load64(volatile const int64_t *ptr)
+{
+    android_memory_barrier();
+    return *ptr;
+}
+
+extern ANDROID_ATOMIC_INLINE
+void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr)
+{
+    *ptr = value;
+    android_memory_barrier();
+}
+
+extern ANDROID_ATOMIC_INLINE
+void android_atomic_acquire_store64(int64_t value, volatile int64_t *ptr)
+{
+    *ptr = value;
+    android_memory_barrier();
+}
+
+extern ANDROID_ATOMIC_INLINE
+void android_atomic_release_store(int32_t value, volatile int32_t *ptr)
+{
+    android_compiler_barrier();
+    *ptr = value;
+}
+
+extern ANDROID_ATOMIC_INLINE
+void android_atomic_release_store64(int64_t value, volatile int64_t *ptr)
+{
+    android_compiler_barrier();
+    *ptr = value;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int android_atomic_cas(int32_t old_value, int32_t new_value,
+                       volatile int32_t *ptr)
+{
+    int32_t prev;
+    __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
+                          : "=a" (prev)
+                          : "q" (new_value), "m" (*ptr), "0" (old_value)
+                          : "memory");
+    return prev != old_value;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int64_t android_atomic_cas64(int64_t old_value, int64_t new_value,
+                             volatile int64_t *ptr)
+{
+    int64_t prev;
+    __asm__ __volatile__ ("lock; cmpxchgq %1, %2"
+                          : "=a" (prev)
+                          : "q" (new_value), "m" (*ptr), "0" (old_value)
+                          : "memory");
+    return prev != old_value;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int android_atomic_acquire_cas(int32_t old_value, int32_t new_value,
+                               volatile int32_t *ptr)
+{
+    /* Loads are not reordered with other loads. */
+    return android_atomic_cas(old_value, new_value, ptr);
+}
+
+extern ANDROID_ATOMIC_INLINE
+int64_t android_atomic_acquire_cas64(int64_t old_value, int64_t new_value,
+                                     volatile int64_t *ptr)
+{
+    /* Loads are not reordered with other loads. */
+    return android_atomic_cas64(old_value, new_value, ptr);
+}
+
+extern ANDROID_ATOMIC_INLINE
+int android_atomic_release_cas(int32_t old_value, int32_t new_value,
+                               volatile int32_t *ptr)
+{
+    /* Stores are not reordered with other stores. */
+    return android_atomic_cas(old_value, new_value, ptr);
+}
+
+extern ANDROID_ATOMIC_INLINE
+int64_t android_atomic_release_cas64(int64_t old_value, int64_t new_value,
+                                     volatile int64_t *ptr)
+{
+    /* Stores are not reordered with other stores. */
+    return android_atomic_cas64(old_value, new_value, ptr);
+}
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr)
+{
+    __asm__ __volatile__ ("lock; xaddl %0, %1"
+                          : "+r" (increment), "+m" (*ptr)
+                          : : "memory");
+    /* increment now holds the old value of *ptr */
+    return increment;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_inc(volatile int32_t *addr)
+{
+    return android_atomic_add(1, addr);
+}
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_dec(volatile int32_t *addr)
+{
+    return android_atomic_add(-1, addr);
+}
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)
+{
+    int32_t prev, status;
+    do {
+        prev = *ptr;
+        status = android_atomic_cas(prev, prev & value, ptr);
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+extern ANDROID_ATOMIC_INLINE
+int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)
+{
+    int32_t prev, status;
+    do {
+        prev = *ptr;
+        status = android_atomic_cas(prev, prev | value, ptr);
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+#endif /* ANDROID_CUTILS_ATOMIC_X86_64_H */
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
index 4eda523..af80e2c 100644
--- a/include/cutils/debugger.h
+++ b/include/cutils/debugger.h
@@ -23,7 +23,11 @@
 extern "C" {
 #endif
 
+#if __LP64__
+#define DEBUGGER_SOCKET_NAME "android:debuggerd64"
+#else
 #define DEBUGGER_SOCKET_NAME "android:debuggerd"
+#endif
 
 typedef enum {
     // dump a crash
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
index ba728ac..3635e89 100644
--- a/include/cutils/klog.h
+++ b/include/cutils/klog.h
@@ -18,22 +18,31 @@
 #define _CUTILS_KLOG_H_
 
 #include <sys/cdefs.h>
+#include <stdarg.h>
 
 __BEGIN_DECLS
 
 void klog_init(void);
+int  klog_get_level(void);
 void klog_set_level(int level);
 void klog_close(void);
 void klog_write(int level, const char *fmt, ...)
     __attribute__ ((format(printf, 2, 3)));
+void klog_vwrite(int level, const char *fmt, va_list ap);
 
 __END_DECLS
 
-#define KLOG_ERROR(tag,x...)   klog_write(3, "<3>" tag ": " x)
-#define KLOG_WARNING(tag,x...) klog_write(4, "<4>" tag ": " x)
-#define KLOG_NOTICE(tag,x...)  klog_write(5, "<5>" tag ": " x)
-#define KLOG_INFO(tag,x...)    klog_write(6, "<6>" tag ": " x)
-#define KLOG_DEBUG(tag,x...)   klog_write(7, "<7>" tag ": " x)
+#define KLOG_ERROR_LEVEL   3
+#define KLOG_WARNING_LEVEL 4
+#define KLOG_NOTICE_LEVEL  5
+#define KLOG_INFO_LEVEL    6
+#define KLOG_DEBUG_LEVEL   7
+
+#define KLOG_ERROR(tag,x...)   klog_write(KLOG_ERROR_LEVEL, "<3>" tag ": " x)
+#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL, "<4>" tag ": " x)
+#define KLOG_NOTICE(tag,x...)  klog_write(KLOG_NOTICE_LEVEL, "<5>" tag ": " x)
+#define KLOG_INFO(tag,x...)    klog_write(KLOG_INFO_LEVEL, "<6>" tag ": " x)
+#define KLOG_DEBUG(tag,x...)   klog_write(KLOG_DEBUG_LEVEL, "<7>" tag ": " x)
 
 #define KLOG_DEFAULT_LEVEL  3  /* messages <= this level are logged */
 
diff --git a/include/cutils/misc.h b/include/cutils/misc.h
index 2c48dfa..0de505f 100644
--- a/include/cutils/misc.h
+++ b/include/cutils/misc.h
@@ -28,13 +28,6 @@
          */
 extern void *load_file(const char *fn, unsigned *sz);
 
-        /* Connects your process to the system debugger daemon
-         * so that on a crash it may be logged or interactively
-         * debugged (depending on system settings).
-         */
-extern void debuggerd_connect(void);
-
-
         /* This is the range of UIDs (and GIDs) that are reserved
          * for assigning to applications.
          */
diff --git a/include/log/log.h b/include/log/log.h
index 7f952ff..d469f40 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -73,10 +73,11 @@
  * Simplified macro to send a verbose log message using the current LOG_TAG.
  */
 #ifndef ALOGV
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define ALOGV(...)   ((void)0)
+#define ALOGV(...) do { if (0) { __ALOGV(__VA_ARGS__); } } while (0)
 #else
-#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
 #endif
 #endif
 
@@ -202,10 +203,11 @@
  * Simplified macro to send a verbose system log message using the current LOG_TAG.
  */
 #ifndef SLOGV
+#define __SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define SLOGV(...)   ((void)0)
+#define SLOGV(...) do { if (0) { __SLOGV(__VA_ARGS__); } } while (0)
 #else
-#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#define SLOGV(...) __SLOGV(__VA_ARGS__)
 #endif
 #endif
 
@@ -284,10 +286,11 @@
  * Simplified macro to send a verbose radio log message using the current LOG_TAG.
  */
 #ifndef RLOGV
+#define __RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
 #if LOG_NDEBUG
-#define RLOGV(...)   ((void)0)
+#define RLOGV(...) do { if (0) { __RLOGV(__VA_ARGS__); } } while (0)
 #else
-#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#define RLOGV(...) __RLOGV(__VA_ARGS__)
 #endif
 #endif
 
@@ -557,7 +560,11 @@
  * Send a simple string to the log.
  */
 int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...);
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 4, 5)))
+#endif
+    ;
 
 #ifdef __cplusplus
 }
diff --git a/include/log/log_read.h b/include/log/log_read.h
index 861c192..7edfe3c 100644
--- a/include/log/log_read.h
+++ b/include/log/log_read.h
@@ -19,21 +19,38 @@
 
 #include <time.h>
 
+/* struct log_time is a wire-format variant of struct timespec */
 #define NS_PER_SEC 1000000000ULL
 #ifdef __cplusplus
-struct log_time : public timespec {
+struct log_time {
 public:
-    log_time(timespec &T)
+    uint32_t tv_sec; // good to Feb 5 2106
+    uint32_t tv_nsec;
+
+    log_time(const timespec &T)
     {
         tv_sec = T.tv_sec;
         tv_nsec = T.tv_nsec;
     }
-    log_time(void)
+    log_time(const log_time &T)
+    {
+        tv_sec = T.tv_sec;
+        tv_nsec = T.tv_nsec;
+    }
+    log_time(uint32_t sec, uint32_t nsec)
+    {
+        tv_sec = sec;
+        tv_nsec = nsec;
+    }
+    log_time()
     {
     }
     log_time(clockid_t id)
     {
-        clock_gettime(id, (timespec *) this);
+        timespec T;
+        clock_gettime(id, &T);
+        tv_sec = T.tv_sec;
+        tv_nsec = T.tv_nsec;
     }
     log_time(const char *T)
     {
@@ -41,6 +58,8 @@
         tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
         tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24);
     }
+
+    // timespec
     bool operator== (const timespec &T) const
     {
         return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
@@ -67,13 +86,45 @@
     {
         return !(*this > T);
     }
-    uint64_t nsec(void) const
+
+    // log_time
+    bool operator== (const log_time &T) const
+    {
+        return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec);
+    }
+    bool operator!= (const log_time &T) const
+    {
+        return !(*this == T);
+    }
+    bool operator< (const log_time &T) const
+    {
+        return (tv_sec < T.tv_sec)
+            || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec));
+    }
+    bool operator>= (const log_time &T) const
+    {
+        return !(*this < T);
+    }
+    bool operator> (const log_time &T) const
+    {
+        return (tv_sec > T.tv_sec)
+            || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec));
+    }
+    bool operator<= (const log_time &T) const
+    {
+        return !(*this > T);
+    }
+
+    uint64_t nsec() const
     {
         return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
     }
-};
+} __attribute__((__packed__));
 #else
-typedef struct timespec log_time;
+typedef struct log_time {
+    uint32_t tv_sec;
+    uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
 #endif
 
 #endif /* define _LIBS_LOG_LOG_READ_H */
diff --git a/include/log/logger.h b/include/log/logger.h
index 966397a..8dab234 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -30,12 +30,12 @@
     int32_t     sec;    /* seconds since Epoch */
     int32_t     nsec;   /* nanoseconds */
     char        msg[0]; /* the entry's payload */
-};
+} __attribute__((__packed__));
 
 /*
  * The userspace structure for version 2 of the logger_entry ABI.
  * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION)
- * is called with version==2
+ * is called with version==2; or used with the user space log daemon.
  */
 struct logger_entry_v2 {
     uint16_t    len;       /* length of the payload */
@@ -46,7 +46,18 @@
     int32_t     nsec;      /* nanoseconds */
     uint32_t    euid;      /* effective UID of logger */
     char        msg[0];    /* the entry's payload */
-};
+} __attribute__((__packed__));
+
+struct logger_entry_v3 {
+    uint16_t    len;       /* length of the payload */
+    uint16_t    hdr_size;  /* sizeof(struct logger_entry_v3) */
+    int32_t     pid;       /* generating process's pid */
+    int32_t     tid;       /* generating process's tid */
+    int32_t     sec;       /* seconds since Epoch */
+    int32_t     nsec;      /* nanoseconds */
+    uint32_t    lid;       /* log id of the payload */
+    char        msg[0];    /* the entry's payload */
+} __attribute__((__packed__));
 
 /*
  * The maximum size of the log entry payload that can be
@@ -68,13 +79,10 @@
 struct log_msg {
     union {
         unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
-        struct logger_entry_v2 entry;
+        struct logger_entry_v3 entry;
+        struct logger_entry_v3 entry_v3;
         struct logger_entry_v2 entry_v2;
         struct logger_entry    entry_v1;
-        struct {
-            unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
-            log_id_t id;
-        } extra;
     } __attribute__((aligned(4)));
 #ifdef __cplusplus
     /* Matching log_time operators */
@@ -106,21 +114,21 @@
     {
         return !(*this > T);
     }
-    uint64_t nsec(void) const
+    uint64_t nsec() const
     {
         return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
     }
 
     /* packet methods */
-    log_id_t id(void)
+    log_id_t id()
     {
-        return extra.id;
+        return (log_id_t) entry.lid;
     }
-    char *msg(void)
+    char *msg()
     {
         return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg;
     }
-    unsigned int len(void)
+    unsigned int len()
     {
         return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len;
     }
@@ -132,12 +140,24 @@
 log_id_t android_logger_get_id(struct logger *logger);
 
 int android_logger_clear(struct logger *logger);
-int android_logger_get_log_size(struct logger *logger);
-int android_logger_get_log_readable_size(struct logger *logger);
+long android_logger_get_log_size(struct logger *logger);
+#ifdef USERDEBUG_BUILD
+int android_logger_set_log_size(struct logger *logger, unsigned long size);
+#endif
+long android_logger_get_log_readable_size(struct logger *logger);
 int android_logger_get_log_version(struct logger *logger);
 
 struct logger_list;
 
+ssize_t android_logger_get_statistics(struct logger_list *logger_list,
+                                      char *buf, size_t len);
+#ifdef USERDEBUG_BUILD
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list,
+                                      char *buf, size_t len);
+int android_logger_set_prune_list(struct logger_list *logger_list,
+                                  char *buf, size_t len);
+#endif
+
 struct logger_list *android_logger_list_alloc(int mode,
                                               unsigned int tail,
                                               pid_t pid);
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 0ed0d78..9c26baf 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -76,6 +76,7 @@
 #define AID_SDCARD_PICS   1033  /* external storage photos access */
 #define AID_SDCARD_AV     1034  /* external storage audio/video access */
 #define AID_SDCARD_ALL    1035  /* access all users external storage */
+#define AID_LOGD          1036  /* log daemon */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -151,6 +152,7 @@
     { "sdcard_pics",   AID_SDCARD_PICS, },
     { "sdcard_av",     AID_SDCARD_AV, },
     { "sdcard_all",    AID_SDCARD_ALL, },
+    { "logd",          AID_LOGD, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h
index c204a0f..bc93b86 100644
--- a/include/sysutils/SocketListener.h
+++ b/include/sysutils/SocketListener.h
@@ -38,6 +38,7 @@
 
     virtual ~SocketListener();
     int startListener();
+    int startListener(int backlog);
     int stopListener();
 
     void sendBroadcast(int code, const char *msg, bool addErrno);
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
index 0b75b19..46173db 100644
--- a/include/utils/Errors.h
+++ b/include/utils/Errors.h
@@ -41,23 +41,23 @@
 #ifdef _WIN32
 # undef NO_ERROR
 #endif
- 
+
 enum {
     OK                = 0,    // Everything's swell.
     NO_ERROR          = 0,    // No errors.
-    
-    UNKNOWN_ERROR       = 0x80000000,
+
+    UNKNOWN_ERROR       = (-2147483647-1), // INT32_MIN value
 
     NO_MEMORY           = -ENOMEM,
     INVALID_OPERATION   = -ENOSYS,
     BAD_VALUE           = -EINVAL,
-    BAD_TYPE            = 0x80000001,
+    BAD_TYPE            = (UNKNOWN_ERROR + 1),
     NAME_NOT_FOUND      = -ENOENT,
     PERMISSION_DENIED   = -EPERM,
     NO_INIT             = -ENODEV,
     ALREADY_EXISTS      = -EEXIST,
     DEAD_OBJECT         = -EPIPE,
-    FAILED_TRANSACTION  = 0x80000002,
+    FAILED_TRANSACTION  = (UNKNOWN_ERROR + 2),
     JPARKS_BROKE_IT     = -EPIPE,
 #if !defined(HAVE_MS_C_RUNTIME)
     BAD_INDEX           = -EOVERFLOW,
@@ -67,12 +67,12 @@
     UNKNOWN_TRANSACTION = -EBADMSG,
 #else    
     BAD_INDEX           = -E2BIG,
-    NOT_ENOUGH_DATA     = 0x80000003,
-    WOULD_BLOCK         = 0x80000004,
-    TIMED_OUT           = 0x80000005,
-    UNKNOWN_TRANSACTION = 0x80000006,
+    NOT_ENOUGH_DATA     = (UNKNOWN_ERROR + 3),
+    WOULD_BLOCK         = (UNKNOWN_ERROR + 4),
+    TIMED_OUT           = (UNKNOWN_ERROR + 5),
+    UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
 #endif    
-    FDS_NOT_ALLOWED     = 0x80000007,
+    FDS_NOT_ALLOWED     = (UNKNOWN_ERROR + 7),
 };
 
 // Restore define; enumeration is in "android" namespace, so the value defined
diff --git a/init/Android.mk b/init/Android.mk
index 1f43ba6..740d10f 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -26,6 +26,9 @@
 LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1
 endif
 
+# Enable ueventd logging
+#LOCAL_CFLAGS += -DLOG_UEVENTS=1
+
 LOCAL_MODULE:= init
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -42,6 +45,8 @@
 	libmincrypt \
 	libext4_utils_static
 
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+
 include $(BUILD_EXECUTABLE)
 
 # Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init
diff --git a/init/devices.c b/init/devices.c
index f7df453..80c6d75 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2007-2014 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.
@@ -37,7 +37,6 @@
 
 #include <private/android_filesystem_config.h>
 #include <sys/time.h>
-#include <asm/page.h>
 #include <sys/wait.h>
 
 #include <cutils/list.h>
@@ -48,6 +47,8 @@
 #include "util.h"
 #include "log.h"
 
+#define UNUSED __attribute__((__unused__))
+
 #define SYSFS_PREFIX    "/sys"
 #define FIRMWARE_DIR1   "/etc/firmware"
 #define FIRMWARE_DIR2   "/vendor/firmware"
@@ -194,7 +195,7 @@
 }
 
 static void make_device(const char *path,
-                        const char *upath,
+                        const char *upath UNUSED,
                         int block, int major, int minor)
 {
     unsigned uid;
@@ -590,6 +591,11 @@
     mkdir_recursive(dir, 0755);
 }
 
+static inline void __attribute__((__deprecated__)) kernel_logger()
+{
+    INFO("kernel logger is deprecated\n");
+}
+
 static void handle_generic_device_event(struct uevent *uevent)
 {
     char *base;
@@ -676,6 +682,7 @@
          make_dir(base, 0755);
      } else if(!strncmp(uevent->subsystem, "misc", 4) &&
                  !strncmp(name, "log_", 4)) {
+         kernel_logger();
          base = "/dev/log/";
          make_dir(base, 0755);
          name += 4;
diff --git a/init/init.c b/init/init.c
index 00f4558..0884236 100644
--- a/init/init.c
+++ b/init/init.c
@@ -933,12 +933,33 @@
     return 0;
 }
 
-int audit_callback(void *data, security_class_t cls, char *buf, size_t len)
+static int audit_callback(void *data, security_class_t cls __attribute__((unused)), char *buf, size_t len)
 {
     snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
     return 0;
 }
 
+static int log_callback(int type, const char *fmt, ...)
+{
+    int level;
+    va_list ap;
+    switch (type) {
+    case SELINUX_WARNING:
+        level = KLOG_WARNING_LEVEL;
+        break;
+    case SELINUX_INFO:
+        level = KLOG_INFO_LEVEL;
+        break;
+    default:
+        level = KLOG_ERROR_LEVEL;
+        break;
+    }
+    va_start(ap, fmt);
+    klog_vwrite(level, fmt, ap);
+    va_end(ap);
+    return 0;
+}
+
 static void selinux_initialize(void)
 {
     if (selinux_is_disabled()) {
@@ -1012,7 +1033,7 @@
     process_kernel_cmdline();
 
     union selinux_callback cb;
-    cb.func_log = klog_write;
+    cb.func_log = log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
     cb.func_audit = audit_callback;
diff --git a/init/log.h b/init/log.h
index 4aac3df..0ba770f 100644
--- a/init/log.h
+++ b/init/log.h
@@ -23,6 +23,4 @@
 #define NOTICE(x...)  KLOG_NOTICE("init", x)
 #define INFO(x...)    KLOG_INFO("init", x)
 
-#define LOG_UEVENTS        0  /* log uevent messages if 1. verbose */
-
 #endif
diff --git a/init/ueventd.c b/init/ueventd.c
index 3d01836..5517448 100644
--- a/init/ueventd.c
+++ b/init/ueventd.c
@@ -69,6 +69,12 @@
 
     open_devnull_stdio();
     klog_init();
+#if LOG_UEVENTS
+    /* Ensure we're at a logging level that will show the events */
+    if (klog_get_level() < KLOG_LEVEL_INFO) {
+        klog_set_level(KLOG_LEVEL_INFO);
+    }
+#endif
 
     INFO("starting ueventd\n");
 
diff --git a/init/util.c b/init/util.c
index e772342..0f69e1c 100644
--- a/init/util.c
+++ b/init/util.c
@@ -527,10 +527,10 @@
 
 int restorecon(const char* pathname)
 {
-    return selinux_android_restorecon(pathname);
+    return selinux_android_restorecon(pathname, 0);
 }
 
 int restorecon_recursive(const char* pathname)
 {
-    return selinux_android_restorecon_recursive(pathname);
+    return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE);
 }
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
new file mode 100644
index 0000000..3c80cc2
--- /dev/null
+++ b/libbacktrace/Android.build.mk
@@ -0,0 +1,73 @@
+#
+# Copyright (C) 2014 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 $(CLEAR_VARS)
+
+LOCAL_MODULE := $(module)
+LOCAL_MODULE_TAGS := $(module_tag)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+    $(LOCAL_PATH)/Android.mk \
+    $(LOCAL_PATH)/Android.build.mk \
+
+LOCAL_CFLAGS := \
+    $(common_cflags) \
+    $($(module)_cflags) \
+    $($(module)_cflags_$(build_type)) \
+
+LOCAL_CONLYFLAGS += \
+    $(common_conlyflags) \
+    $($(module)_conlyflags) \
+    $($(module)_conlyflags_$(build_type)) \
+
+LOCAL_CPPFLAGS += \
+    $(common_cppflags) \
+    $($(module)_cppflags) \
+    $($(module)_cppflags_$(build_type)) \
+
+LOCAL_C_INCLUDES := \
+    $(common_c_includes) \
+    $($(module)_c_includes) \
+    $($(module)_c_includes_$(build_type)) \
+
+LOCAL_SRC_FILES := \
+    $($(module)_src_files) \
+    $($(module)_src_files_$(build_type)) \
+
+LOCAL_STATIC_LIBRARIES := \
+    $($(module)_static_libraries) \
+    $($(module)_static_libraries_$(build_type)) \
+
+LOCAL_SHARED_LIBRARIES := \
+    $($(module)_shared_libraries) \
+    $($(module)_shared_libraries_$(build_type)) \
+
+LOCAL_LDLIBS := \
+    $($(module)_ldlibs) \
+    $($(module)_ldlibs_$(build_type)) \
+
+ifeq ($(build_type),target)
+  include external/stlport/libstlport.mk
+
+  include $(BUILD_$(build_target))
+endif
+
+ifeq ($(build_type),host)
+  # Only build if host builds are supported.
+  ifeq ($(build_host),true)
+    include $(BUILD_HOST_$(build_target))
+  endif
+endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
old mode 100644
new mode 100755
index 0ae8839..2e56756
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -1,14 +1,23 @@
-LOCAL_PATH:= $(call my-dir)
+#
+# Copyright (C) 2014 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.
+#
 
-common_src := \
-	BacktraceImpl.cpp \
-	BacktraceMap.cpp \
-	BacktraceThread.cpp \
-	thread_utils.c \
+LOCAL_PATH:= $(call my-dir)
 
 common_cflags := \
 	-Wall \
-	-Wno-unused-parameter \
 	-Werror \
 
 common_conlyflags := \
@@ -17,274 +26,148 @@
 common_cppflags := \
 	-std=gnu++11 \
 
-common_shared_libs := \
+build_host := false
+ifeq ($(HOST_OS),linux)
+ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
+build_host := true
+endif
+endif
+
+#-------------------------------------------------------------------------
+# The libbacktrace library.
+#-------------------------------------------------------------------------
+libbacktrace_src_files := \
+	BacktraceImpl.cpp \
+	BacktraceMap.cpp \
+	BacktraceThread.cpp \
+	thread_utils.c \
+
+libbacktrace_shared_libraries_target := \
 	libcutils \
 	libgccdemangle \
-	liblog \
 
 # To enable using libunwind on each arch, add it to this list.
-libunwind_architectures := arm arm64
+libunwind_architectures := arm arm64 mips x86 x86_64
 
 ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
-
-#----------------------------------------------------------------------------
-# The native libbacktrace library with libunwind.
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	$(common_src) \
+libbacktrace_src_files += \
 	UnwindCurrent.cpp \
 	UnwindMap.cpp \
 	UnwindPtrace.cpp \
 
-LOCAL_CFLAGS := \
-	$(common_cflags) \
-
-LOCAL_CONLYFLAGS += \
-	$(common_conlyflags) \
-
-LOCAL_CPPFLAGS += \
-	$(common_cppflags) \
-
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_C_INCLUDES := \
-	$(common_c_includes) \
+libbacktrace_c_includes := \
 	external/libunwind/include \
 
-LOCAL_SHARED_LIBRARIES := \
-	$(common_shared_libs) \
+libbacktrace_shared_libraries := \
 	libunwind \
 	libunwind-ptrace \
 
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
-
-include external/stlport/libstlport.mk
-
-include $(BUILD_SHARED_LIBRARY)
-
-else
-
-#----------------------------------------------------------------------------
-# The native libbacktrace library with libcorkscrew.
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	$(common_src) \
-	Corkscrew.cpp \
-
-LOCAL_CFLAGS := \
-	$(common_cflags) \
-
-LOCAL_CONLYFLAGS += \
-	$(common_conlyflags) \
-
-LOCAL_CPPFLAGS += \
-	$(common_cppflags) \
-
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_C_INCLUDES := \
-	$(common_c_includes) \
-	system/core/libcorkscrew \
-
-LOCAL_SHARED_LIBRARIES := \
-	$(common_shared_libs) \
-	libcorkscrew \
-	libdl \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
-
-include external/stlport/libstlport.mk
-
-include $(BUILD_SHARED_LIBRARY)
-
-endif
-
-#----------------------------------------------------------------------------
-# libbacktrace test library, all optimizations turned off
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
-	backtrace_testlib.c
-
-LOCAL_CFLAGS += \
-	-std=gnu99 \
-	-O0 \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
-
-include $(BUILD_SHARED_LIBRARY)
-
-#----------------------------------------------------------------------------
-# libbacktrace test executable
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := backtrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
-	backtrace_test.cpp \
-	thread_utils.c \
-
-LOCAL_CFLAGS += \
-	$(common_cflags) \
-	-fno-builtin \
-	-fstack-protector-all \
-	-O0 \
-	-g \
-	-DGTEST_OS_LINUX_ANDROID \
-	-DGTEST_HAS_STD_STRING \
-
-ifeq ($(TARGET_ARCH),arm64)
-  $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain)
-  LOCAL_CFLAGS += -fno-stack-protector
-endif # arm64
-
-LOCAL_CONLYFLAGS += \
-	$(common_conlyflags) \
-
-LOCAL_CPPFLAGS += \
-	$(common_cppflags) \
-
-LOCAL_SHARED_LIBRARIES += \
-	libcutils \
-	libbacktrace_test \
-	libbacktrace \
-
-LOCAL_LDLIBS := \
-	-lpthread \
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
-
-include $(BUILD_NATIVE_TEST)
-
-#----------------------------------------------------------------------------
-# Only x86 host versions of libbacktrace supported.
-#----------------------------------------------------------------------------
-ifeq ($(HOST_ARCH),x86)
-
-#----------------------------------------------------------------------------
-# The host libbacktrace library using libcorkscrew
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-
-LOCAL_CFLAGS += \
-	$(common_cflags) \
-
-LOCAL_CONLYFLAGS += \
-	$(common_conlyflags) \
-
-LOCAL_C_INCLUDES := \
-	$(common_c_includes) \
-
-LOCAL_SHARED_LIBRARIES := \
-	libgccdemangle \
+libbacktrace_shared_libraries_host := \
 	liblog \
 
-LOCAL_MODULE := libbacktrace
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
-
-ifeq ($(HOST_OS),linux)
-LOCAL_SRC_FILES += \
-	$(common_src) \
-	Corkscrew.cpp \
-
-LOCAL_C_INCLUDES += \
-	system/core/libcorkscrew \
-
-LOCAL_SHARED_LIBRARIES := \
-	libcorkscrew \
-
-LOCAL_CPPFLAGS += \
-	$(common_cppflags) \
-
-LOCAL_LDLIBS += \
-	-ldl \
-	-lrt \
+libbacktrace_static_libraries_host := \
+	libcutils \
 
 else
-LOCAL_SRC_FILES += \
-	BacktraceMap.cpp \
+libbacktrace_src_files += \
+	Corkscrew.cpp \
+
+libbacktrace_c_includes := \
+	system/core/libcorkscrew \
+
+libbacktrace_shared_libraries := \
+	libcorkscrew \
+
+libbacktrace_shared_libraries_target += \
+	libdl \
+
+libbacktrace_ldlibs_host := \
+	-ldl \
 
 endif
 
-include $(BUILD_HOST_SHARED_LIBRARY)
+module := libbacktrace
+module_tag := optional
+build_type := target
+build_target := SHARED_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+include $(LOCAL_PATH)/Android.build.mk
 
-#----------------------------------------------------------------------------
-# The host test is only supported on linux.
-#----------------------------------------------------------------------------
-ifeq ($(HOST_OS),linux)
-
-#----------------------------------------------------------------------------
-# libbacktrace host test library, all optimizations turned off
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libbacktrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
-	backtrace_testlib.c
-
-LOCAL_CFLAGS += \
-	-std=gnu99 \
+#-------------------------------------------------------------------------
+# The libbacktrace_test library needed by backtrace_test.
+#-------------------------------------------------------------------------
+libbacktrace_test_cflags := \
 	-O0 \
 
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
+libbacktrace_test_src_files := \
+	backtrace_testlib.c \
 
-include $(BUILD_HOST_SHARED_LIBRARY)
+module := libbacktrace_test
+module_tag := debug
+build_type := target
+build_target := SHARED_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+include $(LOCAL_PATH)/Android.build.mk
 
-#----------------------------------------------------------------------------
-# libbacktrace host test executable
-#----------------------------------------------------------------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := backtrace_test
-LOCAL_MODULE_FLAGS := debug
-
-LOCAL_SRC_FILES := \
-	backtrace_test.cpp \
-	thread_utils.c \
-
-LOCAL_CFLAGS += \
-	$(common_cflags) \
+#-------------------------------------------------------------------------
+# The backtrace_test executable.
+#-------------------------------------------------------------------------
+backtrace_test_cflags := \
 	-fno-builtin \
-	-fstack-protector-all \
 	-O0 \
 	-g \
 	-DGTEST_HAS_STD_STRING \
 
-LOCAL_SHARED_LIBRARIES := \
+ifneq ($(TARGET_ARCH),arm64)
+backtrace_test_cflags += -fstack-protector-all
+else
+  $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain)
+  common_cflags += -fno-stack-protector
+endif # arm64
+
+backtrace_test_cflags_target := \
+	-DGTEST_OS_LINUX_ANDROID \
+
+backtrace_test_src_files := \
+	backtrace_test.cpp \
+	thread_utils.c \
+
+backtrace_test_ldlibs := \
+	-lpthread \
+
+backtrace_test_ldlibs_host := \
+	-lrt \
+
+backtrace_test_shared_libraries := \
 	libbacktrace_test \
 	libbacktrace \
 
-LOCAL_LDLIBS := \
-	-lpthread \
+backtrace_test_shared_libraries_target := \
+	libcutils \
 
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(LOCAL_PATH)/Android.mk
+module := backtrace_test
+module_tag := debug
+build_type := target
+build_target := NATIVE_TEST
+include $(LOCAL_PATH)/Android.build.mk
+build_type := host
+include $(LOCAL_PATH)/Android.build.mk
 
-include $(BUILD_HOST_NATIVE_TEST)
+#----------------------------------------------------------------------------
+# Special truncated libbacktrace library for mac.
+#----------------------------------------------------------------------------
+ifeq ($(HOST_OS),darwin)
 
-endif # HOST_OS == linux
+include $(CLEAR_VARS)
 
-endif # HOST_ARCH == x86
+LOCAL_MODULE := libbacktrace
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+	BacktraceMap.cpp \
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+endif # HOST_OS-darwin
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
index 39296b4..855810e 100644
--- a/libbacktrace/BacktraceImpl.cpp
+++ b/libbacktrace/BacktraceImpl.cpp
@@ -21,9 +21,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-
 #include <string>
 
 #include <backtrace/Backtrace.h>
@@ -82,10 +79,10 @@
   return func_name;
 }
 
-bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) {
-  if (ptr & 3) {
+bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, word_t* out_value) {
+  if (ptr & (sizeof(word_t)-1)) {
     BACK_LOGW("invalid pointer %p", (void*)ptr);
-    *out_value = (uint32_t)-1;
+    *out_value = (word_t)-1;
     return false;
   }
   return true;
@@ -143,18 +140,18 @@
 BacktraceCurrent::~BacktraceCurrent() {
 }
 
-bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) {
+bool BacktraceCurrent::ReadWord(uintptr_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
     return false;
   }
 
   const backtrace_map_t* map = FindMap(ptr);
   if (map && map->flags & PROT_READ) {
-    *out_value = *reinterpret_cast<uint32_t*>(ptr);
+    *out_value = *reinterpret_cast<word_t*>(ptr);
     return true;
   } else {
     BACK_LOGW("pointer %p not in a readable map", reinterpret_cast<void*>(ptr));
-    *out_value = static_cast<uint32_t>(-1);
+    *out_value = static_cast<word_t>(-1);
     return false;
   }
 }
@@ -171,7 +168,7 @@
 BacktracePtrace::~BacktracePtrace() {
 }
 
-bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) {
+bool BacktracePtrace::ReadWord(uintptr_t ptr, word_t* out_value) {
   if (!VerifyReadWordArgs(ptr, out_value)) {
     return false;
   }
@@ -184,7 +181,7 @@
   // To disambiguate -1 from a valid result, we clear errno beforehand.
   errno = 0;
   *out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL);
-  if (*out_value == static_cast<uint32_t>(-1) && errno) {
+  if (*out_value == static_cast<word_t>(-1) && errno) {
     BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s",
               reinterpret_cast<void*>(ptr), Tid(), strerror(errno));
     return false;
diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h
old mode 100644
new mode 100755
index af014d5..48dd11c
--- a/libbacktrace/BacktraceImpl.h
+++ b/libbacktrace/BacktraceImpl.h
@@ -61,7 +61,7 @@
   BacktraceCurrent(BacktraceImpl* impl, BacktraceMap* map);
   virtual ~BacktraceCurrent();
 
-  bool ReadWord(uintptr_t ptr, uint32_t* out_value);
+  bool ReadWord(uintptr_t ptr, word_t* out_value);
 };
 
 class BacktracePtrace : public Backtrace {
@@ -69,7 +69,7 @@
   BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, BacktraceMap* map);
   virtual ~BacktracePtrace();
 
-  bool ReadWord(uintptr_t ptr, uint32_t* out_value);
+  bool ReadWord(uintptr_t ptr, word_t* out_value);
 };
 
 Backtrace* CreateCurrentObj(BacktraceMap* map);
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 6320800..6eb290d 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -93,7 +93,8 @@
   }
 
   ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
-        map->start, map->end, map->flags, map->name.c_str());
+        reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
+        map->flags, map->name.c_str());
   return true;
 }
 
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
old mode 100644
new mode 100755
index 17b71b9..81e69bb
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "libbacktrace"
 
+#include <sys/ucontext.h>
 #include <sys/types.h>
 
 #include <backtrace/Backtrace.h>
@@ -27,27 +28,6 @@
 #include "UnwindCurrent.h"
 #include "UnwindMap.h"
 
-// Define the ucontext_t structures needed for each supported arch.
-#if defined(__arm__)
-  // The current version of the <signal.h> doesn't define ucontext_t.
-  #include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined.
-
-  // Machine context at the time a signal was raised.
-  typedef struct ucontext {
-    uint32_t uc_flags;
-    struct ucontext* uc_link;
-    stack_t uc_stack;
-    struct sigcontext uc_mcontext;
-    uint32_t uc_sigmask;
-  } ucontext_t;
-#elif defined(__i386__)
-  #include <asm/sigcontext.h>
-  #include <asm/ucontext.h>
-  typedef struct ucontext ucontext_t;
-#elif !defined(__mips__) && !defined(__aarch64__)
-  #error Unsupported architecture.
-#endif
-
 //-------------------------------------------------------------------------
 // UnwindCurrent functions.
 //-------------------------------------------------------------------------
@@ -158,7 +138,7 @@
   context->regs[13] = uc->uc_mcontext.arm_sp;
   context->regs[14] = uc->uc_mcontext.arm_lr;
   context->regs[15] = uc->uc_mcontext.arm_pc;
-#elif defined(__mips__) || defined(__i386__)
+#else
   context->uc_mcontext = uc->uc_mcontext;
 #endif
 }
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 03bb192..8268db6 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -45,7 +45,7 @@
     pthread_mutex_lock(&g_map_mutex);
     if (--g_map_references == 0) {
       // Clear the local address space map.
-      unw_map_set(unw_local_addr_space, NULL);
+      unw_map_local_set(NULL);
       unw_map_cursor_destroy(&map_cursor_);
     }
     pthread_mutex_unlock(&g_map_mutex);
@@ -61,8 +61,8 @@
     if (g_map_references == 0) {
       return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
       if (return_value) {
-        // Set the local address space to this cursor map.
-        unw_map_set(unw_local_addr_space, &map_cursor_);
+        // Set the local address space map to our new map.
+        unw_map_local_set(&map_cursor_);
         g_map_references = 1;
         g_map_cursor = map_cursor_;
       }
diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk
index e275317..8f3b68c 100644
--- a/libcorkscrew/Android.mk
+++ b/libcorkscrew/Android.mk
@@ -30,6 +30,8 @@
 	arch-x86/backtrace-x86.c \
 	arch-x86/ptrace-x86.c
 
+ifneq ($(TARGET_IS_64_BIT),true)
+
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(generic_src_files)
@@ -66,9 +68,9 @@
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
 
+endif # TARGET_IS_64_BIT == false
 
-# TODO: reenable darwin-x86
-# ifeq ($(HOST_ARCH),x86)
+
 ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
 
 # Build libcorkscrew.
@@ -95,4 +97,4 @@
 LOCAL_MODULE_TAGS := optional
 include $(BUILD_HOST_EXECUTABLE)
 
-endif # HOST_ARCH == x86
+endif # $(HOST_OS)-$(HOST_ARCH) == linux-x86
diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c
index 7bd0d8f..751efbf 100644
--- a/libcorkscrew/arch-arm/backtrace-arm.c
+++ b/libcorkscrew/arch-arm/backtrace-arm.c
@@ -62,19 +62,7 @@
 #include <elf.h>
 #include <cutils/log.h>
 
-#if !defined(__BIONIC_HAVE_UCONTEXT_T)
-/* Old versions of the Android <signal.h> didn't define ucontext_t. */
-#include <asm/sigcontext.h> /* Ensure 'struct sigcontext' is defined. */
-
-/* Machine context at the time a signal was raised. */
-typedef struct ucontext {
-    uint32_t uc_flags;
-    struct ucontext* uc_link;
-    stack_t uc_stack;
-    struct sigcontext uc_mcontext;
-    uint32_t uc_sigmask;
-} ucontext_t;
-#endif /* !__BIONIC_HAVE_UCONTEXT_T */
+#include <ucontext.h>
 
 /* Unwind state. */
 typedef struct {
diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c
index 57cb324..832fb86 100644
--- a/libcorkscrew/arch-mips/backtrace-mips.c
+++ b/libcorkscrew/arch-mips/backtrace-mips.c
@@ -36,18 +36,7 @@
 #include <sys/ptrace.h>
 #include <cutils/log.h>
 
-#if defined(__BIONIC__)
-
-#if defined(__BIONIC_HAVE_UCONTEXT_T)
-
-// Bionic offers the Linux kernel headers.
-#include <asm/sigcontext.h>
-#include <asm/ucontext.h>
-typedef struct ucontext ucontext_t;
-
-#else /* __BIONIC_HAVE_UCONTEXT_T */
-
-/* Old versions of the Android <signal.h> didn't define ucontext_t. */
+#include <sys/ucontext.h>
 
 /* For PTRACE_GETREGS */
 typedef struct {
@@ -71,30 +60,6 @@
     REG_GP, REG_SP, REG_S8, REG_RA,
 };
 
-/* Machine context at the time a signal was raised. */
-typedef struct ucontext {
-    unsigned int sc_regmask;
-    unsigned int sc_status;
-    unsigned long long sc_pc;
-    unsigned long long sc_regs[32];
-    unsigned long long sc_fpregs[32];
-    unsigned int sc_acx;
-    unsigned int sc_fpc_csr;
-    unsigned int sc_fpc_eir;
-    unsigned int sc_used_math;
-    unsigned int sc_dsp;
-    unsigned long long sc_mdhi;
-    unsigned long long sc_mdlo;
-    unsigned long sc_hi1;
-    unsigned long sc_lo1;
-    unsigned long sc_hi2;
-    unsigned long sc_lo2;
-    unsigned long sc_hi3;
-    unsigned long sc_lo3;
-} ucontext_t;
-
-#endif /* __BIONIC_HAVE_UCONTEXT_T */
-#endif
 
 /* Unwind state. */
 typedef struct {
@@ -896,10 +861,10 @@
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
     unwind_state_t state;
-    state.reg[DWARF_PC] = uc->sc_pc;
-    state.reg[DWARF_RA] = uc->sc_regs[REG_RA];
-    state.reg[DWARF_FP] = uc->sc_regs[REG_S8];
-    state.reg[DWARF_SP] = uc->sc_regs[REG_SP];
+    state.reg[DWARF_PC] = uc->uc_mcontext.pc;
+    state.reg[DWARF_RA] = uc->uc_mcontext.gregs[REG_RA];
+    state.reg[DWARF_FP] = uc->uc_mcontext.gregs[REG_S8];
+    state.reg[DWARF_SP] = uc->uc_mcontext.gregs[REG_SP];
 
     ALOGV("unwind_backtrace_signal_arch: "
           "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index ef22821..df486de 100755
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -36,46 +36,7 @@
 #include <sys/ptrace.h>
 #include <cutils/log.h>
 
-#if defined(__BIONIC__)
-
-#if defined(__BIONIC_HAVE_UCONTEXT_T)
-
-// Bionic offers the Linux kernel headers.
-#include <asm/sigcontext.h>
-#include <asm/ucontext.h>
-typedef struct ucontext ucontext_t;
-
-#else /* __BIONIC_HAVE_UCONTEXT_T */
-
-/* Old versions of the Android <signal.h> didn't define ucontext_t. */
-
-typedef struct {
-  uint32_t  gregs[32];
-  void*     fpregs;
-  uint32_t  oldmask;
-  uint32_t  cr2;
-} mcontext_t;
-
-enum {
-  REG_GS = 0, REG_FS, REG_ES, REG_DS,
-  REG_EDI, REG_ESI, REG_EBP, REG_ESP,
-  REG_EBX, REG_EDX, REG_ECX, REG_EAX,
-  REG_TRAPNO, REG_ERR, REG_EIP, REG_CS,
-  REG_EFL, REG_UESP, REG_SS
-};
-
-/* Machine context at the time a signal was raised. */
-typedef struct ucontext {
-    uint32_t uc_flags;
-    struct ucontext* uc_link;
-    stack_t uc_stack;
-    mcontext_t uc_mcontext;
-    uint32_t uc_sigmask;
-} ucontext_t;
-
-#endif /* __BIONIC_HAVE_UCONTEXT_T */
-
-#elif defined(__APPLE__)
+#if defined(__APPLE__)
 
 #define _XOPEN_SOURCE
 #include <ucontext.h>
diff --git a/libcutils/klog.c b/libcutils/klog.c
index d69fb10..fbb7b72 100644
--- a/libcutils/klog.c
+++ b/libcutils/klog.c
@@ -28,6 +28,10 @@
 static int klog_fd = -1;
 static int klog_level = KLOG_DEFAULT_LEVEL;
 
+int klog_get_level(void) {
+    return klog_level;
+}
+
 void klog_set_level(int level) {
     klog_level = level;
 }
@@ -49,18 +53,24 @@
 
 #define LOG_BUF_MAX 512
 
-void klog_write(int level, const char *fmt, ...)
+void klog_vwrite(int level, const char *fmt, va_list ap)
 {
     char buf[LOG_BUF_MAX];
-    va_list ap;
 
     if (level > klog_level) return;
     if (klog_fd < 0) klog_init();
     if (klog_fd < 0) return;
 
-    va_start(ap, fmt);
     vsnprintf(buf, LOG_BUF_MAX, fmt, ap);
     buf[LOG_BUF_MAX - 1] = 0;
-    va_end(ap);
+
     write(klog_fd, buf, strlen(buf));
 }
+
+void klog_write(int level, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    klog_vwrite(level, fmt, ap);
+    va_end(ap);
+}
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
index 5bb8176..899a7b4 100644
--- a/libcutils/qtaguid.c
+++ b/libcutils/qtaguid.c
@@ -19,14 +19,16 @@
 
 #define LOG_TAG "qtaguid"
 
-#include <cutils/qtaguid.h>
-#include <cutils/log.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <pthread.h>
+
+#include <cutils/qtaguid.h>
+#include <log/log.h>
 
 static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
 static const int CTRL_MAX_INPUT_LEN = 128;
@@ -103,13 +105,13 @@
 
     pthread_once(&resTrackInitDone, qtaguid_resTrack);
 
-    snprintf(lineBuf, sizeof(lineBuf), "t %d %llu %d", sockfd, kTag, uid);
+    snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
 
-    ALOGV("Tagging socket %d with tag %llx{%u,0} for uid %d", sockfd, kTag, tag, uid);
+    ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid);
 
     res = write_ctrl(lineBuf);
     if (res < 0) {
-        ALOGI("Tagging socket %d with tag %llx(%d) for uid %d failed errno=%d",
+        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d",
              sockfd, kTag, tag, uid, res);
     }
 
@@ -147,14 +149,14 @@
     int fd, cnt = 0, res = 0;
     uint64_t kTag = (uint64_t)tag << 32;
 
-    ALOGV("Deleting tag data with tag %llx{%d,0} for uid %d", kTag, tag, uid);
+    ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
 
     pthread_once(&resTrackInitDone, qtaguid_resTrack);
 
-    snprintf(lineBuf, sizeof(lineBuf), "d %llu %d", kTag, uid);
+    snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
     res = write_ctrl(lineBuf);
     if (res < 0) {
-        ALOGI("Deleteing tag data with tag %llx/%d for uid %d failed with cnt=%d errno=%d",
+        ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
              kTag, tag, uid, cnt, errno);
     }
 
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
index 4971b1b..7628fe4 100644
--- a/libcutils/socket_local_server.c
+++ b/libcutils/socket_local_server.c
@@ -43,6 +43,8 @@
 
 #define LISTEN_BACKLOG 4
 
+/* Only the bottom bits are really the socket type; there are flags too. */
+#define SOCK_TYPE_MASK 0xf
 
 /**
  * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
@@ -107,7 +109,7 @@
         return -1;
     }
 
-    if (type == SOCK_STREAM) {
+    if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {
         int ret;
 
         ret = listen(s, LISTEN_BACKLOG);
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
deleted file mode 100644
index 6571161..0000000
--- a/libcutils/tests/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(all-subdir-makefiles)
diff --git a/libcutils/tests/memset_mips/Android.mk b/libcutils/tests/memset_mips/Android.mk
deleted file mode 100644
index c22fca9..0000000
--- a/libcutils/tests/memset_mips/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2012 The Android Open Source Project
-
-ifeq ($(TARGET_ARCH),mips)
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	test_memset.c \
-	android_memset_dumb.S \
-	android_memset_test.S \
-	memset_cmips.S \
-	memset_omips.S
-
-LOCAL_MODULE:= test_memset
-
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_STATIC_LIBRARIES := libcutils libc
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_EXECUTABLE)
-
-endif
diff --git a/libcutils/tests/memset_mips/android_memset_dumb.S b/libcutils/tests/memset_mips/android_memset_dumb.S
deleted file mode 100644
index c8a1a37..0000000
--- a/libcutils/tests/memset_mips/android_memset_dumb.S
+++ /dev/null
@@ -1,36 +0,0 @@
-	.global	android_memset16_dumb
-	.type   android_memset16_dumb, @function
-android_memset16_dumb:
-        .ent	android_memset16_dumb
-
-	.set	noreorder
-	beqz	$a2,9f
-	 srl	$a2,1
-
-1:	sh	$a1,($a0)
-	subu	$a2,1
-	bnez	$a2,1b
-	 addu	$a0,2
-	.set reorder
-
-9:	j	$ra
-        .end	android_memset16_dumb
-	.size	android_memset16_dumb,.-android_memset16_dumb
-
-	.global android_memset32_dumb
-	.type	android_memset32_dumb, @function
-android_memset32_dumb:
-        .ent	android_memset32_dumb
-	.set	noreorder
-	beqz	$a2,9f
-	 srl	$a2,2
-
-1:	sw	$a1,($a0)
-	subu	$a2,1
-	bnez	$a2,1b
-	 addu	$a0,4
-	.set reorder
-
-9:	j	$ra
-        .end	android_memset32_dumb
-	.size	android_memset32_dumb,.-android_memset32_dumb
diff --git a/libcutils/tests/memset_mips/android_memset_test.S b/libcutils/tests/memset_mips/android_memset_test.S
deleted file mode 100644
index e918843..0000000
--- a/libcutils/tests/memset_mips/android_memset_test.S
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-#ifdef NDEBUG
-#define DBG #
-#else
-#define DBG
-#endif
-
-	.text
-	.align
-
-        /*
-         * Optimized memset16 for MIPS
-         *
-         * void android_memset16_test(uint16_t* dst, uint16_t value, size_t size);
-         *
-         */
-
-	.global	android_memset16_test
-	.type   android_memset16_test, @function
-android_memset16_test:
-        .ent	android_memset16_test
-	.set	noreorder
-
-	/* Check parameters */
-DBG	andi	$t0,$a0,1	/* $a0 must be halfword aligned */
-DBG	tne	$t0
-DBG	lui	$t1,0xffff	/* $a1 must be 16bits */
-DBG	and	$t1,$a1
-DBG	tne	$t1
-DBG	andi	$t2,$a2,1	/* $a2 must be even */
-DBG	tne	$t2
-
-#if (__mips==32) && (__mips_isa_rev>=2)
-	ins	$a2,$0,0,1
-#else
-	li	$t0,~1
-	and	$a2,$t0
-#endif
-
-	move	$t8,$ra
-	blez	$a2,9f		/* Anything to do? */
-	 andi	$t0,$a0,2	/* Check dst alignment */
-	/* Expand value to 32 bits and check destination alignment */
-#if (__mips==32) && (__mips_isa_rev>=2)
-	beqz	$t0,.Laligned32	/* dst is 32 bit aligned */
-	 ins	$a1,$a1,16,16
-#else
-	sll	$t2,$a1,16
-	beqz	$t0,.Laligned32	/* dst is 32 bit aligned */
-	 or	$a1,$t2
-#endif
-	sh	$a1,($a0)	/* do one halfword to get aligned */
-	subu	$a2,2
-	addu	$a0,2
-
-.Laligned32:
-	and	$t1,$a2,63	/* is there enough left to do a full 64 byte loop? */
-	beq	$a2,$t1,1f
-	 subu	$t2,$a2,$t1	/* $t2 is the number of bytes to do in loop64 */
-	addu	$t3,$a0,$t2	/* $t3 is the end marker for loop64 */
-	subu	$a2,$t2
-.Lloop64:
-	addu	$a0,64
-	sw	$a1,-64($a0)
-	sw	$a1,-60($a0)
-	sw	$a1,-56($a0)
-	sw	$a1,-52($a0)
-	sw	$a1,-48($a0)
-	sw	$a1,-44($a0)
-	sw	$a1,-40($a0)
-	sw	$a1,-36($a0)
-	sw	$a1,-32($a0)
-	sw	$a1,-28($a0)
-	sw	$a1,-24($a0)
-	sw	$a1,-20($a0)
-	sw	$a1,-16($a0)
-	sw	$a1,-12($a0)
-	sw	$a1,-8($a0)
-	bne	$a0,$t3,.Lloop64
-	sw	$a1,-4($a0)
-
-	/* Do the last 0..62 bytes */
-1:	li	$t0,64+12
-	andi	$t1,$a2,0x3c	/* $t1 how many bytes to store using sw */
-	bal	1f
-	 subu	$t0,$t1		/* 64+12-$t0 is offset to jump from 1f */
-1:	addu	$ra,$t0
-	j	$ra
-	 subu	$a2,$t1
-2:	sw	$a1,60($a0)
-	sw	$a1,56($a0)
-	sw	$a1,52($a0)
-	sw	$a1,48($a0)
-	sw	$a1,44($a0)
-	sw	$a1,40($a0)
-	sw	$a1,36($a0)
-	sw	$a1,32($a0)
-	sw	$a1,28($a0)
-	sw	$a1,24($a0)
-	sw	$a1,20($a0)
-	sw	$a1,16($a0)
-	sw	$a1,12($a0)
-	sw	$a1,8($a0)
-	sw	$a1,4($a0)
-	sw	$a1,0($a0)
-
-	beqz	$a2,9f
-	 addu	$a0,$t1
-	sh	$a1,($a0)
-
-9:	j	$t8
-	 nop
-        .end	android_memset16_test
-	.size	android_memset16_test,.-android_memset16_test
-
-        /*
-         * Optimized memset32 for MIPS
-         *
-         * void android_memset32_test(uint32_t* dst, uint32_t value, size_t size);
-         *
-         */
-	.global android_memset32_test
-	.type	android_memset32_test, @function
-android_memset32_test:
-        .ent	android_memset32_test
-	.set	noreorder
-
-	/* Check parameters */
-DBG	andi	$t0,$a0,3	/* $a0 must be word aligned */
-DBG	tne	$t0
-DBG	andi	$t2,$a2,3	/* $a2 must be a multiple of 4 bytes */
-DBG	tne	$t2
-
-	b	.Laligned32
-	 move	$t8,$ra
-        .end	android_memset32_test
-	.size	android_memset32_test,.-android_memset32_test
diff --git a/libcutils/tests/memset_mips/memset_cmips.S b/libcutils/tests/memset_mips/memset_cmips.S
deleted file mode 100644
index f8f3a91..0000000
--- a/libcutils/tests/memset_mips/memset_cmips.S
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (c) 2009
- *      MIPS Technologies, Inc., California.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/************************************************************************
- *
- *  memset.S, version "64h" with 1 cache line horizon for "pref 30" and 14 nops
- *  Version: "043009"
- *
- ************************************************************************/
-
-
-/************************************************************************
- *  Include files
- ************************************************************************/
-
-#include "machine/asm.h"
-
-/*
- * This routine could be optimized for MIPS64. The current code only
- * uses MIPS32 instructions.
- */
-
-#if defined(__MIPSEB__)
-#  define SWHI	swl		/* high part is left in big-endian	*/
-#endif
-
-#if defined(__MIPSEL__)
-#  define SWHI	swr		/* high part is right in little-endian	*/
-#endif
-
-#if !(defined(XGPROF) || defined(XPROF))
-#undef SETUP_GP
-#define SETUP_GP
-#endif
-
-LEAF(memset_cmips,0)
-
-	.set	noreorder
-	.set	noat
-
-	addu	t0,a0,a2		# t0 is the "past the end" address
-	slti	AT,a2,4			# is a2 less than 4?
-	bne	AT,zero,.Llast4		# if yes, go to last4
-	move	v0,a0			# memset returns the dst pointer
-
-	beq	a1,zero,.Lset0
-	subu	v1,zero,a0
-
-	# smear byte into 32 bit word
-#if (__mips==32) && (__mips_isa_rev>=2)
-	ins     a1, a1, 8, 8        # Replicate fill byte into half-word.
-	ins     a1, a1, 16, 16      # Replicate fill byte into word.
-#else
-	and	a1,0xff
-	sll	AT,a1,8
-	or	a1,AT
-	sll	AT,a1,16
-	or	a1,AT
-#endif
-
-.Lset0:	andi	v1,v1,0x3		# word-unaligned address?
-	beq	v1,zero,.Laligned	# v1 is the unalignment count
-	subu	a2,a2,v1
-	SWHI	a1,0(a0)
-	addu	a0,a0,v1
-
-# Here we have the "word-aligned" a0 (until the "last4")
-.Laligned:
-	andi	t8,a2,0x3f	# any 64-byte chunks?
-				# t8 is the byte count past 64-byte chunks
-	beq	a2,t8,.Lchk8w	# when a2==t8, no 64-byte chunks
-				# There will be at most 1 32-byte chunk then
-	subu	a3,a2,t8	# subtract from a2 the reminder
-				# Here a3 counts bytes in 16w chunks
-	addu	a3,a0,a3	# Now a3 is the final dst after 64-byte chunks
-
-# Find out, if there are any 64-byte chunks after which will be still at least
-# 96 bytes left. The value "96" is calculated as needed buffer for
-# "pref 30,64(a0)" prefetch, which can be used as "pref 30,0(a0)" after
-# incrementing "a0" by 64.
-# For "a2" below 160 there will be no such "pref 30 safe" 64-byte chunk.
-#
-	sltiu	v1,a2,160
-	bgtz	v1,.Lloop16w_nopref30	# skip "pref 30,0(a0)"
-	subu	t7,a2,96	# subtract "pref 30 unsafe" region
-		# below we have at least 1 64-byte chunk which is "pref 30 safe"
-	andi	t6,t7,0x3f	# t6 is past "64-byte safe chunks" reminder
-	subu	t5,t7,t6	# subtract from t7 the reminder
-				# Here t5 counts bytes in 16w "safe" chunks
-	addu	t4,a0,t5	# Now t4 is the dst after 64-byte "safe" chunks
-
-# Don't use "pref 30,0(a0)" for a0 in a "middle" of a cache line
-#	pref	30,0(a0)
-# Here we are in the region, where it is safe to use "pref 30,64(a0)"
-.Lloop16w:
-	addiu	a0,a0,64
-	pref	30,-32(a0)	# continue setting up the dest, addr 64-32
-	sw	a1,-64(a0)
-	sw	a1,-60(a0)
-	sw	a1,-56(a0)
-	sw	a1,-52(a0)
-	sw	a1,-48(a0)
-	sw	a1,-44(a0)
-	sw	a1,-40(a0)
-	sw	a1,-36(a0)
-	nop
-	nop			# the extra nop instructions help to balance
-	nop			# cycles needed for "store" + "fill" + "evict"
-	nop			# For 64byte store there are needed 8 fill
-	nop			# and 8 evict cycles, i.e. at least 32 instr.
-	nop
-	nop
-	pref	30,0(a0)	# continue setting up the dest, addr 64-0
-	sw	a1,-32(a0)
-	sw	a1,-28(a0)
-	sw	a1,-24(a0)
-	sw	a1,-20(a0)
-	sw	a1,-16(a0)
-	sw	a1,-12(a0)
-	sw	a1,-8(a0)
-	sw	a1,-4(a0)
-	nop
-	nop
-	nop
-	nop			# NOTE: adding 14 nop-s instead of 12 nop-s
-	nop			# gives better results for "fast" memory
-	nop
-	bne	a0,t4,.Lloop16w
-	nop
-
-	beq	a0,a3,.Lchk8w	# maybe no more 64-byte chunks?
-	nop			# this "delayed slot" is useless ...
-
-.Lloop16w_nopref30:	# there could be up to 3 "64-byte nopref30" chunks
-	addiu	a0,a0,64
-	sw	a1,-64(a0)
-	sw	a1,-60(a0)
-	sw	a1,-56(a0)
-	sw	a1,-52(a0)
-	sw	a1,-48(a0)
-	sw	a1,-44(a0)
-	sw	a1,-40(a0)
-	sw	a1,-36(a0)
-	sw	a1,-32(a0)
-	sw	a1,-28(a0)
-	sw	a1,-24(a0)
-	sw	a1,-20(a0)
-	sw	a1,-16(a0)
-	sw	a1,-12(a0)
-	sw	a1,-8(a0)
-	bne	a0,a3,.Lloop16w_nopref30
-	sw	a1,-4(a0)
-
-.Lchk8w:		# t8 here is the byte count past 64-byte chunks
-
-	andi	t7,t8,0x1f	# is there a 32-byte chunk?
-				# the t7 is the reminder count past 32-bytes
-	beq	t8,t7,.Lchk1w	# when t8==t7, no 32-byte chunk
-	move	a2,t7
-
-	sw	a1,0(a0)
-	sw	a1,4(a0)
-	sw	a1,8(a0)
-	sw	a1,12(a0)
-	sw	a1,16(a0)
-	sw	a1,20(a0)
-	sw	a1,24(a0)
-	sw	a1,28(a0)
-	addiu	a0,a0,32
-
-.Lchk1w:
-	andi	t8,a2,0x3	# now t8 is the reminder past 1w chunks
-	beq	a2,t8,.Llast4
-	subu	a3,a2,t8	# a3 is the count of bytes in 1w chunks
-	addu	a3,a0,a3	# now a3 is the dst address past the 1w chunks
-
-# copying in words (4-byte chunks)
-.LwordCopy_loop:
-	addiu	a0,a0,4
-	bne	a0,a3,.LwordCopy_loop
-	sw	a1,-4(a0)
-
-.Llast4:beq	a0,t0,.Llast4e
-.Llast4l:addiu	a0,a0,1
-	bne	a0,t0,.Llast4l
-	sb	a1,-1(a0)
-
-.Llast4e:
-	j	ra
-	nop
-
-	.set	at
-	.set	reorder
-
-END(memset_cmips)
-
-
-/************************************************************************
- *  Implementation : Static functions
- ************************************************************************/
-
diff --git a/libcutils/tests/memset_mips/memset_omips.S b/libcutils/tests/memset_mips/memset_omips.S
deleted file mode 100644
index 4c47001..0000000
--- a/libcutils/tests/memset_mips/memset_omips.S
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Hartvig Ekner <hartvige@mips.com>, 2002.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-/* void *memset_omips(void *s, int c, size_t n).  */
-
-#include "machine/asm.h"
-
-#ifdef __mips64
-#error mips32 code being compiled for mips64!
-#endif
-
-#if defined(__MIPSEB__)
-#error big-endian is not supported in Broadcom MIPS Android platform
-# define SWHI	swl		/* high part is left in big-endian	*/
-#else
-# define SWHI	swr		/* high part is right in little-endian	*/
-#endif
-
-LEAF (memset_omips,0)
-	.set	noreorder
-
-	slti	t1, a2, 8		# Less than 8?
-	bne	t1, zero, .Llast8
-	move	v0, a0			# Setup exit value before too late
-
-	beq	a1, zero, .Lueven	# If zero pattern, no need to extend
-	andi	a1, 0xff		# Avoid problems with bogus arguments
-	sll	t0, a1, 8
-	or	a1, t0
-	sll	t0, a1, 16
-	or	a1, t0			# a1 is now pattern in full word
-
-.Lueven:
-	subu	t0, zero, a0		# Unaligned address?
-	andi	t0, 0x3
-	beq	t0, zero, .Lchkw
-	subu	a2, t0
-	SWHI	a1, 0(a0)		# Yes, handle first unaligned part
-	addu	a0, t0			# Now both a0 and a2 are updated
-
-.Lchkw:
-	andi	t0, a2, 0x7		# Enough left for one loop iteration?
-	beq	t0, a2, .Lchkl
-	subu	a3, a2, t0
-	addu	a3, a0			# a3 is last loop address +1
-	move	a2, t0			# a2 is now # of bytes left after loop
-.Lloopw:
-	addiu	a0, 8			# Handle 2 words pr. iteration
-	sw	a1, -8(a0)
-	bne	a0, a3, .Lloopw
-	sw	a1, -4(a0)
-
-.Lchkl:
-	andi	t0, a2, 0x4		# Check if there is at least a full
-	beq	t0, zero, .Llast8	#  word remaining after the loop
-	subu	a2, t0
-	sw	a1, 0(a0)		# Yes...
-	addiu	a0, 4
-
-.Llast8:
-	blez	a2, .Lexit		# Handle last 8 bytes (if cnt>0)
-	addu	a3, a2, a0		# a3 is last address +1
-.Llst8l:
-	addiu	a0, 1
-	bne	a0, a3, .Llst8l
-	sb	a1, -1(a0)
-.Lexit:
-	j	ra			# Bye, bye
-	nop
-
-	.set	reorder
-END (memset_omips)
-
-
diff --git a/libcutils/tests/memset_mips/test_memset.c b/libcutils/tests/memset_mips/test_memset.c
deleted file mode 100644
index 9705c65..0000000
--- a/libcutils/tests/memset_mips/test_memset.c
+++ /dev/null
@@ -1,235 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <cutils/memory.h>
-#include <time.h>
-
-/*
- * All systems must implement or emulate the rdhwr instruction to read
- * the userlocal register. Systems that emulate also return teh count register
- * when accessing register $2 so this should work on most systems
- */
-#define USE_RDHWR
-
-#ifdef USE_RDHWR
-#define UNITS "cycles"
-#define SCALE 2			/* Most CPU's */
-static inline uint32_t
-get_count(void)
-{
-  uint32_t res;
-  asm volatile (".set push; .set mips32r2; rdhwr %[res],$2; .set pop" : [res] "=r" (res) : : "memory");
-  return res;
-}
-#else
-#define UNITS "ns"
-#define SCALE 1
-static inline uint32_t
-get_count(void)
-{
-  struct timespec now;
-  uint32_t res;
-  clock_gettime(CLOCK_REALTIME, &now);
-  res = (uint32_t)(now.tv_sec * 1000000000LL + now.tv_nsec);
-  // printf ("now=%d.%09d res=%d\n", (int)now.tv_sec, (int)now.tv_nsec, res);
-  return res;
-}
-#endif
-
-uint32_t overhead;
-void
-measure_overhead(void)
-{
-  int i;
-  uint32_t start, stop, delta;
-  for (i = 0; i < 32; i++) {
-    start = get_count();
-    stop = get_count();
-    delta = stop - start;
-    if (overhead == 0 || delta < overhead)
-      overhead = delta;
-  }
-  printf("overhead is %d"UNITS"\n", overhead);
-}
-
-uint32_t
-timeone(void (*fn)(), void *d, uint32_t val, uint32_t bytes)
-{
-  uint32_t start, stop, delta;
-  start = get_count();
-  (*fn)(d, val, bytes);
-  stop = get_count();
-  delta = stop - start - overhead;
-  // printf ("start=0x%08x stop=0x%08x delta=0x%08x\n", start, stop, delta);
-  return delta * SCALE;
-}
-
-/* define VERIFY to check that memset only touches the bytes it's supposed to */
-/*#define VERIFY*/
-
-/*
- * Using a big arena means that memset will most likely miss in the cache
- * NB Enabling verification effectively warms up the cache...
- */
-#define ARENASIZE 0x1000000
-#ifdef VERIFY
-char arena[ARENASIZE+8];	/* Allow space for guard words */
-#else
-char arena[ARENASIZE];
-#endif
-
-void
-testone(char *tag, void (*fn)(), int trials, int minbytes, int maxbytes, int size, int threshold)
-{
-  int offset;
-  void *d;
-  void *p;
-  uint32_t v, notv = 0;
-  uint32_t n;
-  int i, units;
-  int totalunits = 0, totalbytes = 0, samples = 0;
-
-  /* Reset RNG to ensure each test uses same random values */
-  srand(0);			/* FIXME should be able to use some other seed than 0 */
-
-  for (i = 0; i < trials; i++) {
-    n = minbytes + (rand() % (maxbytes-minbytes));	/* How many bytes to do */
-    offset = ((rand() % (ARENASIZE-n)));		/* Where to start */
-
-#ifdef VERIFY
-    offset += 4;		/* Allow space for guard word at beginning */
-#endif
-    v = rand();
-
-    /* Adjust alignment and sizes based on transfer size */
-    switch (size) {
-    case 1:
-      v &= 0xff;
-      notv = ~v & 0xff;
-      break;
-    case 2:
-      v &= 0xffff;
-      notv = ~v & 0xffff;
-      offset &= ~1;
-      n &= ~1;
-      break;
-    case 4:
-      notv = ~v;
-      offset &= ~3;
-      n &= ~3;
-      break;
-    }
-
-    d = &arena[offset];
-
-#ifdef VERIFY
-    /* Initialise the area and guard words */
-    for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) {
-      if (size == 1)
-	*(uint8_t *)p = notv;
-      else if (size == 2)
-	*(uint16_t *)p = notv;
-      else if (size == 4)
-	*(uint32_t *)p = notv;
-    }
-#endif
-    units = timeone(fn, d, v, n);
-#ifdef VERIFY
-    /* Check the area and guard words */
-    for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) {
-      uint32_t got = 0;
-      if (size == 1)
-	got = *(uint8_t *)p;
-      else if (size == 2)
-	got = *(uint16_t *)p;
-      else if (size == 4)
-	got = *(uint32_t *)p;
-      if (p < (void *)&arena[offset]) {
-	if (got != notv)
-	  printf ("%s: verify failure: preguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, got, n);
-      }
-      else if (p < (void *)&arena[offset+n]) {
-	if (got != v)
-	  printf ("%s: verify failure: arena:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n);
-      }
-      else {
-	if (got != notv)
-	  printf ("%s: verify failure: postguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n);
-      }
-    }
-#endif
-
-    /* If the cycle count looks reasonable include it in the statistics */
-    if (units < threshold) {
-      totalbytes += n;
-      totalunits += units;
-      samples++;
-    }
-  }
-
-  printf("%s: samples=%d avglen=%d avg" UNITS "=%d bp"UNITS"=%g\n",
-	 tag, samples, totalbytes/samples, totalunits/samples, (double)totalbytes/(double)totalunits);
-}
-
-extern void android_memset32_dumb(uint32_t* dst, uint32_t value, size_t size);
-extern void android_memset16_dumb(uint32_t* dst, uint16_t value, size_t size);
-extern void android_memset32_test(uint32_t* dst, uint32_t value, size_t size);
-extern void android_memset16_test(uint32_t* dst, uint16_t value, size_t size);
-extern void memset_cmips(void* dst, int value, size_t size);
-extern void memset_omips(void* dst, int value, size_t size);
-
-int
-main(int argc, char **argv)
-{
-  int i;
-  struct {
-    char *type;
-    int trials;
-    int minbytes, maxbytes;
-  } *pp, params[] = {
-    {"small",  10000,   0,   64},
-    {"medium", 10000,  64,  512},
-    {"large",  10000, 512, 1280},
-    {"varied", 10000,   0, 1280},
-  };
-#define NPARAMS (sizeof(params)/sizeof(params[0]))
-  struct {
-    char *name;
-    void (*fn)();
-    int size;
-  } *fp, functions[] = {
-    {"dmemset16", (void (*)())android_memset16_dumb, 2},
-    {"tmemset16", (void (*)())android_memset16_test, 2},
-    {"lmemset16", (void (*)())android_memset16,      2},
-
-    {"dmemset32", (void (*)())android_memset32_dumb, 4},
-    {"tmemset32", (void (*)())android_memset32_test, 4},
-    {"lmemset32", (void (*)())android_memset32,      4},
-
-    {"cmemset",    (void (*)())memset_cmips,         1},
-    {"omemset",    (void (*)())memset_omips,         1},
-    {"lmemset",    (void (*)())memset,               1},
-  };
-#define NFUNCTIONS (sizeof(functions)/sizeof(functions[0]))
-  char tag[40];
-  int threshold;
-
-  measure_overhead();
-
-  /* Warm up the page cache */
-  memset(arena, 0xff, ARENASIZE); /* use 0xff now to avoid COW later */
-
-  for (fp = functions; fp < &functions[NFUNCTIONS]; fp++) {
-    (fp->fn)(arena, 0xffffffff, ARENASIZE);	/* one call to get the code into Icache */
-    for (pp = params; pp < &params[NPARAMS]; pp++) {
-      sprintf(tag, "%10s: %7s %4d-%4d", fp->name, pp->type, pp->minbytes, pp->maxbytes);
-
-      /* Set the cycle threshold */
-      threshold = pp->maxbytes * 4 * 10;	/* reasonable for cycles and ns */
-      testone(tag, fp->fn, pp->trials, pp->minbytes, pp->maxbytes, fp->size, threshold);
-    }
-    printf ("\n");
-  }
-
-  return 0;
-}
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 0d6c970..4fe20db 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -16,7 +16,15 @@
 LOCAL_PATH := $(my-dir)
 include $(CLEAR_VARS)
 
+ifneq ($(TARGET_USES_LOGD),false)
 liblog_sources := logd_write.c
+else
+liblog_sources := logd_write_kern.c
+endif
+
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+liblog_cflags := -DUSERDEBUG_BUILD=1
+endif
 
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
@@ -42,7 +50,11 @@
 endif
 
 liblog_host_sources := $(liblog_sources) fake_log_device.c
+ifneq ($(TARGET_USES_LOGD),false)
 liblog_target_sources = $(liblog_sources) log_read.c
+else
+liblog_target_sources = $(liblog_sources) log_read_kern.c
+endif
 
 # Shared and static library for host
 # ========================================================
@@ -72,11 +84,13 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_target_sources)
+LOCAL_CFLAGS := $(liblog_cflags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS := $(liblog_cflags)
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 47aa711..e4acac2 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -14,37 +14,176 @@
 ** limitations under the License.
 */
 
-#define _GNU_SOURCE /* asprintf for x86 host */
 #include <errno.h>
 #include <fcntl.h>
-#include <poll.h>
-#include <string.h>
-#include <stdio.h>
+#include <signal.h>
+#include <stddef.h>
+#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
 #include <cutils/list.h>
+#include <cutils/sockets.h>
 #include <log/log.h>
 #include <log/logger.h>
 
-#include <sys/ioctl.h>
+/* branchless on many architectures. */
+#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
-#define __LOGGERIO     0xAE
+#define WEAK __attribute__((weak))
+#define UNUSED __attribute__((unused))
 
-#define LOGGER_GET_LOG_BUF_SIZE    _IO(__LOGGERIO, 1) /* size of log */
-#define LOGGER_GET_LOG_LEN         _IO(__LOGGERIO, 2) /* used log len */
-#define LOGGER_GET_NEXT_ENTRY_LEN  _IO(__LOGGERIO, 3) /* next entry len */
-#define LOGGER_FLUSH_LOG           _IO(__LOGGERIO, 4) /* flush log */
-#define LOGGER_GET_VERSION         _IO(__LOGGERIO, 5) /* abi version */
-#define LOGGER_SET_VERSION         _IO(__LOGGERIO, 6) /* abi version */
+/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
 
-typedef char bool;
-#define false (const bool)0
-#define true (const bool)1
+#ifdef HAVE_WINSOCK
 
-#define LOG_FILE_DIR "/dev/log/"
+int WEAK socket_local_client(const char *name, int namespaceId, int type)
+{
+    errno = ENOSYS;
+    return -ENOSYS;
+}
 
-/* timeout in milliseconds */
-#define LOG_TIMEOUT_FLUSH 5
-#define LOG_TIMEOUT_NEVER -1
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+/* Private copy of ../libcutils/socket_local.h prevent library loops */
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+/* End of ../libcutils/socket_local.h */
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int WEAK socket_make_sockaddr_un(const char *name, int namespaceId,
+                                 struct sockaddr_un *p_addr, socklen_t *alen)
+{
+    memset (p_addr, 0, sizeof (*p_addr));
+    size_t namelen;
+
+    switch (namespaceId) {
+    case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+        namelen  = strlen(name);
+
+        /* Test with length +1 for the *initial* '\0'. */
+        if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+            goto error;
+        }
+
+        /*
+         * Note: The path in this case is *not* supposed to be
+         * '\0'-terminated. ("man 7 unix" for the gory details.)
+         */
+
+        p_addr->sun_path[0] = 0;
+        memcpy(p_addr->sun_path + 1, name, namelen);
+#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+        /* this OS doesn't have the Linux abstract namespace */
+
+        namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+        /* unix_path_max appears to be missing on linux */
+        if (namelen > sizeof(*p_addr)
+                - offsetof(struct sockaddr_un, sun_path) - 1) {
+            goto error;
+        }
+
+        strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+        strcat(p_addr->sun_path, name);
+#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+        break;
+
+    case ANDROID_SOCKET_NAMESPACE_RESERVED:
+        namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+        /* unix_path_max appears to be missing on linux */
+        if (namelen > sizeof(*p_addr)
+                - offsetof(struct sockaddr_un, sun_path) - 1) {
+            goto error;
+        }
+
+        strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+        strcat(p_addr->sun_path, name);
+        break;
+
+    case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+        namelen = strlen(name);
+        /* unix_path_max appears to be missing on linux */
+        if (namelen > sizeof(*p_addr)
+                - offsetof(struct sockaddr_un, sun_path) - 1) {
+            goto error;
+        }
+
+        strcpy(p_addr->sun_path, name);
+        break;
+
+    default:
+        /* invalid namespace id */
+        return -1;
+    }
+
+    p_addr->sun_family = AF_LOCAL;
+    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+    return 0;
+error:
+    return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId,
+                                     int type UNUSED)
+{
+    struct sockaddr_un addr;
+    socklen_t alen;
+    size_t namelen;
+    int err;
+
+    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+    if (err < 0) {
+        goto error;
+    }
+
+    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+        goto error;
+    }
+
+    return fd;
+
+error:
+    return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int WEAK socket_local_client(const char *name, int namespaceId, int type)
+{
+    int s;
+
+    s = socket(AF_LOCAL, type, 0);
+    if(s < 0) return -1;
+
+    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+#endif /* !HAVE_WINSOCK */
+/* End of ../libcutils/socket_local_client.c */
 
 #define logger_for_each(logger, logger_list) \
     for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
@@ -59,44 +198,17 @@
     [LOG_ID_SYSTEM] = "system"
 };
 
-const char *android_log_id_to_name(log_id_t log_id) {
+const char *android_log_id_to_name(log_id_t log_id)
+{
     if (log_id >= LOG_ID_MAX) {
         log_id = LOG_ID_MAIN;
     }
     return LOG_NAME[log_id];
 }
 
-static int accessmode(int mode)
+log_id_t android_name_to_log_id(const char *logName)
 {
-    if ((mode & O_ACCMODE) == O_WRONLY) {
-        return W_OK;
-    }
-    if ((mode & O_ACCMODE) == O_RDWR) {
-        return R_OK | W_OK;
-    }
-    return R_OK;
-}
-
-/* repeated fragment */
-static int check_allocate_accessible(char **n, const char *b, int mode)
-{
-    *n = NULL;
-
-    if (!b) {
-        return -EINVAL;
-    }
-
-    asprintf(n, LOG_FILE_DIR "%s", b);
-    if (!*n) {
-        return -1;
-    }
-
-    return access(*n, accessmode(mode));
-}
-
-log_id_t android_name_to_log_id(const char *logName) {
     const char *b;
-    char *n;
     int ret;
 
     if (!logName) {
@@ -109,12 +221,6 @@
         ++b;
     }
 
-    ret = check_allocate_accessible(&n, b, O_RDONLY);
-    free(n);
-    if (ret) {
-        return ret;
-    }
-
     for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
         const char *l = LOG_NAME[ret];
         if (l && !strcmp(b, l)) {
@@ -129,26 +235,13 @@
     int mode;
     unsigned int tail;
     pid_t pid;
-    unsigned int queued_lines;
-    int timeout_ms;
-    int error;
-    bool flush;
-    bool valid_entry; /* valiant(?) effort to deal with memory starvation */
-    struct log_msg entry;
-};
-
-struct log_list {
-    struct listnode node;
-    struct log_msg entry; /* Truncated to event->len() + 1 to save space */
+    int sock;
 };
 
 struct logger {
     struct listnode node;
     struct logger_list *top;
-    int fd;
     log_id_t id;
-    short *revents;
-    struct listnode log_list;
 };
 
 /* android_logger_alloc unimplemented, no use case */
@@ -159,93 +252,182 @@
         return;
     }
 
-    while (!list_empty(&logger->log_list)) {
-        struct log_list *entry = node_to_item(
-            list_head(&logger->log_list), struct log_list, node);
-        list_remove(&entry->node);
-        free(entry);
-        if (logger->top->queued_lines) {
-            logger->top->queued_lines--;
-        }
-    }
-
-    if (logger->fd >= 0) {
-        close(logger->fd);
-    }
-
     list_remove(&logger->node);
 
     free(logger);
 }
 
+/* android_logger_alloc unimplemented, no use case */
+
+/* method for getting the associated sublog id */
 log_id_t android_logger_get_id(struct logger *logger)
 {
     return logger->id;
 }
 
 /* worker for sending the command to the logger */
-static int logger_ioctl(struct logger *logger, int cmd, int mode)
+static ssize_t send_log_msg(struct logger *logger,
+                            const char *msg, char *buf, size_t buf_size)
 {
-    char *n;
-    int  f, ret;
-
-    if (!logger || !logger->top) {
-        return -EFAULT;
+    ssize_t ret;
+    int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    if (sock < 0) {
+        ret = sock;
+        goto done;
     }
 
-    if (((mode & O_ACCMODE) == O_RDWR)
-     || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) {
-        return ioctl(logger->fd, cmd);
+    if (msg) {
+        snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1);
     }
 
-    /* We go here if android_logger_list_open got mode wrong for this ioctl */
-    ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode);
-    if (ret) {
-        free(n);
+    ret = write(sock, buf, strlen(buf) + 1);
+    if (ret <= 0) {
+        goto done;
+    }
+
+    ret = read(sock, buf, buf_size);
+
+done:
+    if ((ret == -1) && errno) {
+        ret = -errno;
+    }
+    close(sock);
+    return ret;
+}
+
+static int check_log_success(char *buf, ssize_t ret)
+{
+    if (ret < 0) {
         return ret;
     }
 
-    f = open(n, mode);
-    free(n);
-    if (f < 0) {
-        return f;
+    if (strncmp(buf, "success", 7)) {
+        return -1;
     }
 
-    ret = ioctl(f, cmd);
-    close (f);
-
-    return ret;
+    return 0;
 }
 
 int android_logger_clear(struct logger *logger)
 {
-    return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY);
+    char buf[512];
+
+    return check_log_success(buf,
+        send_log_msg(logger, "clear %d", buf, sizeof(buf)));
 }
 
 /* returns the total size of the log's ring buffer */
-int android_logger_get_log_size(struct logger *logger)
+long android_logger_get_log_size(struct logger *logger)
 {
-    return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR);
+    char buf[512];
+
+    ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+    if (ret < 0) {
+        return ret;
+    }
+
+    if ((buf[0] < '0') || ('9' < buf[0])) {
+        return -1;
+    }
+
+    return atol(buf);
 }
 
+#ifdef USERDEBUG_BUILD
+
+int android_logger_set_log_size(struct logger *logger, unsigned long size)
+{
+    char buf[512];
+
+    snprintf(buf, sizeof(buf), "setLogSize %d %lu",
+        logger ? logger->id : (unsigned) -1, size);
+
+    return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+}
+
+#endif /* USERDEBUG_BUILD */
+
 /*
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
  */
-int android_logger_get_log_readable_size(struct logger *logger)
+long android_logger_get_log_readable_size(struct logger *logger)
 {
-    return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY);
+    char buf[512];
+
+    ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+    if (ret < 0) {
+        return ret;
+    }
+
+    if ((buf[0] < '0') || ('9' < buf[0])) {
+        return -1;
+    }
+
+    return atol(buf);
 }
 
 /*
  * returns the logger version
  */
-int android_logger_get_log_version(struct logger *logger)
+int android_logger_get_log_version(struct logger *logger UNUSED)
 {
-    int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR);
-    return (ret < 0) ? 1 : ret;
+    return 3;
 }
 
+/*
+ * returns statistics
+ */
+ssize_t android_logger_get_statistics(struct logger_list *logger_list,
+                                      char *buf, size_t len)
+{
+    struct listnode *node;
+    struct logger *logger;
+    char *cp = buf;
+    size_t remaining = len;
+    size_t n;
+
+    n = snprintf(cp, remaining, "getStatistics");
+    n = min(n, remaining);
+    remaining -= n;
+    cp += n;
+
+    logger_for_each(logger, logger_list) {
+        n = snprintf(cp, remaining, " %d", logger->id);
+        n = min(n, remaining);
+        remaining -= n;
+        cp += n;
+    }
+    return send_log_msg(NULL, NULL, buf, len);
+}
+
+#ifdef USERDEBUG_BUILD
+
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED,
+                                      char *buf, size_t len)
+{
+    return send_log_msg(NULL, "getPruneList", buf, len);
+}
+
+int android_logger_set_prune_list(struct logger_list *logger_list UNUSED,
+                                  char *buf, size_t len)
+{
+    const char cmd[] = "setPruneList ";
+    const size_t cmdlen = sizeof(cmd) - 1;
+
+    if (strlen(buf) > (len - cmdlen)) {
+        return -ENOMEM; /* KISS */
+    }
+    memmove(buf + cmdlen, buf, len - cmdlen);
+    buf[len - 1] = '\0';
+    memcpy(buf, cmd, cmdlen);
+
+    return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+}
+
+#endif /* USERDEBUG_BUILD */
+
 struct logger_list *android_logger_list_alloc(int mode,
                                               unsigned int tail,
                                               pid_t pid)
@@ -256,10 +438,13 @@
     if (!logger_list) {
         return NULL;
     }
+
     list_init(&logger_list->node);
     logger_list->mode = mode;
     logger_list->tail = tail;
     logger_list->pid = pid;
+    logger_list->sock = -1;
+
     return logger_list;
 }
 
@@ -289,28 +474,11 @@
         goto err;
     }
 
-    if (check_allocate_accessible(&n, android_log_id_to_name(id),
-                                  logger_list->mode)) {
-        goto err_name;
-    }
-
-    logger->fd = open(n, logger_list->mode);
-    if (logger->fd < 0) {
-        goto err_name;
-    }
-
-    free(n);
     logger->id = id;
-    list_init(&logger->log_list);
     list_add_tail(&logger_list->node, &logger->node);
     logger->top = logger_list;
-    logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
     goto ok;
 
-err_name:
-    free(n);
-err_logger:
-    free(logger);
 err:
     logger = NULL;
 ok:
@@ -336,315 +504,137 @@
     return logger_list;
 }
 
-/* prevent memory starvation when backfilling */
-static unsigned int queue_threshold(struct logger_list *logger_list)
+static void caught_signal(int signum UNUSED)
 {
-    return (logger_list->tail < 64) ? 64 : logger_list->tail;
-}
-
-static bool low_queue(struct listnode *node)
-{
-    /* low is considered less than 2 */
-    return list_head(node) == list_tail(node);
-}
-
-/* Flush queues in sequential order, one at a time */
-static int android_logger_list_flush(struct logger_list *logger_list,
-                                     struct log_msg *log_msg)
-{
-    int ret = 0;
-    struct log_list *firstentry = NULL;
-
-    while ((ret == 0)
-     && (logger_list->flush
-      || (logger_list->queued_lines > logger_list->tail))) {
-        struct logger *logger;
-
-        /* Merge sort */
-        bool at_least_one_is_low = false;
-        struct logger *firstlogger = NULL;
-        firstentry = NULL;
-
-        logger_for_each(logger, logger_list) {
-            struct listnode *node;
-            struct log_list *oldest = NULL;
-
-            /* kernel logger channels not necessarily time-sort order */
-            list_for_each(node, &logger->log_list) {
-                struct log_list *entry = node_to_item(node,
-                                                      struct log_list, node);
-                if (!oldest
-                 || (entry->entry.entry.sec < oldest->entry.entry.sec)
-                 || ((entry->entry.entry.sec == oldest->entry.entry.sec)
-                  && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) {
-                     oldest = entry;
-                }
-            }
-
-            if (!oldest) {
-                at_least_one_is_low = true;
-                continue;
-            } else if (low_queue(&logger->log_list)) {
-                at_least_one_is_low = true;
-            }
-
-            if (!firstentry
-             || (oldest->entry.entry.sec < firstentry->entry.entry.sec)
-             || ((oldest->entry.entry.sec == firstentry->entry.entry.sec)
-              && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) {
-                firstentry = oldest;
-                firstlogger = logger;
-            }
-        }
-
-        if (!firstentry) {
-            break;
-        }
-
-        /* when trimming list, tries to keep one entry behind in each bucket */
-        if (!logger_list->flush
-         && at_least_one_is_low
-         && (logger_list->queued_lines < queue_threshold(logger_list))) {
-            break;
-        }
-
-        /* within tail?, send! */
-        if ((logger_list->tail == 0)
-         || (logger_list->queued_lines <= logger_list->tail)) {
-            ret = firstentry->entry.entry.hdr_size;
-            if (!ret) {
-                ret = sizeof(firstentry->entry.entry_v1);
-            }
-            ret += firstentry->entry.entry.len;
-
-            memcpy(log_msg->buf, firstentry->entry.buf, ret + 1);
-            log_msg->extra.id = firstlogger->id;
-        }
-
-        /* next entry */
-        list_remove(&firstentry->node);
-        free(firstentry);
-        if (logger_list->queued_lines) {
-            logger_list->queued_lines--;
-        }
-    }
-
-    /* Flushed the list, no longer in tail mode for continuing content */
-    if (logger_list->flush && !firstentry) {
-        logger_list->tail = 0;
-    }
-    return ret;
 }
 
 /* Read from the selected logs */
 int android_logger_list_read(struct logger_list *logger_list,
                              struct log_msg *log_msg)
 {
+    int ret, e;
     struct logger *logger;
-    nfds_t nfds;
-    struct pollfd *p, *pollfds = NULL;
-    int error = 0, ret = 0;
-
-    memset(log_msg, 0, sizeof(struct log_msg));
+    struct sigaction ignore;
+    struct sigaction old_sigaction;
+    unsigned int old_alarm = 0;
 
     if (!logger_list) {
-        return -ENODEV;
+        return -EINVAL;
     }
 
-    if (!(accessmode(logger_list->mode) & R_OK)) {
-        logger_list->error = EPERM;
-        goto done;
+    if (logger_list->mode & O_NONBLOCK) {
+        memset(&ignore, 0, sizeof(ignore));
+        ignore.sa_handler = caught_signal;
+        sigemptyset(&ignore.sa_mask);
     }
 
-    nfds = 0;
-    logger_for_each(logger, logger_list) {
-        ++nfds;
-    }
-    if (nfds <= 0) {
-        error = ENODEV;
-        goto done;
-    }
+    if (logger_list->sock < 0) {
+        char buffer[256], *cp, c;
 
-    /* Do we have anything to offer from the buffer or state? */
-    if (logger_list->valid_entry) { /* implies we are also in a flush state */
-        goto flush;
-    }
-
-    ret = android_logger_list_flush(logger_list, log_msg);
-    if (ret) {
-        goto done;
-    }
-
-    if (logger_list->error) { /* implies we are also in a flush state */
-        goto done;
-    }
-
-    /* Lets start grinding on metal */
-    pollfds = calloc(nfds, sizeof(struct pollfd));
-    if (!pollfds) {
-        error = ENOMEM;
-        goto flush;
-    }
-
-    p = pollfds;
-    logger_for_each(logger, logger_list) {
-        p->fd = logger->fd;
-        p->events = POLLIN;
-        logger->revents = &p->revents;
-        ++p;
-    }
-
-    while (!ret && !error) {
-        int result;
-
-        /* If we oversleep it's ok, i.e. ignore EINTR. */
-        result = TEMP_FAILURE_RETRY(
-            poll(pollfds, nfds, logger_list->timeout_ms));
-
-        if (result <= 0) {
-            if (result) {
-                error = errno;
-            } else if (logger_list->mode & O_NDELAY) {
-                error = EAGAIN;
-            } else {
-                logger_list->timeout_ms = LOG_TIMEOUT_NEVER;
+        int sock = socket_local_client("logdr",
+                                       ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                       SOCK_SEQPACKET);
+        if (sock < 0) {
+            if ((sock == -1) && errno) {
+                return -errno;
             }
-
-            logger_list->flush = true;
-            goto try_flush;
+            return sock;
         }
 
-        logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
+        strcpy(buffer,
+               (logger_list->mode & O_NONBLOCK) ? "dumpAndClose" : "stream");
+        cp = buffer + strlen(buffer);
 
-        /* Anti starvation */
-        if (!logger_list->flush
-         && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) {
-            /* Any queues with input pending that is low? */
-            bool starving = false;
-            logger_for_each(logger, logger_list) {
-                if ((*(logger->revents) & POLLIN)
-                 && low_queue(&logger->log_list)) {
-                    starving = true;
-                    break;
-                }
-            }
-
-            /* pushback on any queues that are not low */
-            if (starving) {
-                logger_for_each(logger, logger_list) {
-                    if ((*(logger->revents) & POLLIN)
-                     && !low_queue(&logger->log_list)) {
-                        *(logger->revents) &= ~POLLIN;
-                    }
-                }
-            }
-        }
-
+        strcpy(cp, " lids");
+        cp += 5;
+        c = '=';
+        int remaining = sizeof(buffer) - (cp - buffer);
         logger_for_each(logger, logger_list) {
-            unsigned int hdr_size;
-            struct log_list *entry;
+            ret = snprintf(cp, remaining, "%c%u", c, logger->id);
+            ret = min(ret, remaining);
+            remaining -= ret;
+            cp += ret;
+            c = ',';
+        }
 
-            if (!(*(logger->revents) & POLLIN)) {
-                continue;
+        if (logger_list->tail) {
+            ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+            ret = min(ret, remaining);
+            remaining -= ret;
+            cp += ret;
+        }
+
+        if (logger_list->pid) {
+            ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+            ret = min(ret, remaining);
+            remaining -= ret;
+            cp += ret;
+        }
+
+        if (logger_list->mode & O_NONBLOCK) {
+            /* Deal with an unresponsive logd */
+            sigaction(SIGALRM, &ignore, &old_sigaction);
+            old_alarm = alarm(30);
+        }
+        ret = write(sock, buffer, cp - buffer);
+        e = errno;
+        if (logger_list->mode & O_NONBLOCK) {
+            if (e == EINTR) {
+                e = ETIMEDOUT;
             }
-
-            memset(logger_list->entry.buf, 0, sizeof(struct log_msg));
-            /* NOTE: driver guarantees we read exactly one full entry */
-            result = read(logger->fd, logger_list->entry.buf,
-                          LOGGER_ENTRY_MAX_LEN);
-            if (result <= 0) {
-                if (!result) {
-                    error = EIO;
-                } else if (errno != EINTR) {
-                    error = errno;
-                }
-                continue;
-            }
-
-            if (logger_list->pid
-             && (logger_list->pid != logger_list->entry.entry.pid)) {
-                continue;
-            }
-
-            hdr_size = logger_list->entry.entry.hdr_size;
-            if (!hdr_size) {
-                hdr_size = sizeof(logger_list->entry.entry_v1);
-            }
-
-            if ((hdr_size > sizeof(struct log_msg))
-             || (logger_list->entry.entry.len
-               > sizeof(logger_list->entry.buf) - hdr_size)
-             || (logger_list->entry.entry.len != result - hdr_size)) {
-                error = EINVAL;
-                continue;
-            }
-
-            logger_list->entry.extra.id = logger->id;
-
-            /* speedup: If not tail, and only one list, send directly */
-            if (!logger_list->tail
-             && (list_head(&logger_list->node)
-              == list_tail(&logger_list->node))) {
-                ret = result;
-                memcpy(log_msg->buf, logger_list->entry.buf, result + 1);
-                log_msg->extra.id = logger->id;
-                break;
-            }
-
-            entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1);
-
-            if (!entry) {
-                logger_list->valid_entry = true;
-                error = ENOMEM;
-                break;
-            }
-
-            logger_list->queued_lines++;
-
-            memcpy(entry->entry.buf, logger_list->entry.buf, result);
-            entry->entry.buf[result] = '\0';
-            list_add_tail(&logger->log_list, &entry->node);
+            alarm(old_alarm);
+            sigaction(SIGALRM, &old_sigaction, NULL);
         }
 
         if (ret <= 0) {
-try_flush:
-            ret = android_logger_list_flush(logger_list, log_msg);
-        }
-    }
-
-    free(pollfds);
-
-flush:
-    if (error) {
-        logger_list->flush = true;
-    }
-
-    if (ret <= 0) {
-        ret = android_logger_list_flush(logger_list, log_msg);
-
-        if (!ret && logger_list->valid_entry) {
-            ret = logger_list->entry.entry.hdr_size;
-            if (!ret) {
-                ret = sizeof(logger_list->entry.entry_v1);
+            close(sock);
+            if ((ret == -1) && e) {
+                return -e;
             }
-            ret += logger_list->entry.entry.len;
-
-            memcpy(log_msg->buf, logger_list->entry.buf,
-                   sizeof(struct log_msg));
-            logger_list->valid_entry = false;
+            if (ret == 0) {
+                return -EIO;
+            }
+            return ret;
         }
+
+        logger_list->sock = sock;
     }
 
-done:
-    if (logger_list->error) {
-        error = logger_list->error;
-    }
-    if (error) {
-        logger_list->error = error;
-        if (!ret) {
-            ret = -error;
+    ret = 0;
+    while(1) {
+        memset(log_msg, 0, sizeof(*log_msg));
+
+        if (logger_list->mode & O_NONBLOCK) {
+            /* particularily useful if tombstone is reporting for logd */
+            sigaction(SIGALRM, &ignore, &old_sigaction);
+            old_alarm = alarm(30);
+        }
+        /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
+        ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
+        e = errno;
+        if (logger_list->mode & O_NONBLOCK) {
+            if ((ret == 0) || (e == EINTR)) {
+                e = EAGAIN;
+                ret = -1;
+            }
+            alarm(old_alarm);
+            sigaction(SIGALRM, &old_sigaction, NULL);
+        }
+
+        if (ret <= 0) {
+            if ((ret == -1) && e) {
+                return -e;
+            }
+            return ret;
+        }
+
+        logger_for_each(logger, logger_list) {
+            if (log_msg->entry.lid == logger->id) {
+                return ret;
+            }
         }
     }
+    /* NOTREACH */
     return ret;
 }
 
@@ -661,5 +651,9 @@
         android_logger_free(logger);
     }
 
+    if (logger_list->sock >= 0) {
+        close (logger_list->sock);
+    }
+
     free(logger_list);
 }
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
new file mode 100644
index 0000000..483b6b6
--- /dev/null
+++ b/liblog/log_read_kern.c
@@ -0,0 +1,738 @@
+/*
+** Copyright 2013-2014, 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.
+*/
+
+#define _GNU_SOURCE /* asprintf for x86 host */
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/list.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+#include <sys/ioctl.h>
+
+#define __LOGGERIO     0xAE
+
+#define LOGGER_GET_LOG_BUF_SIZE    _IO(__LOGGERIO, 1) /* size of log */
+#define LOGGER_GET_LOG_LEN         _IO(__LOGGERIO, 2) /* used log len */
+#define LOGGER_GET_NEXT_ENTRY_LEN  _IO(__LOGGERIO, 3) /* next entry len */
+#define LOGGER_FLUSH_LOG           _IO(__LOGGERIO, 4) /* flush log */
+#define LOGGER_GET_VERSION         _IO(__LOGGERIO, 5) /* abi version */
+#define LOGGER_SET_VERSION         _IO(__LOGGERIO, 6) /* abi version */
+
+typedef char bool;
+#define false (const bool)0
+#define true (const bool)1
+
+#define LOG_FILE_DIR "/dev/log/"
+
+/* timeout in milliseconds */
+#define LOG_TIMEOUT_FLUSH 5
+#define LOG_TIMEOUT_NEVER -1
+
+#define logger_for_each(logger, logger_list) \
+    for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
+         logger != node_to_item(&(logger_list)->node, struct logger, node); \
+         logger = node_to_item((logger)->node.next, struct logger, node))
+
+#define UNUSED __attribute__((unused))
+
+/* In the future, we would like to make this list extensible */
+static const char *LOG_NAME[LOG_ID_MAX] = {
+    [LOG_ID_MAIN] = "main",
+    [LOG_ID_RADIO] = "radio",
+    [LOG_ID_EVENTS] = "events",
+    [LOG_ID_SYSTEM] = "system"
+};
+
+const char *android_log_id_to_name(log_id_t log_id)
+{
+    if (log_id >= LOG_ID_MAX) {
+        log_id = LOG_ID_MAIN;
+    }
+    return LOG_NAME[log_id];
+}
+
+static int accessmode(int mode)
+{
+    if ((mode & O_ACCMODE) == O_WRONLY) {
+        return W_OK;
+    }
+    if ((mode & O_ACCMODE) == O_RDWR) {
+        return R_OK | W_OK;
+    }
+    return R_OK;
+}
+
+/* repeated fragment */
+static int check_allocate_accessible(char **n, const char *b, int mode)
+{
+    *n = NULL;
+
+    if (!b) {
+        return -EINVAL;
+    }
+
+    asprintf(n, LOG_FILE_DIR "%s", b);
+    if (!*n) {
+        return -1;
+    }
+
+    return access(*n, accessmode(mode));
+}
+
+log_id_t android_name_to_log_id(const char *logName)
+{
+    const char *b;
+    char *n;
+    int ret;
+
+    if (!logName) {
+        return -1; /* NB: log_id_t is unsigned */
+    }
+    b = strrchr(logName, '/');
+    if (!b) {
+        b = logName;
+    } else {
+        ++b;
+    }
+
+    ret = check_allocate_accessible(&n, b, O_RDONLY);
+    free(n);
+    if (ret) {
+        return ret;
+    }
+
+    for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+        const char *l = LOG_NAME[ret];
+        if (l && !strcmp(b, l)) {
+            return ret;
+        }
+    }
+    return -1;   /* should never happen */
+}
+
+struct logger_list {
+    struct listnode node;
+    int mode;
+    unsigned int tail;
+    pid_t pid;
+    unsigned int queued_lines;
+    int timeout_ms;
+    int error;
+    bool flush;
+    bool valid_entry; /* valiant(?) effort to deal with memory starvation */
+    struct log_msg entry;
+};
+
+struct log_list {
+    struct listnode node;
+    struct log_msg entry; /* Truncated to event->len() + 1 to save space */
+};
+
+struct logger {
+    struct listnode node;
+    struct logger_list *top;
+    int fd;
+    log_id_t id;
+    short *revents;
+    struct listnode log_list;
+};
+
+/* android_logger_alloc unimplemented, no use case */
+/* android_logger_free not exported */
+static void android_logger_free(struct logger *logger)
+{
+    if (!logger) {
+        return;
+    }
+
+    while (!list_empty(&logger->log_list)) {
+        struct log_list *entry = node_to_item(
+            list_head(&logger->log_list), struct log_list, node);
+        list_remove(&entry->node);
+        free(entry);
+        if (logger->top->queued_lines) {
+            logger->top->queued_lines--;
+        }
+    }
+
+    if (logger->fd >= 0) {
+        close(logger->fd);
+    }
+
+    list_remove(&logger->node);
+
+    free(logger);
+}
+
+log_id_t android_logger_get_id(struct logger *logger)
+{
+    return logger->id;
+}
+
+/* worker for sending the command to the logger */
+static int logger_ioctl(struct logger *logger, int cmd, int mode)
+{
+    char *n;
+    int  f, ret;
+
+    if (!logger || !logger->top) {
+        return -EFAULT;
+    }
+
+    if (((mode & O_ACCMODE) == O_RDWR)
+            || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) {
+        return ioctl(logger->fd, cmd);
+    }
+
+    /* We go here if android_logger_list_open got mode wrong for this ioctl */
+    ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode);
+    if (ret) {
+        free(n);
+        return ret;
+    }
+
+    f = open(n, mode);
+    free(n);
+    if (f < 0) {
+        return f;
+    }
+
+    ret = ioctl(f, cmd);
+    close (f);
+
+    return ret;
+}
+
+int android_logger_clear(struct logger *logger)
+{
+    return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY);
+}
+
+/* returns the total size of the log's ring buffer */
+long android_logger_get_log_size(struct logger *logger)
+{
+    return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR);
+}
+
+#ifdef USERDEBUG_BUILD
+
+int android_logger_set_log_size(struct logger *logger UNUSED,
+                                unsigned long size UNUSED)
+{
+    return -ENOTSUP;
+}
+
+#endif /* USERDEBUG_BUILD */
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+long android_logger_get_log_readable_size(struct logger *logger)
+{
+    return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY);
+}
+
+/*
+ * returns the logger version
+ */
+int android_logger_get_log_version(struct logger *logger)
+{
+    int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR);
+    return (ret < 0) ? 1 : ret;
+}
+
+/*
+ * returns statistics
+ */
+static const char unsupported[] = "18\nNot Supported\n\f";
+
+ssize_t android_logger_get_statistics(struct logger_list *logger_list UNUSED,
+                                      char *buf, size_t len)
+{
+    strncpy(buf, unsupported, len);
+    return -ENOTSUP;
+}
+
+#ifdef USERDEBUG_BUILD
+
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED,
+                                      char *buf, size_t len)
+{
+    strncpy(buf, unsupported, len);
+    return -ENOTSUP;
+}
+
+int android_logger_set_prune_list(struct logger_list *logger_list UNUSED,
+                                  char *buf, size_t len)
+{
+    static const char unsupported_error[] = "Unsupported";
+    strncpy(buf, unsupported, len);
+    return -ENOTSUP;
+}
+
+#endif /* USERDEBUG_BUILD */
+
+struct logger_list *android_logger_list_alloc(int mode,
+                                              unsigned int tail,
+                                              pid_t pid)
+{
+    struct logger_list *logger_list;
+
+    logger_list = calloc(1, sizeof(*logger_list));
+    if (!logger_list) {
+        return NULL;
+    }
+    list_init(&logger_list->node);
+    logger_list->mode = mode;
+    logger_list->tail = tail;
+    logger_list->pid = pid;
+    return logger_list;
+}
+
+/* android_logger_list_register unimplemented, no use case */
+/* android_logger_list_unregister unimplemented, no use case */
+
+/* Open the named log and add it to the logger list */
+struct logger *android_logger_open(struct logger_list *logger_list,
+                                   log_id_t id)
+{
+    struct listnode *node;
+    struct logger *logger;
+    char *n;
+
+    if (!logger_list || (id >= LOG_ID_MAX)) {
+        goto err;
+    }
+
+    logger_for_each(logger, logger_list) {
+        if (logger->id == id) {
+            goto ok;
+        }
+    }
+
+    logger = calloc(1, sizeof(*logger));
+    if (!logger) {
+        goto err;
+    }
+
+    if (check_allocate_accessible(&n, android_log_id_to_name(id),
+                                  logger_list->mode)) {
+        goto err_name;
+    }
+
+    logger->fd = open(n, logger_list->mode);
+    if (logger->fd < 0) {
+        goto err_name;
+    }
+
+    free(n);
+    logger->id = id;
+    list_init(&logger->log_list);
+    list_add_tail(&logger_list->node, &logger->node);
+    logger->top = logger_list;
+    logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
+    goto ok;
+
+err_name:
+    free(n);
+err_logger:
+    free(logger);
+err:
+    logger = NULL;
+ok:
+    return logger;
+}
+
+/* Open the single named log and make it part of a new logger list */
+struct logger_list *android_logger_list_open(log_id_t id,
+                                             int mode,
+                                             unsigned int tail,
+                                             pid_t pid)
+{
+    struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
+    if (!logger_list) {
+        return NULL;
+    }
+
+    if (!android_logger_open(logger_list, id)) {
+        android_logger_list_free(logger_list);
+        return NULL;
+    }
+
+    return logger_list;
+}
+
+/* prevent memory starvation when backfilling */
+static unsigned int queue_threshold(struct logger_list *logger_list)
+{
+    return (logger_list->tail < 64) ? 64 : logger_list->tail;
+}
+
+static bool low_queue(struct listnode *node)
+{
+    /* low is considered less than 2 */
+    return list_head(node) == list_tail(node);
+}
+
+/* Flush queues in sequential order, one at a time */
+static int android_logger_list_flush(struct logger_list *logger_list,
+                                     struct log_msg *log_msg)
+{
+    int ret = 0;
+    struct log_list *firstentry = NULL;
+
+    while ((ret == 0)
+            && (logger_list->flush
+                || (logger_list->queued_lines > logger_list->tail))) {
+        struct logger *logger;
+
+        /* Merge sort */
+        bool at_least_one_is_low = false;
+        struct logger *firstlogger = NULL;
+        firstentry = NULL;
+
+        logger_for_each(logger, logger_list) {
+            struct listnode *node;
+            struct log_list *oldest = NULL;
+
+            /* kernel logger channels not necessarily time-sort order */
+            list_for_each(node, &logger->log_list) {
+                struct log_list *entry = node_to_item(node,
+                                                      struct log_list, node);
+                if (!oldest
+                        || (entry->entry.entry.sec < oldest->entry.entry.sec)
+                        || ((entry->entry.entry.sec == oldest->entry.entry.sec)
+                            && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) {
+                    oldest = entry;
+                }
+            }
+
+            if (!oldest) {
+                at_least_one_is_low = true;
+                continue;
+            } else if (low_queue(&logger->log_list)) {
+                at_least_one_is_low = true;
+            }
+
+            if (!firstentry
+                    || (oldest->entry.entry.sec < firstentry->entry.entry.sec)
+                    || ((oldest->entry.entry.sec == firstentry->entry.entry.sec)
+                        && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) {
+                firstentry = oldest;
+                firstlogger = logger;
+            }
+        }
+
+        if (!firstentry) {
+            break;
+        }
+
+        /* when trimming list, tries to keep one entry behind in each bucket */
+        if (!logger_list->flush
+                && at_least_one_is_low
+                && (logger_list->queued_lines < queue_threshold(logger_list))) {
+            break;
+        }
+
+        /* within tail?, send! */
+        if ((logger_list->tail == 0)
+                || (logger_list->queued_lines <= logger_list->tail)) {
+            int diff;
+            ret = firstentry->entry.entry.hdr_size;
+            if (!ret) {
+                ret = sizeof(firstentry->entry.entry_v1);
+            }
+
+            /* Promote entry to v3 format */
+            memcpy(log_msg->buf, firstentry->entry.buf, ret);
+            diff = sizeof(firstentry->entry.entry_v3) - ret;
+            if (diff < 0) {
+                diff = 0;
+            } else if (diff > 0) {
+                memset(log_msg->buf + ret, 0, diff);
+            }
+            memcpy(log_msg->buf + ret + diff, firstentry->entry.buf + ret,
+                   firstentry->entry.entry.len + 1);
+            ret += diff;
+            log_msg->entry.hdr_size = ret;
+            log_msg->entry.lid = firstlogger->id;
+
+            ret += firstentry->entry.entry.len;
+        }
+
+        /* next entry */
+        list_remove(&firstentry->node);
+        free(firstentry);
+        if (logger_list->queued_lines) {
+            logger_list->queued_lines--;
+        }
+    }
+
+    /* Flushed the list, no longer in tail mode for continuing content */
+    if (logger_list->flush && !firstentry) {
+        logger_list->tail = 0;
+    }
+    return ret;
+}
+
+/* Read from the selected logs */
+int android_logger_list_read(struct logger_list *logger_list,
+                             struct log_msg *log_msg)
+{
+    struct logger *logger;
+    nfds_t nfds;
+    struct pollfd *p, *pollfds = NULL;
+    int error = 0, ret = 0;
+
+    memset(log_msg, 0, sizeof(struct log_msg));
+
+    if (!logger_list) {
+        return -ENODEV;
+    }
+
+    if (!(accessmode(logger_list->mode) & R_OK)) {
+        logger_list->error = EPERM;
+        goto done;
+    }
+
+    nfds = 0;
+    logger_for_each(logger, logger_list) {
+        ++nfds;
+    }
+    if (nfds <= 0) {
+        error = ENODEV;
+        goto done;
+    }
+
+    /* Do we have anything to offer from the buffer or state? */
+    if (logger_list->valid_entry) { /* implies we are also in a flush state */
+        goto flush;
+    }
+
+    ret = android_logger_list_flush(logger_list, log_msg);
+    if (ret) {
+        goto done;
+    }
+
+    if (logger_list->error) { /* implies we are also in a flush state */
+        goto done;
+    }
+
+    /* Lets start grinding on metal */
+    pollfds = calloc(nfds, sizeof(struct pollfd));
+    if (!pollfds) {
+        error = ENOMEM;
+        goto flush;
+    }
+
+    p = pollfds;
+    logger_for_each(logger, logger_list) {
+        p->fd = logger->fd;
+        p->events = POLLIN;
+        logger->revents = &p->revents;
+        ++p;
+    }
+
+    while (!ret && !error) {
+        int result;
+
+        /* If we oversleep it's ok, i.e. ignore EINTR. */
+        result = TEMP_FAILURE_RETRY(
+                    poll(pollfds, nfds, logger_list->timeout_ms));
+
+        if (result <= 0) {
+            if (result) {
+                error = errno;
+            } else if (logger_list->mode & O_NDELAY) {
+                error = EAGAIN;
+            } else {
+                logger_list->timeout_ms = LOG_TIMEOUT_NEVER;
+            }
+
+            logger_list->flush = true;
+            goto try_flush;
+        }
+
+        logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
+
+        /* Anti starvation */
+        if (!logger_list->flush
+                && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) {
+            /* Any queues with input pending that is low? */
+            bool starving = false;
+            logger_for_each(logger, logger_list) {
+                if ((*(logger->revents) & POLLIN)
+                        && low_queue(&logger->log_list)) {
+                    starving = true;
+                    break;
+                }
+            }
+
+            /* pushback on any queues that are not low */
+            if (starving) {
+                logger_for_each(logger, logger_list) {
+                    if ((*(logger->revents) & POLLIN)
+                            && !low_queue(&logger->log_list)) {
+                        *(logger->revents) &= ~POLLIN;
+                    }
+                }
+            }
+        }
+
+        logger_for_each(logger, logger_list) {
+            unsigned int hdr_size;
+            struct log_list *entry;
+            int diff;
+
+            if (!(*(logger->revents) & POLLIN)) {
+                continue;
+            }
+
+            memset(logger_list->entry.buf, 0, sizeof(struct log_msg));
+            /* NOTE: driver guarantees we read exactly one full entry */
+            result = read(logger->fd, logger_list->entry.buf,
+                          LOGGER_ENTRY_MAX_LEN);
+            if (result <= 0) {
+                if (!result) {
+                    error = EIO;
+                } else if (errno != EINTR) {
+                    error = errno;
+                }
+                continue;
+            }
+
+            if (logger_list->pid
+                    && (logger_list->pid != logger_list->entry.entry.pid)) {
+                continue;
+            }
+
+            hdr_size = logger_list->entry.entry.hdr_size;
+            if (!hdr_size) {
+                hdr_size = sizeof(logger_list->entry.entry_v1);
+            }
+
+            if ((hdr_size > sizeof(struct log_msg))
+                    || (logger_list->entry.entry.len
+                        > sizeof(logger_list->entry.buf) - hdr_size)
+                    || (logger_list->entry.entry.len != result - hdr_size)) {
+                error = EINVAL;
+                continue;
+            }
+
+            /* Promote entry to v3 format */
+            diff = sizeof(logger_list->entry.entry_v3) - hdr_size;
+            if (diff > 0) {
+                if (logger_list->entry.entry.len
+                        > sizeof(logger_list->entry.buf) - hdr_size - diff) {
+                    error = EINVAL;
+                    continue;
+                }
+                result += diff;
+                memmove(logger_list->entry.buf + hdr_size + diff,
+                        logger_list->entry.buf + hdr_size,
+                        logger_list->entry.entry.len + 1);
+                memset(logger_list->entry.buf + hdr_size, 0, diff);
+                logger_list->entry.entry.hdr_size = hdr_size + diff;
+            }
+            logger_list->entry.entry.lid = logger->id;
+
+            /* speedup: If not tail, and only one list, send directly */
+            if (!logger_list->tail
+                    && (list_head(&logger_list->node)
+                        == list_tail(&logger_list->node))) {
+                ret = result;
+                memcpy(log_msg->buf, logger_list->entry.buf, result + 1);
+                break;
+            }
+
+            entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1);
+
+            if (!entry) {
+                logger_list->valid_entry = true;
+                error = ENOMEM;
+                break;
+            }
+
+            logger_list->queued_lines++;
+
+            memcpy(entry->entry.buf, logger_list->entry.buf, result);
+            entry->entry.buf[result] = '\0';
+            list_add_tail(&logger->log_list, &entry->node);
+        }
+
+        if (ret <= 0) {
+try_flush:
+            ret = android_logger_list_flush(logger_list, log_msg);
+        }
+    }
+
+    free(pollfds);
+
+flush:
+    if (error) {
+        logger_list->flush = true;
+    }
+
+    if (ret <= 0) {
+        ret = android_logger_list_flush(logger_list, log_msg);
+
+        if (!ret && logger_list->valid_entry) {
+            ret = logger_list->entry.entry.hdr_size;
+            if (!ret) {
+                ret = sizeof(logger_list->entry.entry_v1);
+            }
+            ret += logger_list->entry.entry.len;
+
+            memcpy(log_msg->buf, logger_list->entry.buf,
+                   sizeof(struct log_msg));
+            logger_list->valid_entry = false;
+        }
+    }
+
+done:
+    if (logger_list->error) {
+        error = logger_list->error;
+    }
+    if (error) {
+        logger_list->error = error;
+        if (!ret) {
+            ret = -error;
+        }
+    }
+    return ret;
+}
+
+/* Close all the logs */
+void android_logger_list_free(struct logger_list *logger_list)
+{
+    if (logger_list == NULL) {
+        return;
+    }
+
+    while (!list_empty(&logger_list->node)) {
+        struct listnode *node = list_head(&logger_list->node);
+        struct logger *logger = node_to_item(node, struct logger, node);
+        android_logger_free(logger);
+    }
+
+    free(logger_list);
+}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 5766f8c..c3efc33 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -13,41 +13,34 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <time.h>
-#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
 #ifdef HAVE_PTHREADS
 #include <pthread.h>
 #endif
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
 #include <stdarg.h>
-#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+#if (FAKE_LOG_DEVICE == 0)
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+#include <time.h>
+#include <unistd.h>
 
-#include <log/logger.h>
 #include <log/logd.h>
-#include <log/log.h>
-
-#define LOGGER_LOG_MAIN		"log/main"
-#define LOGGER_LOG_RADIO	"log/radio"
-#define LOGGER_LOG_EVENTS	"log/events"
-#define LOGGER_LOG_SYSTEM	"log/system"
+#include <log/logger.h>
+#include <log/log_read.h>
+#include <private/android_filesystem_config.h>
 
 #define LOG_BUF_SIZE 1024
 
 #if FAKE_LOG_DEVICE
-// This will be defined when building for the host.
+/* This will be defined when building for the host. */
 #include "fake_log_device.h"
-#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
-#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
-#define log_close(filedes) fakeLogClose(filedes)
-#else
-#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
-#define log_writev(filedes, vector, count) writev(filedes, vector, count)
-#define log_close(filedes) close(filedes)
 #endif
 
 static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
@@ -58,11 +51,15 @@
 
 #define UNUSED  __attribute__((__unused__))
 
+static int logd_fd = -1;
+#if FAKE_LOG_DEVICE
+#define WEAK __attribute__((weak))
 static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
+#endif
 
 /*
  * This is used by the C++ code to decide if it should write logs through
- * the C code.  Basically, if /dev/log/... is available, we're running in
+ * the C code.  Basically, if /dev/socket/logd is available, we're running in
  * the simulator rather than a desktop tool and want to use the device.
  */
 static enum {
@@ -71,7 +68,7 @@
 int __android_log_dev_available(void)
 {
     if (g_log_status == kLogUninitialized) {
-        if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0)
+        if (access("/dev/socket/logdw", W_OK) == 0)
             g_log_status = kLogAvailable;
         else
             g_log_status = kLogNotAvailable;
@@ -81,13 +78,14 @@
 }
 
 static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED,
-        size_t nr UNUSED)
+                               size_t nr UNUSED)
 {
     return -1;
 }
 
 static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
 {
+#if FAKE_LOG_DEVICE
     ssize_t ret;
     int log_fd;
 
@@ -96,14 +94,66 @@
     } else {
         return EBADF;
     }
-
     do {
-        ret = log_writev(log_fd, vec, nr);
+        ret = fakeLogWritev(log_fd, vec, nr);
     } while (ret < 0 && errno == EINTR);
 
     return ret;
+#else
+    if (logd_fd == -1) {
+        return -1;
+    }
+    if (getuid() == AID_LOGD) {
+        /*
+         * ignore log messages we send to ourself.
+         * Such log messages are often generated by libraries we depend on
+         * which use standard Android logging.
+         */
+        return 0;
+    }
+    struct iovec newVec[nr + 2];
+    typeof_log_id_t log_id_buf = log_id;
+
+    newVec[0].iov_base   = (unsigned char *) &log_id_buf;
+    newVec[0].iov_len    = sizeof_log_id_t;
+
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME, &ts);
+    log_time realtime_ts;
+    realtime_ts.tv_sec = ts.tv_sec;
+    realtime_ts.tv_nsec = ts.tv_nsec;
+
+    newVec[1].iov_base   = (unsigned char *) &realtime_ts;
+    newVec[1].iov_len    = sizeof(log_time);
+
+    size_t i;
+    for (i = 2; i < nr + 2; i++) {
+        newVec[i].iov_base = vec[i-2].iov_base;
+        newVec[i].iov_len  = vec[i-2].iov_len;
+    }
+
+    /* The write below could be lost, but will never block. */
+    return writev(logd_fd, newVec, nr + 2);
+#endif
 }
 
+#if FAKE_LOG_DEVICE
+static const char *LOG_NAME[LOG_ID_MAX] = {
+    [LOG_ID_MAIN] = "main",
+    [LOG_ID_RADIO] = "radio",
+    [LOG_ID_EVENTS] = "events",
+    [LOG_ID_SYSTEM] = "system"
+};
+
+const WEAK char *android_log_id_to_name(log_id_t log_id)
+{
+    if (log_id >= LOG_ID_MAX) {
+        log_id = LOG_ID_MAIN;
+    }
+    return LOG_NAME[log_id];
+}
+#endif
+
 static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
 {
 #ifdef HAVE_PTHREADS
@@ -111,27 +161,35 @@
 #endif
 
     if (write_to_log == __write_to_log_init) {
-        log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
-        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
-        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
-        log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
-
         write_to_log = __write_to_log_kernel;
 
-        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
-                log_fds[LOG_ID_EVENTS] < 0) {
-            log_close(log_fds[LOG_ID_MAIN]);
-            log_close(log_fds[LOG_ID_RADIO]);
-            log_close(log_fds[LOG_ID_EVENTS]);
-            log_fds[LOG_ID_MAIN] = -1;
-            log_fds[LOG_ID_RADIO] = -1;
-            log_fds[LOG_ID_EVENTS] = -1;
+#if FAKE_LOG_DEVICE
+        int i;
+        for (i = 0; i < LOG_ID_MAX; i++) {
+            char buf[sizeof("/dev/log_system")];
+            snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+            log_fds[i] = fakeLogOpen(buf, O_WRONLY);
+        }
+#else
+        int sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+        if (sock != -1) {
+            if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+                /* NB: Loss of content */
+                close(sock);
+                sock = -1;
+            } else {
+                struct sockaddr_un un;
+                memset(&un, 0, sizeof(struct sockaddr_un));
+                un.sun_family = AF_UNIX;
+                strcpy(un.sun_path, "/dev/socket/logdw");
+
+                connect(sock, (struct sockaddr *)&un, sizeof(struct sockaddr_un));
+            }
+        } else {
             write_to_log = __write_to_log_null;
         }
-
-        if (log_fds[LOG_ID_SYSTEM] < 0) {
-            log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
-        }
+        logd_fd = sock;
+#endif
     }
 
 #ifdef HAVE_PTHREADS
@@ -288,7 +346,7 @@
  * handy if we just want to dump an integer into the log.
  */
 int __android_log_btwrite(int32_t tag, char type, const void *payload,
-    size_t len)
+                          size_t len)
 {
     struct iovec vec[3];
 
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
new file mode 100644
index 0000000..32a202b
--- /dev/null
+++ b/liblog/logd_write_kern.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2007-2014 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 <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <log/logd.h>
+#include <log/logger.h>
+
+#define LOGGER_LOG_MAIN		"log/main"
+#define LOGGER_LOG_RADIO	"log/radio"
+#define LOGGER_LOG_EVENTS	"log/events"
+#define LOGGER_LOG_SYSTEM	"log/system"
+
+#define LOG_BUF_SIZE 1024
+
+#if FAKE_LOG_DEVICE
+/* This will be defined when building for the host. */
+#include "fake_log_device.h"
+#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
+#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
+#define log_close(filedes) fakeLogClose(filedes)
+#else
+#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
+#define log_writev(filedes, vector, count) writev(filedes, vector, count)
+#define log_close(filedes) close(filedes)
+#endif
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
+#ifdef HAVE_PTHREADS
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#define UNUSED  __attribute__((__unused__))
+
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code.  Basically, if /dev/log/... is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+    kLogUninitialized, kLogNotAvailable, kLogAvailable
+} g_log_status = kLogUninitialized;
+int __android_log_dev_available(void)
+{
+    if (g_log_status == kLogUninitialized) {
+        if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0)
+            g_log_status = kLogAvailable;
+        else
+            g_log_status = kLogNotAvailable;
+    }
+
+    return (g_log_status == kLogAvailable);
+}
+
+static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED,
+                               size_t nr UNUSED)
+{
+    return -1;
+}
+
+static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+    ssize_t ret;
+    int log_fd;
+
+    if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
+        log_fd = log_fds[(int)log_id];
+    } else {
+        return EBADF;
+    }
+
+    do {
+        ret = log_writev(log_fd, vec, nr);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+#ifdef HAVE_PTHREADS
+    pthread_mutex_lock(&log_init_lock);
+#endif
+
+    if (write_to_log == __write_to_log_init) {
+        log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
+        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
+        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
+        log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);
+
+        write_to_log = __write_to_log_kernel;
+
+        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
+                log_fds[LOG_ID_EVENTS] < 0) {
+            log_close(log_fds[LOG_ID_MAIN]);
+            log_close(log_fds[LOG_ID_RADIO]);
+            log_close(log_fds[LOG_ID_EVENTS]);
+            log_fds[LOG_ID_MAIN] = -1;
+            log_fds[LOG_ID_RADIO] = -1;
+            log_fds[LOG_ID_EVENTS] = -1;
+            write_to_log = __write_to_log_null;
+        }
+
+        if (log_fds[LOG_ID_SYSTEM] < 0) {
+            log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
+        }
+    }
+
+#ifdef HAVE_PTHREADS
+    pthread_mutex_unlock(&log_init_lock);
+#endif
+
+    return write_to_log(log_id, vec, nr);
+}
+
+int __android_log_write(int prio, const char *tag, const char *msg)
+{
+    struct iovec vec[3];
+    log_id_t log_id = LOG_ID_MAIN;
+    char tmp_tag[32];
+
+    if (!tag)
+        tag = "";
+
+    /* XXX: This needs to go! */
+    if (!strcmp(tag, "HTC_RIL") ||
+        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+        !strcmp(tag, "AT") ||
+        !strcmp(tag, "GSM") ||
+        !strcmp(tag, "STK") ||
+        !strcmp(tag, "CDMA") ||
+        !strcmp(tag, "PHONE") ||
+        !strcmp(tag, "SMS")) {
+            log_id = LOG_ID_RADIO;
+            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
+            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+            tag = tmp_tag;
+    }
+
+    vec[0].iov_base   = (unsigned char *) &prio;
+    vec[0].iov_len    = 1;
+    vec[1].iov_base   = (void *) tag;
+    vec[1].iov_len    = strlen(tag) + 1;
+    vec[2].iov_base   = (void *) msg;
+    vec[2].iov_len    = strlen(msg) + 1;
+
+    return write_to_log(log_id, vec, 3);
+}
+
+int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
+{
+    struct iovec vec[3];
+    char tmp_tag[32];
+
+    if (!tag)
+        tag = "";
+
+    /* XXX: This needs to go! */
+    if ((bufID != LOG_ID_RADIO) &&
+         (!strcmp(tag, "HTC_RIL") ||
+        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+        !strcmp(tag, "AT") ||
+        !strcmp(tag, "GSM") ||
+        !strcmp(tag, "STK") ||
+        !strcmp(tag, "CDMA") ||
+        !strcmp(tag, "PHONE") ||
+        !strcmp(tag, "SMS"))) {
+            bufID = LOG_ID_RADIO;
+            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
+            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+            tag = tmp_tag;
+    }
+
+    vec[0].iov_base   = (unsigned char *) &prio;
+    vec[0].iov_len    = 1;
+    vec[1].iov_base   = (void *) tag;
+    vec[1].iov_len    = strlen(tag) + 1;
+    vec[2].iov_base   = (void *) msg;
+    vec[2].iov_len    = strlen(msg) + 1;
+
+    return write_to_log(bufID, vec, 3);
+}
+
+int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+{
+    char buf[LOG_BUF_SIZE];
+
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+    return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+{
+    va_list ap;
+    char buf[LOG_BUF_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+
+    return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+{
+    va_list ap;
+    char buf[LOG_BUF_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+
+    return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+void __android_log_assert(const char *cond, const char *tag,
+                          const char *fmt, ...)
+{
+    char buf[LOG_BUF_SIZE];
+
+    if (fmt) {
+        va_list ap;
+        va_start(ap, fmt);
+        vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+        va_end(ap);
+    } else {
+        /* Msg not provided, log condition.  N.B. Do not use cond directly as
+         * format string as it could contain spurious '%' syntax (e.g.
+         * "%d" in "blocks%devs == 0").
+         */
+        if (cond)
+            snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+        else
+            strcpy(buf, "Unspecified assertion failed");
+    }
+
+    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+
+    __builtin_trap(); /* trap so we have a chance to debug the situation */
+}
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+{
+    struct iovec vec[2];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = (void*)payload;
+    vec[1].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well.  Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+                          size_t len)
+{
+    struct iovec vec[3];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = (void*)payload;
+    vec[2].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp
index 02df460..090394c 100644
--- a/liblog/tests/benchmark_main.cpp
+++ b/liblog/tests/benchmark_main.cpp
@@ -16,6 +16,7 @@
 
 #include <benchmark.h>
 
+#include <inttypes.h>
 #include <regex.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -158,10 +159,10 @@
     sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5;
   }
   if (mean > (10000 * sdev)) {
-    printf("%-25s %10llu %10llu%s\n", full_name,
+    printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name,
             static_cast<uint64_t>(iterations), mean, throughput);
   } else {
-    printf("%-25s %10llu %10llu(\317\203%llu)%s\n", full_name,
+    printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n", full_name,
            static_cast<uint64_t>(iterations), mean, sdev, throughput);
   }
   fflush(stdout);
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 19406fb..39fe2ad 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -143,7 +143,7 @@
     for (int j = 0, i = 0; i < iters && j < 10*iters; ++i, ++j) {
         log_time ts;
         LOG_FAILURE_RETRY((
-            clock_gettime(CLOCK_REALTIME, &ts),
+            ts = log_time(CLOCK_REALTIME),
             android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))));
 
         for (;;) {
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 9ae8f22..ffb7fd1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <signal.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -85,8 +86,8 @@
 
 static void* ConcurrentPrintFn(void *arg) {
     int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
-                                  "TEST__android_log_print", "Concurrent %d",
-                                  reinterpret_cast<int>(arg));
+                                  "TEST__android_log_print", "Concurrent %" PRIuPTR,
+                                  reinterpret_cast<uintptr_t>(arg));
     return reinterpret_cast<void*>(ret);
 }
 
@@ -106,8 +107,9 @@
     for (i=0; i < NUM_CONCURRENT; i++) {
         void* result;
         ASSERT_EQ(0, pthread_join(t[i], &result));
-        if ((0 == ret) && (0 != reinterpret_cast<int>(result))) {
-            ret = reinterpret_cast<int>(result);
+        int this_result = reinterpret_cast<uintptr_t>(result);
+        if ((0 == ret) && (0 != this_result)) {
+            ret = this_result;
         }
     }
     ASSERT_LT(0, ret);
@@ -169,7 +171,7 @@
 
     ++signaled;
     if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
-        clock_gettime(CLOCK_MONOTONIC, &signal_time);
+        signal_time = log_time(CLOCK_MONOTONIC);
         signal_time.tv_sec += 2;
     }
 
diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk
index deac9de..3721fc6 100644
--- a/libnl_2/Android.mk
+++ b/libnl_2/Android.mk
@@ -26,6 +26,7 @@
 # Static Library
 LOCAL_MODULE := libnl_2
 LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -34,4 +35,5 @@
 LOCAL_SHARED_LIBRARIES:= liblog
 LOCAL_MODULE := libnl_2
 LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index fe50cc6..484cf50 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -7,13 +7,13 @@
 
 include $(CLEAR_VARS)
 PIXELFLINGER_SRC_FILES:= \
-    codeflinger/ARMAssemblerInterface.cpp \
-    codeflinger/ARMAssemblerProxy.cpp \
-    codeflinger/CodeCache.cpp \
-    codeflinger/GGLAssembler.cpp \
-    codeflinger/load_store.cpp \
-    codeflinger/blending.cpp \
-    codeflinger/texturing.cpp \
+	codeflinger/ARMAssemblerInterface.cpp \
+	codeflinger/ARMAssemblerProxy.cpp \
+	codeflinger/CodeCache.cpp \
+	codeflinger/GGLAssembler.cpp \
+	codeflinger/load_store.cpp \
+	codeflinger/blending.cpp \
+	codeflinger/texturing.cpp \
 	codeflinger/tinyutils/SharedBuffer.cpp \
 	codeflinger/tinyutils/VectorImpl.cpp \
 	fixed.cpp.arm \
@@ -26,39 +26,28 @@
 	raster.cpp \
 	buffer.cpp
 
-ifeq ($(TARGET_ARCH),arm)
-ifeq ($(TARGET_ARCH_VERSION),armv7-a)
-PIXELFLINGER_SRC_FILES += col32cb16blend_neon.S
-PIXELFLINGER_SRC_FILES += col32cb16blend.S
-else
-PIXELFLINGER_SRC_FILES += t32cb16blend.S
-PIXELFLINGER_SRC_FILES += col32cb16blend.S
-endif
+PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
+
+PIXELFLINGER_SRC_FILES_arm := \
+	codeflinger/ARMAssembler.cpp \
+	codeflinger/disassem.c \
+	col32cb16blend.S \
+	t32cb16blend.S \
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
 endif
 
-ifeq ($(TARGET_ARCH),arm)
-PIXELFLINGER_SRC_FILES += codeflinger/ARMAssembler.cpp
-PIXELFLINGER_SRC_FILES += codeflinger/disassem.c
-# special optimization flags for pixelflinger
-PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer
-endif
+PIXELFLINGER_SRC_FILES_arm64 := \
+	codeflinger/Arm64Assembler.cpp \
+	codeflinger/Arm64Disassembler.cpp \
+	arch-arm64/col32cb16blend.S \
+	arch-arm64/t32cb16blend.S \
 
-ifeq ($(TARGET_ARCH),mips)
-PIXELFLINGER_SRC_FILES += codeflinger/MIPSAssembler.cpp
-PIXELFLINGER_SRC_FILES += codeflinger/mips_disassem.c
-PIXELFLINGER_SRC_FILES += arch-mips/t32cb16blend.S
-PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer
-endif
-
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-
-ifeq ($(TARGET_ARCH),arm64)
-PIXELFLINGER_SRC_FILES += arch-arm64/t32cb16blend.S
-PIXELFLINGER_SRC_FILES += arch-arm64/col32cb16blend.S
-PIXELFLINGER_SRC_FILES += codeflinger/Arm64Assembler.cpp
-PIXELFLINGER_SRC_FILES += codeflinger/Arm64Disassembler.cpp
-PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer
-endif
+PIXELFLINGER_SRC_FILES_mips := \
+	codeflinger/MIPSAssembler.cpp \
+	codeflinger/mips_disassem.c \
+	arch-mips/t32cb16blend.S \
 
 #
 # Shared library
@@ -66,7 +55,11 @@
 
 LOCAL_MODULE:= libpixelflinger
 LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
+LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
+LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
+LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
 LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
+LOCAL_SHARED_LIBRARIES := libcutils liblog
 
 ifneq ($(BUILD_TINY_ANDROID),true)
 # Really this should go away entirely or at least not depend on
@@ -83,6 +76,9 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE:= libpixelflinger_static
 LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
+LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
+LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
+LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
 LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
 include $(BUILD_STATIC_LIBRARY)
 
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
index cbdab5a..dcb95c5 100644
--- a/libpixelflinger/buffer.cpp
+++ b/libpixelflinger/buffer.cpp
@@ -130,7 +130,7 @@
     }
 }
 
-void readRGB565(const surface_t* s, context_t* c,
+void readRGB565(const surface_t* s, context_t* /*c*/,
         uint32_t x, uint32_t y, pixel_t* pixel)
 {
     uint16_t v = *(reinterpret_cast<uint16_t*>(s->data) + (x + (s->stride * y)));
@@ -144,7 +144,7 @@
     pixel->s[3] = 5;
 }
 
-void readABGR8888(const surface_t* s, context_t* c,
+void readABGR8888(const surface_t* s, context_t* /*c*/,
         uint32_t x, uint32_t y, pixel_t* pixel)
 {
     uint32_t v = *(reinterpret_cast<uint32_t*>(s->data) + (x + (s->stride * y)));
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.cpp b/libpixelflinger/codeflinger/Arm64Assembler.cpp
index f37072a..bd11818 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.cpp
+++ b/libpixelflinger/codeflinger/Arm64Assembler.cpp
@@ -273,7 +273,7 @@
     *mPC++ = (0x54 << 24) | cc;
 }
 
-void ArmToArm64Assembler::BL(int cc, const char* label)
+void ArmToArm64Assembler::BL(int /*cc*/, const char* /*label*/)
 {
     NOT_IMPLEMENTED(); //Not Required
 }
@@ -289,7 +289,7 @@
     *mPC++ = A64_MOVZ_X(mZeroReg,0,0);
 }
 
-void ArmToArm64Assembler::epilog(uint32_t touched)
+void ArmToArm64Assembler::epilog(uint32_t /*touched*/)
 {
     // write epilog code
     static const int XLR = 30;
@@ -530,23 +530,23 @@
     if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
     *mPC++ = A64_MADD_W(Rd, Rm, Rs, mZeroReg);
 }
-void ArmToArm64Assembler::UMULL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs)
+void ArmToArm64Assembler::UMULL(int /*cc*/, int /*s*/,
+        int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
-void ArmToArm64Assembler::UMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs)
+void ArmToArm64Assembler::UMUAL(int /*cc*/, int /*s*/,
+        int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
-void ArmToArm64Assembler::SMULL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs)
+void ArmToArm64Assembler::SMULL(int /*cc*/, int /*s*/,
+        int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
-void ArmToArm64Assembler::SMUAL(int cc, int s,
-        int RdLo, int RdHi, int Rm, int Rs)
+void ArmToArm64Assembler::SMUAL(int /*cc*/, int /*s*/,
+        int /*RdLo*/, int /*RdHi*/, int /*Rm*/, int /*Rs*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
@@ -554,15 +554,15 @@
 // ----------------------------------------------------------------------------
 // branches relative to PC...
 // ----------------------------------------------------------------------------
-void ArmToArm64Assembler::B(int cc, uint32_t* pc){
+void ArmToArm64Assembler::B(int /*cc*/, uint32_t* /*pc*/){
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::BL(int cc, uint32_t* pc){
+void ArmToArm64Assembler::BL(int /*cc*/, uint32_t* /*pc*/){
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::BX(int cc, int Rn){
+void ArmToArm64Assembler::BX(int /*cc*/, int /*Rn*/){
     NOT_IMPLEMENTED(); //Not required
 }
 
@@ -661,11 +661,11 @@
 {
     return dataTransfer(opLDRH, cc, Rd, Rn, op_type);
 }
-void ArmToArm64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToArm64Assembler::LDRSB(int /*cc*/, int /*Rd*/, int /*Rn*/, uint32_t /*offset*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
-void ArmToArm64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+void ArmToArm64Assembler::LDRSH(int /*cc*/, int /*Rd*/, int /*Rn*/, uint32_t /*offset*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
@@ -723,15 +723,15 @@
 // ----------------------------------------------------------------------------
 // special...
 // ----------------------------------------------------------------------------
-void ArmToArm64Assembler::SWP(int cc, int Rn, int Rd, int Rm)
+void ArmToArm64Assembler::SWP(int /*cc*/, int /*Rn*/, int /*Rd*/, int /*Rm*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
-void ArmToArm64Assembler::SWPB(int cc, int Rn, int Rd, int Rm)
+void ArmToArm64Assembler::SWPB(int /*cc*/, int /*Rn*/, int /*Rd*/, int /*Rm*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
-void ArmToArm64Assembler::SWI(int cc, uint32_t comment)
+void ArmToArm64Assembler::SWI(int /*cc*/, uint32_t /*comment*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
@@ -739,31 +739,31 @@
 // ----------------------------------------------------------------------------
 // DSP instructions...
 // ----------------------------------------------------------------------------
-void ArmToArm64Assembler::PLD(int Rn, uint32_t offset) {
+void ArmToArm64Assembler::PLD(int /*Rn*/, uint32_t /*offset*/) {
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::CLZ(int cc, int Rd, int Rm)
+void ArmToArm64Assembler::CLZ(int /*cc*/, int /*Rd*/, int /*Rm*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::QADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToArm64Assembler::QADD(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+void ArmToArm64Assembler::QDADD(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToArm64Assembler::QSUB(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
 
-void ArmToArm64Assembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+void ArmToArm64Assembler::QDSUB(int /*cc*/, int /*Rd*/, int /*Rm*/, int /*Rn*/)
 {
     NOT_IMPLEMENTED(); //Not required
 }
@@ -817,15 +817,15 @@
     *mPC++ = A64_MADD_W(Rd, mTmpReg1, mTmpReg2, Rn);
 }
 
-void ArmToArm64Assembler::SMLAL(int cc, int xy,
-                int RdHi, int RdLo, int Rs, int Rm)
+void ArmToArm64Assembler::SMLAL(int /*cc*/, int /*xy*/,
+                int /*RdHi*/, int /*RdLo*/, int /*Rs*/, int /*Rm*/)
 {
     NOT_IMPLEMENTED(); //Not required
     return;
 }
 
-void ArmToArm64Assembler::SMLAW(int cc, int y,
-                int Rd, int Rm, int Rs, int Rn)
+void ArmToArm64Assembler::SMLAW(int /*cc*/, int /*y*/,
+                int /*Rd*/, int /*Rm*/, int /*Rs*/, int /*Rn*/)
 {
     NOT_IMPLEMENTED(); //Not required
     return;
@@ -890,13 +890,13 @@
     return OPERAND_REG_IMM;
 }
 
-uint32_t ArmToArm64Assembler::reg_rrx(int Rm)
+uint32_t ArmToArm64Assembler::reg_rrx(int /*Rm*/)
 {
     NOT_IMPLEMENTED();
     return OPERAND_UNSUPPORTED;
 }
 
-uint32_t ArmToArm64Assembler::reg_reg(int Rm, int type, int Rs)
+uint32_t ArmToArm64Assembler::reg_reg(int /*Rm*/, int /*type*/, int /*Rs*/)
 {
     NOT_IMPLEMENTED(); //Not required
     return OPERAND_UNSUPPORTED;
@@ -937,7 +937,7 @@
     }
 }
 
-uint32_t ArmToArm64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+uint32_t ArmToArm64Assembler::reg_scale_post(int /*Rm*/, int /*type*/, uint32_t /*shift*/)
 {
     NOT_IMPLEMENTED(); //Not required
     return OPERAND_UNSUPPORTED;
@@ -975,7 +975,7 @@
     }
 }
 
-uint32_t ArmToArm64Assembler::reg_post(int Rm)
+uint32_t ArmToArm64Assembler::reg_post(int /*Rm*/)
 {
     NOT_IMPLEMENTED(); //Not required
     return OPERAND_UNSUPPORTED;
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 4fe30d9..7446da2 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -201,13 +201,9 @@
         mCacheInUse += assemblySize;
         mWhen++;
         // synchronize caches...
-#if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
         const long base = long(assembly->base());
         const long curr = base + long(assembly->size());
-        err = cacheflush(base, curr, 0);
-        ALOGE_IF(err, "cacheflush error %s\n",
-                 strerror(errno));
-#endif
+        __builtin___clear_cache((void*)base, (void*)curr);
     }
 
     pthread_mutex_unlock(&mLock);
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 2422d7b..325caba 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -694,7 +694,7 @@
 // ---------------------------------------------------------------------------
 
 void GGLAssembler::build_alpha_test(component_t& fragment,
-                                    const fragment_parts_t& parts)
+                                    const fragment_parts_t& /*parts*/)
 {
     if (mAlphaTest != GGL_ALWAYS) {
         comment("Alpha Test");
@@ -796,7 +796,7 @@
     }
 }
 
-void GGLAssembler::build_iterate_f(const fragment_parts_t& parts)
+void GGLAssembler::build_iterate_f(const fragment_parts_t& /*parts*/)
 {
     const needs_t& needs = mBuilderContext.needs;
     if (GGL_READ_NEEDS(P_FOG, needs.p)) {
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index b2cfbb3..81950bf 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -694,7 +694,7 @@
 }
 
 void GGLAssembler::filter8(
-        const fragment_parts_t& parts,
+        const fragment_parts_t& /*parts*/,
         pixel_t& texel, const texture_unit_t& tmu,
         int U, int V, pointer_t& txPtr,
         int FRAC_BITS)
@@ -761,7 +761,7 @@
 }
 
 void GGLAssembler::filter16(
-        const fragment_parts_t& parts,
+        const fragment_parts_t& /*parts*/,
         pixel_t& texel, const texture_unit_t& tmu,
         int U, int V, pointer_t& txPtr,
         int FRAC_BITS)
@@ -879,10 +879,10 @@
 }
 
 void GGLAssembler::filter24(
-        const fragment_parts_t& parts,
-        pixel_t& texel, const texture_unit_t& tmu,
-        int U, int V, pointer_t& txPtr,
-        int FRAC_BITS)
+        const fragment_parts_t& /*parts*/,
+        pixel_t& texel, const texture_unit_t& /*tmu*/,
+        int /*U*/, int /*V*/, pointer_t& txPtr,
+        int /*FRAC_BITS*/)
 {
     // not supported yet (currently disabled)
     load(txPtr, texel, 0);
@@ -989,8 +989,8 @@
 }
 #else
 void GGLAssembler::filter32(
-        const fragment_parts_t& parts,
-        pixel_t& texel, const texture_unit_t& tmu,
+        const fragment_parts_t& /*parts*/,
+        pixel_t& texel, const texture_unit_t& /*tmu*/,
         int U, int V, pointer_t& txPtr,
         int FRAC_BITS)
 {
diff --git a/libpixelflinger/picker.cpp b/libpixelflinger/picker.cpp
index 030ef19..aa55229 100644
--- a/libpixelflinger/picker.cpp
+++ b/libpixelflinger/picker.cpp
@@ -26,7 +26,7 @@
 
 // ----------------------------------------------------------------------------
 
-void ggl_init_picker(context_t* c)
+void ggl_init_picker(context_t* /*c*/)
 {
 }
 
diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp
index 84e584e..ea5bc8e 100644
--- a/libpixelflinger/pixelflinger.cpp
+++ b/libpixelflinger/pixelflinger.cpp
@@ -662,7 +662,7 @@
     }
 }
 
-void ggl_enable_stencil_test(context_t* c, int enable)
+void ggl_enable_stencil_test(context_t* /*c*/, int /*enable*/)
 {
 }
 
diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp
index 32b2a97..26d8e45 100644
--- a/libpixelflinger/raster.cpp
+++ b/libpixelflinger/raster.cpp
@@ -51,7 +51,7 @@
 }
 
 void ggl_copyPixels(void* con, GGLint xs, GGLint ys,
-        GGLsizei width, GGLsizei height, GGLenum type)
+        GGLsizei width, GGLsizei height, GGLenum /*type*/)
 {
     GGL_CONTEXT(c, con);
 
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index aa23ca6..f84a28a 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -26,6 +26,10 @@
 #include <cutils/memory.h>
 #include <cutils/log.h>
 
+#ifdef __arm__
+#include <machine/cpu-features.h>
+#endif
+
 #include "buffer.h"
 #include "scanline.h"
 
@@ -408,10 +412,10 @@
         GGLAssembler assembler( new ArmToArm64Assembler(a) );
 #endif
         // generate the scanline code for the given needs
-        int err = assembler.scanline(c->state.needs, c);
+        bool err = assembler.scanline(c->state.needs, c) != 0;
         if (ggl_likely(!err)) {
             // finally, cache this assembly
-            err = gCodeCache.cache(a->key(), a);
+            err = gCodeCache.cache(a->key(), a) < 0;
         }
         if (ggl_unlikely(err)) {
             ALOGE("error generating or caching assembly. Reverting to NOP.");
@@ -534,7 +538,7 @@
     return x;
 }
 
-void blend_factor(context_t* c, pixel_t* r, 
+void blend_factor(context_t* /*c*/, pixel_t* r, 
         uint32_t factor, const pixel_t* src, const pixel_t* dst)
 {
     switch (factor) {
@@ -1161,7 +1165,7 @@
  *   blender.blend(<32-bit-src-pixel-value>,<ptr-to-16-bit-dest-pixel>)
  */
 struct blender_32to16 {
-    blender_32to16(context_t* c) { }
+    blender_32to16(context_t* /*c*/) { }
     void write(uint32_t s, uint16_t* dst) {
         if (s == 0)
             return;
@@ -1218,7 +1222,7 @@
  * where dstFactor=srcA*(1-srcA) srcFactor=srcA
  */
 struct blender_32to16_srcA {
-    blender_32to16_srcA(const context_t* c) { }
+    blender_32to16_srcA(const context_t* /*c*/) { }
     void write(uint32_t s, uint16_t* dst) {
         if (!s) {
             return;
@@ -2317,7 +2321,7 @@
     memset(dst, 0xFF, size);
 }
 
-void scanline_noop(context_t* c)
+void scanline_noop(context_t* /*c*/)
 {
 }
 
diff --git a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
index 84381d5..456be58 100644
--- a/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
+++ b/libpixelflinger/tests/arch-arm64/assembler/arm64_assembler_test.cpp
@@ -405,11 +405,11 @@
 };
 
 
-int flushcache()
+void flushcache()
 {
     const long base = long(instrMem);
     const long curr = base + long(instrMemSize);
-    return cacheflush(base, curr, 0);
+    __builtin___clear_cache((void*)base, (void*)curr);
 }
 void dataOpTest(dataOpTest_t test, ARMAssemblerInterface *a64asm, uint32_t Rd = 0,
                 uint32_t Rn = 1, uint32_t Rm = 2, uint32_t Rs = 3)
diff --git a/libpixelflinger/tests/gglmul/gglmul_test.cpp b/libpixelflinger/tests/gglmul/gglmul_test.cpp
index 073368e..5d460d6 100644
--- a/libpixelflinger/tests/gglmul/gglmul_test.cpp
+++ b/libpixelflinger/tests/gglmul/gglmul_test.cpp
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <stdint.h>
+#include <inttypes.h>
 
 #include "private/pixelflinger/ggl_fixed.h"
 
@@ -260,12 +261,12 @@
         if(actual == expected)
             printf(" Passed\n");
         else
-            printf(" Failed Actual(%ld) Expected(%ld)\n",
+            printf(" Failed Actual(%" PRId64 ") Expected(%" PRId64 ")\n",
                     actual, expected);
     }
 }
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     gglClampx_test();
     gglClz_test();
diff --git a/libsparse/defs.h b/libsparse/defs.h
new file mode 100644
index 0000000..34e63c5
--- /dev/null
+++ b/libsparse/defs.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _LIBSPARSE_DEFS_H_
+
+#ifndef __unused
+#define __unused        __attribute__((__unused__))
+#endif
+
+#endif /* _LIBSPARSE_DEFS_H_ */
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index e63c4a9..1cf8d8d 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -29,6 +29,7 @@
 #include <unistd.h>
 #include <zlib.h>
 
+#include "defs.h"
 #include "output_file.h"
 #include "sparse_format.h"
 #include "sparse_crc32.h"
@@ -264,7 +265,7 @@
 	.close = gz_file_close,
 };
 
-static int callback_file_open(struct output_file *out, int fd)
+static int callback_file_open(struct output_file *out __unused, int fd __unused)
 {
 	return 0;
 }
@@ -287,7 +288,7 @@
 	return 0;
 }
 
-static int callback_file_pad(struct output_file *out, int64_t len)
+static int callback_file_pad(struct output_file *out __unused, int64_t len __unused)
 {
 	return -1;
 }
@@ -631,8 +632,8 @@
 }
 
 struct output_file *output_file_open_callback(int (*write)(void *, const void *, int),
-		void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
-		int chunks, int crc)
+		void *priv, unsigned int block_size, int64_t len,
+		int gz __unused, int sparse, int chunks, int crc)
 {
 	int ret;
 	struct output_file_callback *outc;
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index 741e8c6..0f107b0 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -19,6 +19,7 @@
 
 #include <sparse/sparse.h>
 
+#include "defs.h"
 #include "sparse_file.h"
 
 #include "output_file.h"
@@ -189,7 +190,7 @@
 	return ret;
 }
 
-static int out_counter_write(void *priv, const void *data, int len)
+static int out_counter_write(void *priv, const void *data __unused, int len)
 {
 	int64_t *count = priv;
 	*count += len;
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c
index 704bcfa..873c87c 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.c
@@ -29,6 +29,8 @@
 
 #include <sparse/sparse.h>
 
+#include "defs.h"
+#include "output_file.h"
 #include "sparse_crc32.h"
 #include "sparse_file.h"
 #include "sparse_format.h"
@@ -175,7 +177,8 @@
 }
 
 static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size,
-		int fd, unsigned int blocks, unsigned int block, uint32_t *crc32)
+		int fd __unused, unsigned int blocks,
+		unsigned int block __unused, uint32_t *crc32)
 {
 	int ret;
 	int chunk;
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 3625d93..d3ce8f5 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -1,10 +1,11 @@
 #include <alloca.h>
 #include <errno.h>
-#include <sys/socket.h>
-#include <sys/types.h>
 #include <pthread.h>
+#include <signal.h>
 #include <string.h>
 #include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 
 #define LOG_TAG "SocketClient"
 #include <cutils/log.h>
@@ -43,8 +44,7 @@
     }
 }
 
-SocketClient::~SocketClient()
-{
+SocketClient::~SocketClient() {
     if (mSocketOwned) {
         close(mSocket);
     }
@@ -180,10 +180,19 @@
         return 0;
     }
 
+    int ret = 0;
+    int e = 0; // SLOGW and sigaction are not inert regarding errno
     int current = 0;
 
+    struct sigaction new_action, old_action;
+    memset(&new_action, 0, sizeof(new_action));
+    new_action.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &new_action, &old_action);
+
     for (;;) {
-        ssize_t rc = writev(mSocket, iov + current, iovcnt - current);
+        ssize_t rc = TEMP_FAILURE_RETRY(
+            writev(mSocket, iov + current, iovcnt - current));
+
         if (rc > 0) {
             size_t written = rc;
             while ((current < iovcnt) && (written >= iov[current].iov_len)) {
@@ -198,18 +207,21 @@
             continue;
         }
 
-        if (rc < 0 && errno == EINTR)
-            continue;
-
         if (rc == 0) {
+            e = EIO;
             SLOGW("0 length write :(");
-            errno = EIO;
         } else {
-            SLOGW("write error (%s)", strerror(errno));
+            e = errno;
+            SLOGW("write error (%s)", strerror(e));
         }
-        return -1;
+        ret = -1;
+        break;
     }
-    return 0;
+
+    sigaction(SIGPIPE, &old_action, &new_action);
+
+    errno = e;
+    return ret;
 }
 
 void SocketClient::incRef() {
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 5c75206..527a6a0 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -70,6 +70,10 @@
 }
 
 int SocketListener::startListener() {
+    return startListener(4);
+}
+
+int SocketListener::startListener(int backlog) {
 
     if (!mSocketName && mSock == -1) {
         SLOGE("Failed to start unbound listener");
@@ -84,7 +88,7 @@
         SLOGV("got mSock = %d for %s", mSock, mSocketName);
     }
 
-    if (mListen && listen(mSock, 4) < 0) {
+    if (mListen && listen(mSock, backlog) < 0) {
         SLOGE("Unable to listen on socket (%s)", strerror(errno));
         return -1;
     } else if (!mListen)
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
index 9565cc5..acfc020 100644
--- a/libusbhost/Android.mk
+++ b/libusbhost/Android.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-LOCAL_PATH := $(my-dir)
+LOCAL_PATH := $(call my-dir)
 
 # Static library for Linux host
 # ========================================================
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index e852d77..8acb4d4 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -323,8 +323,17 @@
 
 status_t String8::appendFormatV(const char* fmt, va_list args)
 {
-    int result = NO_ERROR;
-    int n = vsnprintf(NULL, 0, fmt, args);
+    int n, result = NO_ERROR;
+    va_list tmp_args;
+
+    /* args is undefined after vsnprintf.
+     * So we need a copy here to avoid the
+     * second vsnprintf access undefined args.
+     */
+    va_copy(tmp_args, args);
+    n = vsnprintf(NULL, 0, fmt, tmp_args);
+    va_end(tmp_args);
+
     if (n != 0) {
         size_t oldLength = length();
         char* buf = lockBuffer(oldLength + n);
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index a23d4ae..01f9249 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -279,7 +279,7 @@
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
-  ALOGV("Zip: Unable to find entry %.*s", name_length, name);
+  ALOGV("Zip: Unable to find entry %.*s", length, name);
   return kEntryNotFound;
 }
 
@@ -591,13 +591,7 @@
     archive->directory_map->release();
   }
   free(archive->hash_table);
-
-  /* ensure nobody tries to use the ZipArchive after it's closed */
-  archive->directory_offset = -1;
-  archive->fd = -1;
-  archive->num_entries = -1;
-  archive->hash_table_size = -1;
-  archive->hash_table = NULL;
+  free(archive);
 }
 
 static int32_t UpdateEntryFromDataDescriptor(int fd,
diff --git a/logcat/Android.mk b/logcat/Android.mk
index b5e27eb..dd15cb3 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -3,6 +3,10 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
+endif
+
 LOCAL_SRC_FILES:= logcat.cpp event.logtags
 
 LOCAL_SHARED_LIBRARIES := liblog
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 3c33938..00b5ba9 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -225,11 +225,23 @@
                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
                     "  -g              get the size of the log's ring buffer and exit\n"
-                    "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio'\n"
-                    "                  or 'events'. Multiple -b parameters are allowed and the\n"
+                    "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
+                    "                  'events' or 'all'. Multiple -b parameters are allowed and\n"
                     "                  results are interleaved. The default is -b main -b system.\n"
-                    "  -B              output the log in binary");
+                    "  -B              output the log in binary.\n"
+                    "  -S              output statistics.\n");
 
+#ifdef USERDEBUG_BUILD
+
+    fprintf(stderr, "--------------------- eng & userdebug builds only ---------------------------\n"
+                    "  -G <count>      set size of log's ring buffer and exit\n"
+                    "  -p              output prune white and ~black list\n"
+                    "  -P '<list> ...' set prune white and ~black list; UID, /PID or !(worst UID)\n"
+                    "                  default is ~!, prune worst UID.\n"
+                    "-----------------------------------------------------------------------------\n"
+    );
+
+#endif
 
     fprintf(stderr,"\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
@@ -278,6 +290,12 @@
     int hasSetLogFormat = 0;
     int clearLog = 0;
     int getLogSize = 0;
+#ifdef USERDEBUG_BUILD
+    unsigned long setLogSize = 0;
+    int getPruneList = 0;
+    char *setPruneList = NULL;
+#endif
+    int printStatistics = 0;
     int mode = O_RDONLY;
     const char *forceFilters = NULL;
     log_device_t* devices = NULL;
@@ -303,7 +321,13 @@
     for (;;) {
         int ret;
 
-        ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B");
+        ret = getopt(argc, argv,
+#ifdef USERDEBUG_BUILD
+            "cdt:T:gG:sQf:r::n:v:b:BSpP:"
+#else
+            "cdt:T:gsQf:r::n:v:b:BS"
+#endif
+        );
 
         if (ret < 0) {
             break;
@@ -335,7 +359,89 @@
                 getLogSize = 1;
             break;
 
+#ifdef USERDEBUG_BUILD
+
+            case 'G': {
+                // would use atol if not for the multiplier
+                char *cp = optarg;
+                setLogSize = 0;
+                while (('0' <= *cp) && (*cp <= '9')) {
+                    setLogSize *= 10;
+                    setLogSize += *cp - '0';
+                    ++cp;
+                }
+
+                switch(*cp) {
+                case 'g':
+                case 'G':
+                    setLogSize *= 1024;
+                /* FALLTHRU */
+                case 'm':
+                case 'M':
+                    setLogSize *= 1024;
+                /* FALLTHRU */
+                case 'k':
+                case 'K':
+                    setLogSize *= 1024;
+                /* FALLTHRU */
+                case '\0':
+                break;
+
+                default:
+                    setLogSize = 0;
+                }
+
+                if (!setLogSize) {
+                    fprintf(stderr, "ERROR: -G <num><multiplier>\n");
+                    exit(1);
+                }
+            }
+            break;
+
+            case 'p':
+                getPruneList = 1;
+            break;
+
+            case 'P':
+                setPruneList = optarg;
+            break;
+
+#endif
+
             case 'b': {
+                if (strcmp(optarg, "all") == 0) {
+                    while (devices) {
+                        dev = devices;
+                        devices = dev->next;
+                        delete dev;
+                    }
+
+                    dev = devices = new log_device_t("main", false, 'm');
+                    android::g_devCount = 1;
+                    if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
+                        dev->next = new log_device_t("system", false, 's');
+                        if (dev->next) {
+                            dev = dev->next;
+                            android::g_devCount++;
+                        }
+                    }
+                    if (android_name_to_log_id("radio") == LOG_ID_RADIO) {
+                        dev->next = new log_device_t("radio", false, 'r');
+                        if (dev->next) {
+                            dev = dev->next;
+                            android::g_devCount++;
+                        }
+                    }
+                    if (android_name_to_log_id("events") == LOG_ID_EVENTS) {
+                        dev->next = new log_device_t("events", true, 'e');
+                        if (dev->next) {
+                            android::g_devCount++;
+                            needBinary = true;
+                        }
+                    }
+                    break;
+                }
+
                 bool binary = strcmp(optarg, "events") == 0;
                 if (binary) {
                     needBinary = true;
@@ -470,6 +576,10 @@
                 }
                 break;
 
+            case 'S':
+                printStatistics = 1;
+                break;
+
             default:
                 fprintf(stderr,"Unrecognized Option\n");
                 android::show_help(argv[0]);
@@ -563,8 +673,17 @@
             }
         }
 
+#ifdef USERDEBUG_BUILD
+
+        if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
+            perror("setLogSize");
+            exit(EXIT_FAILURE);
+        }
+
+#endif
+
         if (getLogSize) {
-            int size, readable;
+            long size, readable;
 
             size = android_logger_get_log_size(dev->logger);
             if (size < 0) {
@@ -578,7 +697,7 @@
                 exit(EXIT_FAILURE);
             }
 
-            printf("%s: ring buffer is %dKb (%dKb consumed), "
+            printf("%s: ring buffer is %ldKb (%ldKb consumed), "
                    "max entry is %db, max payload is %db\n", dev->device,
                    size / 1024, readable / 1024,
                    (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
@@ -587,9 +706,95 @@
         dev = dev->next;
     }
 
+#ifdef USERDEBUG_BUILD
+
+    if (setPruneList) {
+        size_t len = strlen(setPruneList) + 32; // margin to allow rc
+        char *buf = (char *) malloc(len);
+
+        strcpy(buf, setPruneList);
+        int ret = android_logger_set_prune_list(logger_list, buf, len);
+        free(buf);
+
+        if (ret) {
+            perror("setPruneList");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+#endif
+
+    if (
+#ifdef USERDEBUG_BUILD
+        printStatistics || getPruneList
+#else
+        printStatistics
+#endif
+    ) {
+        size_t len = 8192;
+        char *buf;
+
+        for(int retry = 32;
+                (retry >= 0) && ((buf = new char [len]));
+                delete [] buf, --retry) {
+#ifdef USERDEBUG_BUILD
+            if (getPruneList) {
+                android_logger_get_prune_list(logger_list, buf, len);
+            } else {
+                android_logger_get_statistics(logger_list, buf, len);
+            }
+#else
+            android_logger_get_statistics(logger_list, buf, len);
+#endif
+            buf[len-1] = '\0';
+            size_t ret = atol(buf) + 1;
+            if (ret < 4) {
+                delete [] buf;
+                buf = NULL;
+                break;
+            }
+            bool check = ret <= len;
+            len = ret;
+            if (check) {
+                break;
+            }
+        }
+
+        if (!buf) {
+            perror("response read");
+            exit(EXIT_FAILURE);
+        }
+
+        // remove trailing FF
+        char *cp = buf + len - 1;
+        *cp = '\0';
+        bool truncated = *--cp != '\f';
+        if (!truncated) {
+            *cp = '\0';
+        }
+
+        // squash out the byte count
+        cp = buf;
+        if (!truncated) {
+            while (isdigit(*cp) || (*cp == '\n')) {
+                ++cp;
+            }
+        }
+
+        printf("%s", cp);
+        delete [] buf;
+        exit(0);
+    }
+
+
     if (getLogSize) {
         exit(0);
     }
+#ifdef USERDEBUG_BUILD
+    if (setLogSize || setPruneList) {
+        exit(0);
+    }
+#endif
     if (clearLog) {
         exit(0);
     }
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index f963a3a..fc696bb 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -319,12 +319,16 @@
 
 TEST(logcat, blocking) {
     FILE *fp;
-    unsigned long long v = 0xDEADBEEFA55A0000ULL;
+    unsigned long long v = 0xDEADBEEFA55F0000ULL;
 
     pid_t pid = getpid();
 
     v += pid & 0xFFFF;
 
+    LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+    v &= 0xFFFFFFFFFFFAFFFFULL;
+
     ASSERT_EQ(0, NULL == (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events 2>&1",
@@ -341,12 +345,12 @@
     while (fgets(buffer, sizeof(buffer), fp)) {
         alarm(2);
 
-        ++count;
-
         if (!strncmp(buffer, "DONE", 4)) {
             break;
         }
 
+        ++count;
+
         int p;
         unsigned long long l;
 
@@ -369,7 +373,7 @@
 
     pclose(fp);
 
-    ASSERT_LT(10, count);
+    ASSERT_LE(2, count);
 
     ASSERT_EQ(1, signals);
 }
@@ -385,12 +389,16 @@
 
 TEST(logcat, blocking_tail) {
     FILE *fp;
-    unsigned long long v = 0xA55ADEADBEEF0000ULL;
+    unsigned long long v = 0xA55FDEADBEEF0000ULL;
 
     pid_t pid = getpid();
 
     v += pid & 0xFFFF;
 
+    LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+    v &= 0xFFFAFFFFFFFFFFFFULL;
+
     ASSERT_EQ(0, NULL == (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events -T 5 2>&1",
@@ -407,12 +415,12 @@
     while (fgets(buffer, sizeof(buffer), fp)) {
         alarm(2);
 
-        ++count;
-
         if (!strncmp(buffer, "DONE", 4)) {
             break;
         }
 
+        ++count;
+
         int p;
         unsigned long long l;
 
@@ -431,13 +439,91 @@
     alarm(0);
     signal(SIGALRM, SIG_DFL);
 
-    /* Generate SIGPIPE */
+    // Generate SIGPIPE
     fclose(fp);
     caught_blocking_tail(0);
 
     pclose(fp);
 
-    ASSERT_LT(5, count);
+    ASSERT_LE(2, count);
+
+    ASSERT_EQ(1, signals);
+}
+
+static void caught_blocking_clear(int signum)
+{
+    unsigned long long v = 0xDEADBEEFA55C0000ULL;
+
+    v += getpid() & 0xFFFF;
+
+    LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+}
+
+TEST(logcat, blocking_clear) {
+    FILE *fp;
+    unsigned long long v = 0xDEADBEEFA55C0000ULL;
+
+    pid_t pid = getpid();
+
+    v += pid & 0xFFFF;
+
+    // This test is racey; an event occurs between clear and dump.
+    // We accept that we will get a false positive, but never a false negative.
+    ASSERT_EQ(0, NULL == (fp = popen(
+      "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
+      " logcat -b events -c 2>&1 ;"
+      " logcat -b events 2>&1",
+      "r")));
+
+    char buffer[5120];
+
+    int count = 0;
+
+    int signals = 0;
+
+    signal(SIGALRM, caught_blocking_clear);
+    alarm(2);
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        alarm(2);
+
+        if (!strncmp(buffer, "clearLog: ", 10)) {
+            fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+            count = signals = 1;
+            break;
+        }
+
+        if (!strncmp(buffer, "DONE", 4)) {
+            break;
+        }
+
+        ++count;
+
+        int p;
+        unsigned long long l;
+
+        if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l))
+         || (p != pid)) {
+            continue;
+        }
+
+        if (l == v) {
+            if (count > 1) {
+                fprintf(stderr, "WARNING: Possible false positive\n");
+            }
+            ++signals;
+            break;
+        }
+    }
+    alarm(0);
+    signal(SIGALRM, SIG_DFL);
+
+    // Generate SIGPIPE
+    fclose(fp);
+    caught_blocking_clear(0);
+
+    pclose(fp);
+
+    ASSERT_LE(1, count);
 
     ASSERT_EQ(1, signals);
 }
diff --git a/logd/Android.mk b/logd/Android.mk
new file mode 100644
index 0000000..b0bc746
--- /dev/null
+++ b/logd/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= logd
+
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
+endif
+
+LOCAL_SRC_FILES := \
+    main.cpp \
+    LogCommand.cpp \
+    CommandListener.cpp \
+    LogListener.cpp \
+    LogReader.cpp \
+    FlushCommand.cpp \
+    LogBuffer.cpp \
+    LogBufferElement.cpp \
+    LogTimes.cpp \
+    LogStatistics.cpp \
+    LogWhiteBlackList.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libsysutils \
+    liblog \
+    libcutils \
+    libutils
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
new file mode 100644
index 0000000..12b10ca
--- /dev/null
+++ b/logd/CommandListener.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2012-2014 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 <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <private/android_filesystem_config.h>
+#include <sysutils/SocketClient.h>
+
+#include "CommandListener.h"
+#include "LogCommand.h"
+
+CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
+                                 LogListener * /*swl*/)
+        : FrameworkListener("logd")
+        , mBuf(*buf) {
+    // registerCmd(new ShutdownCmd(buf, writer, swl));
+    registerCmd(new ClearCmd(buf));
+    registerCmd(new GetBufSizeCmd(buf));
+#ifdef USERDEBUG_BUILD
+    registerCmd(new SetBufSizeCmd(buf));
+#endif
+    registerCmd(new GetBufSizeUsedCmd(buf));
+    registerCmd(new GetStatisticsCmd(buf));
+#ifdef USERDEBUG_BUILD
+    registerCmd(new SetPruneListCmd(buf));
+    registerCmd(new GetPruneListCmd(buf));
+#endif
+}
+
+CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
+                                          LogListener *swl)
+        : LogCommand("shutdown")
+        , mBuf(*buf)
+        , mReader(*reader)
+        , mSwl(*swl)
+{ }
+
+int CommandListener::ShutdownCmd::runCommand(SocketClient * /*cli*/,
+                                             int /*argc*/,
+                                             char ** /*argv*/) {
+    mSwl.stopListener();
+    mReader.stopListener();
+    exit(0);
+}
+
+CommandListener::ClearCmd::ClearCmd(LogBuffer *buf)
+        : LogCommand("clear")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::ClearCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (!clientHasLogCredentials(cli)) {
+        cli->sendMsg("Permission Denied");
+        return 0;
+    }
+
+    if (argc < 2) {
+        cli->sendMsg("Missing Argument");
+        return 0;
+    }
+
+    int id = atoi(argv[1]);
+    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    mBuf.clear((log_id_t) id);
+    cli->sendMsg("success");
+    return 0;
+}
+
+CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer *buf)
+        : LogCommand("getLogSize")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (argc < 2) {
+        cli->sendMsg("Missing Argument");
+        return 0;
+    }
+
+    int id = atoi(argv[1]);
+    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    unsigned long size = mBuf.getSize((log_id_t) id);
+    char buf[512];
+    snprintf(buf, sizeof(buf), "%lu", size);
+    cli->sendMsg(buf);
+    return 0;
+}
+
+#ifdef USERDEBUG_BUILD
+
+CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf)
+        : LogCommand("setLogSize")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (!clientHasLogCredentials(cli)) {
+        cli->sendMsg("Permission Denied");
+        return 0;
+    }
+
+    if (argc < 3) {
+        cli->sendMsg("Missing Argument");
+        return 0;
+    }
+
+    int id = atoi(argv[1]);
+    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    unsigned long size = atol(argv[2]);
+    if (mBuf.setSize((log_id_t) id, size)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    cli->sendMsg("success");
+    return 0;
+}
+
+#endif // USERDEBUG_BUILD
+
+CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
+        : LogCommand("getLogSizeUsed")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (argc < 2) {
+        cli->sendMsg("Missing Argument");
+        return 0;
+    }
+
+    int id = atoi(argv[1]);
+    if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+        cli->sendMsg("Range Error");
+        return 0;
+    }
+
+    unsigned long size = mBuf.getSizeUsed((log_id_t) id);
+    char buf[512];
+    snprintf(buf, sizeof(buf), "%lu", size);
+    cli->sendMsg(buf);
+    return 0;
+}
+
+CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf)
+        : LogCommand("getStatistics")
+        , mBuf(*buf)
+{ }
+
+static void package_string(char **strp) {
+    const char *a = *strp;
+    if (!a) {
+        a = "";
+    }
+
+    // Calculate total buffer size prefix, count is the string length w/o nul
+    char fmt[32];
+    for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
+        snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
+    }
+
+    char *b = *strp;
+    *strp = NULL;
+    asprintf(strp, fmt, a);
+    free(b);
+}
+
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    uid_t uid = cli->getUid();
+    gid_t gid = cli->getGid();
+    if (clientHasLogCredentials(cli)) {
+        uid = AID_ROOT;
+    }
+
+    unsigned int logMask = -1;
+    if (argc > 1) {
+        logMask = 0;
+        for (int i = 1; i < argc; ++i) {
+            int id = atoi(argv[i]);
+            if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+                cli->sendMsg("Range Error");
+                return 0;
+            }
+            logMask |= 1 << id;
+        }
+    }
+
+    char *buf = NULL;
+
+    mBuf.formatStatistics(&buf, uid, logMask);
+    if (!buf) {
+        cli->sendMsg("Failed");
+    } else {
+        package_string(&buf);
+        cli->sendMsg(buf);
+        free(buf);
+    }
+    return 0;
+}
+
+#ifdef USERDEBUG_BUILD
+
+CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf)
+        : LogCommand("getPruneList")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
+                                         int /*argc*/, char ** /*argv*/) {
+    char *buf = NULL;
+    mBuf.formatPrune(&buf);
+    if (!buf) {
+        cli->sendMsg("Failed");
+    } else {
+        package_string(&buf);
+        cli->sendMsg(buf);
+        free(buf);
+    }
+    return 0;
+}
+
+CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf)
+        : LogCommand("setPruneList")
+        , mBuf(*buf)
+{ }
+
+int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
+                                         int argc, char **argv) {
+    if (!clientHasLogCredentials(cli)) {
+        cli->sendMsg("Permission Denied");
+        return 0;
+    }
+
+    char *cp = NULL;
+    for (int i = 1; i < argc; ++i) {
+        char *p = cp;
+        if (p) {
+            cp = NULL;
+            asprintf(&cp, "%s %s", p, argv[i]);
+            free(p);
+        } else {
+            asprintf(&cp, "%s", argv[i]);
+        }
+    }
+
+    int ret = mBuf.initPrune(cp);
+    free(cp);
+
+    if (ret) {
+        cli->sendMsg("Invalid");
+        return 0;
+    }
+
+    cli->sendMsg("success");
+
+    return 0;
+}
+
+#endif // USERDEBUG_BUILD
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
new file mode 100644
index 0000000..de1dcb9
--- /dev/null
+++ b/logd/CommandListener.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012-2014 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.
+ */
+
+#ifndef _COMMANDLISTENER_H__
+#define _COMMANDLISTENER_H__
+
+#include <sysutils/FrameworkListener.h>
+#include "LogCommand.h"
+#include "LogBuffer.h"
+#include "LogReader.h"
+#include "LogListener.h"
+
+class CommandListener : public FrameworkListener {
+    LogBuffer &mBuf;
+
+public:
+    CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl);
+    virtual ~CommandListener() {}
+
+private:
+    class ShutdownCmd : public LogCommand {
+        LogBuffer &mBuf;
+        LogReader &mReader;
+        LogListener &mSwl;
+
+    public:
+        ShutdownCmd(LogBuffer *buf, LogReader *reader, LogListener *swl);
+        virtual ~ShutdownCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
+#define LogBufferCmd(name)                                       \
+    class name##Cmd : public LogCommand {                        \
+        LogBuffer &mBuf;                                         \
+    public:                                                      \
+        name##Cmd(LogBuffer *buf);                               \
+        virtual ~name##Cmd() {}                                  \
+        int runCommand(SocketClient *c, int argc, char ** argv); \
+    };
+
+    LogBufferCmd(Clear)
+    LogBufferCmd(GetBufSize)
+#ifdef USERDEBUG_BUILD
+    LogBufferCmd(SetBufSize)
+#endif
+    LogBufferCmd(GetBufSizeUsed)
+    LogBufferCmd(GetStatistics)
+#ifdef USERDEBUG_BUILD
+    LogBufferCmd(GetPruneList)
+    LogBufferCmd(SetPruneList)
+#endif
+};
+
+#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
new file mode 100644
index 0000000..0b8c31b
--- /dev/null
+++ b/logd/FlushCommand.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012-2014 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 <stdlib.h>
+
+#include "FlushCommand.h"
+#include "LogBufferElement.h"
+#include "LogCommand.h"
+#include "LogReader.h"
+#include "LogTimes.h"
+
+FlushCommand::FlushCommand(LogReader &reader,
+                           bool nonBlock,
+                           unsigned long tail,
+                           unsigned int logMask,
+                           pid_t pid)
+        : mReader(reader)
+        , mNonBlock(nonBlock)
+        , mTail(tail)
+        , mLogMask(logMask)
+        , mPid(pid)
+{ }
+
+// runSocketCommand is called once for every open client on the
+// log reader socket. Here we manage and associated the reader
+// client tracking and log region locks LastLogTimes list of
+// LogTimeEntrys, and spawn a transitory per-client thread to
+// work at filing data to the  socket.
+//
+// global LogTimeEntry::lock() is used to protect access,
+// reference counts are used to ensure that individual
+// LogTimeEntry lifetime is managed when not protected.
+void FlushCommand::runSocketCommand(SocketClient *client) {
+    LogTimeEntry *entry = NULL;
+    LastLogTimes &times = mReader.logbuf().mTimes;
+
+    LogTimeEntry::lock();
+    LastLogTimes::iterator it = times.begin();
+    while(it != times.end()) {
+        entry = (*it);
+        if (entry->mClient == client) {
+            entry->triggerReader_Locked();
+            if (entry->runningReader_Locked()) {
+                LogTimeEntry::unlock();
+                return;
+            }
+            entry->incRef_Locked();
+            break;
+        }
+        it++;
+    }
+
+    if (it == times.end()) {
+        // Create LogTimeEntry in notifyNewLog() ?
+        if (mTail == (unsigned long) -1) {
+            LogTimeEntry::unlock();
+            return;
+        }
+        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid);
+        times.push_back(entry);
+    }
+
+    client->incRef();
+
+    // release client and entry reference counts once done
+    entry->startReader_Locked();
+    LogTimeEntry::unlock();
+}
+
+bool FlushCommand::hasReadLogs(SocketClient *client) {
+    return clientHasLogCredentials(client);
+}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
new file mode 100644
index 0000000..715daac
--- /dev/null
+++ b/logd/FlushCommand.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012-2013 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.
+ */
+#ifndef _FLUSH_COMMAND_H
+#define _FLUSH_COMMAND_H
+
+#include <sysutils/SocketClientCommand.h>
+
+class LogReader;
+
+class FlushCommand : public SocketClientCommand {
+    LogReader &mReader;
+    bool mNonBlock;
+    unsigned long mTail;
+    unsigned int mLogMask;
+    pid_t mPid;
+
+public:
+    FlushCommand(LogReader &mReader,
+                 bool nonBlock = false,
+                 unsigned long tail = -1,
+                 unsigned int logMask = -1,
+                 pid_t pid = 0);
+    virtual void runSocketCommand(SocketClient *client);
+
+    static bool hasReadLogs(SocketClient *client);
+};
+
+#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
new file mode 100644
index 0000000..197b7e8
--- /dev/null
+++ b/logd/LogBuffer.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2012-2014 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 <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+
+#include "LogBuffer.h"
+#include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
+#include "LogReader.h"
+
+// Default
+#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+#ifdef USERDEBUG_BUILD
+#define log_buffer_size(id) mMaxSize[id]
+#else
+#define log_buffer_size(id) LOG_BUFFER_SIZE
+#endif
+
+LogBuffer::LogBuffer(LastLogTimes *times)
+        : mTimes(*times) {
+    pthread_mutex_init(&mLogElementsLock, NULL);
+#ifdef USERDEBUG_BUILD
+    log_id_for_each(i) {
+        mMaxSize[i] = LOG_BUFFER_SIZE;
+    }
+#endif
+}
+
+void LogBuffer::log(log_id_t log_id, log_time realtime,
+                    uid_t uid, pid_t pid, const char *msg,
+                    unsigned short len) {
+    if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
+        return;
+    }
+    LogBufferElement *elem = new LogBufferElement(log_id, realtime,
+                                                  uid, pid, msg, len);
+
+    pthread_mutex_lock(&mLogElementsLock);
+
+    // Insert elements in time sorted order if possible
+    //  NB: if end is region locked, place element at end of list
+    LogBufferElementCollection::iterator it = mLogElements.end();
+    LogBufferElementCollection::iterator last = it;
+    while (--it != mLogElements.begin()) {
+        if ((*it)->getRealTime() <= realtime) {
+            break;
+        }
+        last = it;
+    }
+
+    if (last == mLogElements.end()) {
+        mLogElements.push_back(elem);
+    } else {
+        log_time end;
+        bool end_set = false;
+        bool end_always = false;
+
+        LogTimeEntry::lock();
+
+        LastLogTimes::iterator t = mTimes.begin();
+        while(t != mTimes.end()) {
+            LogTimeEntry *entry = (*t);
+            if (entry->owned_Locked()) {
+                if (!entry->mNonBlock) {
+                    end_always = true;
+                    break;
+                }
+                if (!end_set || (end <= entry->mEnd)) {
+                    end = entry->mEnd;
+                    end_set = true;
+                }
+            }
+            t++;
+        }
+
+        if (end_always
+                || (end_set && (end >= (*last)->getMonotonicTime()))) {
+            mLogElements.push_back(elem);
+        } else {
+            mLogElements.insert(last,elem);
+        }
+
+        LogTimeEntry::unlock();
+    }
+
+    stats.add(len, log_id, uid, pid);
+    maybePrune(log_id);
+    pthread_mutex_unlock(&mLogElementsLock);
+}
+
+// If we're using more than 256K of memory for log entries, prune
+// at least 10% of the log entries.
+//
+// mLogElementsLock must be held when this function is called.
+void LogBuffer::maybePrune(log_id_t id) {
+    size_t sizes = stats.sizes(id);
+    if (sizes > log_buffer_size(id)) {
+        size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10);
+        size_t elements = stats.elements(id);
+        unsigned long pruneRows = elements * sizeOver90Percent / sizes;
+        elements /= 10;
+        if (pruneRows <= elements) {
+            pruneRows = elements;
+        }
+        prune(id, pruneRows);
+    }
+}
+
+// prune "pruneRows" of type "id" from the buffer.
+//
+// mLogElementsLock must be held when this function is called.
+void LogBuffer::prune(log_id_t id, unsigned long pruneRows) {
+    LogTimeEntry *oldest = NULL;
+
+    LogTimeEntry::lock();
+
+    // Region locked?
+    LastLogTimes::iterator t = mTimes.begin();
+    while(t != mTimes.end()) {
+        LogTimeEntry *entry = (*t);
+        if (entry->owned_Locked()
+                && (!oldest || (oldest->mStart > entry->mStart))) {
+            oldest = entry;
+        }
+        t++;
+    }
+
+    LogBufferElementCollection::iterator it;
+
+    // prune by worst offender by uid
+    while (pruneRows > 0) {
+        // recalculate the worst offender on every batched pass
+        uid_t worst = (uid_t) -1;
+        size_t worst_sizes = 0;
+        size_t second_worst_sizes = 0;
+
+#ifdef USERDEBUG_BUILD
+        if (mPrune.worstUidEnabled())
+#endif
+        {
+            LidStatistics &l = stats.id(id);
+            UidStatisticsCollection::iterator iu;
+            for (iu = l.begin(); iu != l.end(); ++iu) {
+                UidStatistics *u = (*iu);
+                size_t sizes = u->sizes();
+                if (worst_sizes < sizes) {
+                    second_worst_sizes = worst_sizes;
+                    worst_sizes = sizes;
+                    worst = u->getUid();
+                }
+                if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
+                    second_worst_sizes = sizes;
+                }
+            }
+        }
+
+        bool kick = false;
+        for(it = mLogElements.begin(); it != mLogElements.end();) {
+            LogBufferElement *e = *it;
+
+            if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
+                break;
+            }
+
+            if (e->getLogId() != id) {
+                ++it;
+                continue;
+            }
+
+            uid_t uid = e->getUid();
+
+            if (uid == worst) {
+                it = mLogElements.erase(it);
+                unsigned short len = e->getMsgLen();
+                stats.subtract(len, id, worst, e->getPid());
+                delete e;
+                kick = true;
+                pruneRows--;
+                if ((pruneRows == 0) || (worst_sizes < second_worst_sizes)) {
+                    break;
+                }
+                worst_sizes -= len;
+            }
+#ifdef USERDEBUG_BUILD
+            else if (mPrune.naughty(e)) { // BlackListed
+                it = mLogElements.erase(it);
+                stats.subtract(e->getMsgLen(), id, uid, e->getPid());
+                delete e;
+                pruneRows--;
+                if (pruneRows == 0) {
+                    break;
+                }
+            }
+#endif
+            else {
+                ++it;
+            }
+        }
+
+        if (!kick
+#ifdef USERDEBUG_BUILD
+                || !mPrune.worstUidEnabled()
+#endif
+        ) {
+            break; // the following loop will ask bad clients to skip/drop
+        }
+    }
+
+#ifdef USERDEBUG_BUILD
+    bool whitelist = false;
+#endif
+    it = mLogElements.begin();
+    while((pruneRows > 0) && (it != mLogElements.end())) {
+        LogBufferElement *e = *it;
+        if (e->getLogId() == id) {
+            if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
+#ifdef USERDEBUG_BUILD
+                if (!whitelist)
+#endif
+                {
+                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+                        // kick a misbehaving log reader client off the island
+                        oldest->release_Locked();
+                    } else {
+                        oldest->triggerSkip_Locked(pruneRows);
+                    }
+                }
+                break;
+            }
+#ifdef USERDEBUG_BUILD
+            if (mPrune.nice(e)) { // WhiteListed
+                whitelist = true;
+                it++;
+                continue;
+            }
+#endif
+            it = mLogElements.erase(it);
+            stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
+            delete e;
+            pruneRows--;
+        } else {
+            it++;
+        }
+    }
+
+#ifdef USERDEBUG_BUILD
+    if (whitelist && (pruneRows > 0)) {
+        it = mLogElements.begin();
+        while((it != mLogElements.end()) && (pruneRows > 0)) {
+            LogBufferElement *e = *it;
+            if (e->getLogId() == id) {
+                if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
+                    if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+                        // kick a misbehaving log reader client off the island
+                        oldest->release_Locked();
+                    } else {
+                        oldest->triggerSkip_Locked(pruneRows);
+                    }
+                    break;
+                }
+                it = mLogElements.erase(it);
+                stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
+                delete e;
+                pruneRows--;
+            } else {
+                it++;
+            }
+        }
+    }
+#endif
+
+    LogTimeEntry::unlock();
+}
+
+// clear all rows of type "id" from the buffer.
+void LogBuffer::clear(log_id_t id) {
+    pthread_mutex_lock(&mLogElementsLock);
+    prune(id, ULONG_MAX);
+    pthread_mutex_unlock(&mLogElementsLock);
+}
+
+// get the used space associated with "id".
+unsigned long LogBuffer::getSizeUsed(log_id_t id) {
+    pthread_mutex_lock(&mLogElementsLock);
+    size_t retval = stats.sizes(id);
+    pthread_mutex_unlock(&mLogElementsLock);
+    return retval;
+}
+
+#ifdef USERDEBUG_BUILD
+
+// set the total space allocated to "id"
+int LogBuffer::setSize(log_id_t id, unsigned long size) {
+    // Reasonable limits ...
+    if ((size < (64 * 1024)) || ((256 * 1024 * 1024) < size)) {
+        return -1;
+    }
+    pthread_mutex_lock(&mLogElementsLock);
+    log_buffer_size(id) = size;
+    pthread_mutex_unlock(&mLogElementsLock);
+    return 0;
+}
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t id) {
+    pthread_mutex_lock(&mLogElementsLock);
+    size_t retval = log_buffer_size(id);
+    pthread_mutex_unlock(&mLogElementsLock);
+    return retval;
+}
+
+#else // ! USERDEBUG_BUILD
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t /*id*/) {
+    return log_buffer_size(id);
+}
+
+#endif
+
+log_time LogBuffer::flushTo(
+        SocketClient *reader, const log_time start, bool privileged,
+        bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
+    LogBufferElementCollection::iterator it;
+    log_time max = start;
+    uid_t uid = reader->getUid();
+
+    pthread_mutex_lock(&mLogElementsLock);
+    for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
+        LogBufferElement *element = *it;
+
+        if (!privileged && (element->getUid() != uid)) {
+            continue;
+        }
+
+        if (element->getMonotonicTime() <= start) {
+            continue;
+        }
+
+        // NB: calling out to another object with mLogElementsLock held (safe)
+        if (filter && !(*filter)(element, arg)) {
+            continue;
+        }
+
+        pthread_mutex_unlock(&mLogElementsLock);
+
+        // range locking in LastLogTimes looks after us
+        max = element->flushTo(reader);
+
+        if (max == element->FLUSH_ERROR) {
+            return max;
+        }
+
+        pthread_mutex_lock(&mLogElementsLock);
+    }
+    pthread_mutex_unlock(&mLogElementsLock);
+
+    return max;
+}
+
+void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+    log_time oldest(CLOCK_MONOTONIC);
+
+    pthread_mutex_lock(&mLogElementsLock);
+
+    // Find oldest element in the log(s)
+    LogBufferElementCollection::iterator it;
+    for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
+        LogBufferElement *element = *it;
+
+        if ((logMask & (1 << element->getLogId()))) {
+            oldest = element->getMonotonicTime();
+            break;
+        }
+    }
+
+    stats.format(strp, uid, logMask, oldest);
+
+    pthread_mutex_unlock(&mLogElementsLock);
+}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
new file mode 100644
index 0000000..0745e56
--- /dev/null
+++ b/logd/LogBuffer.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012-2014 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.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_H__
+#define _LOGD_LOG_BUFFER_H__
+
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <sysutils/SocketClient.h>
+#include <utils/List.h>
+
+#include "LogBufferElement.h"
+#include "LogTimes.h"
+#include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
+
+typedef android::List<LogBufferElement *> LogBufferElementCollection;
+
+class LogBuffer {
+    LogBufferElementCollection mLogElements;
+    pthread_mutex_t mLogElementsLock;
+
+    LogStatistics stats;
+
+#ifdef USERDEBUG_BUILD
+    PruneList mPrune;
+
+    unsigned long mMaxSize[LOG_ID_MAX];
+#endif
+
+public:
+    LastLogTimes &mTimes;
+
+    LogBuffer(LastLogTimes *times);
+
+    void log(log_id_t log_id, log_time realtime,
+             uid_t uid, pid_t pid, const char *msg, unsigned short len);
+    log_time flushTo(SocketClient *writer, const log_time start,
+                     bool privileged,
+                     bool (*filter)(const LogBufferElement *element, void *arg) = NULL,
+                     void *arg = NULL);
+
+    void clear(log_id_t id);
+    unsigned long getSize(log_id_t id);
+#ifdef USERDEBUG_BUILD
+    int setSize(log_id_t id, unsigned long size);
+#endif
+    unsigned long getSizeUsed(log_id_t id);
+    // *strp uses malloc, use free to release.
+    void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+
+#ifdef USERDEBUG_BUILD
+    int initPrune(char *cp) { return mPrune.init(cp); }
+    // *strp uses malloc, use free to release.
+    void formatPrune(char **strp) { mPrune.format(strp); }
+#endif
+
+private:
+    void maybePrune(log_id_t id);
+    void prune(log_id_t id, unsigned long pruneRows);
+
+};
+
+#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
new file mode 100644
index 0000000..01cc9de
--- /dev/null
+++ b/logd/LogBufferElement.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012-2014 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 <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+
+#include "LogBufferElement.h"
+#include "LogReader.h"
+
+const log_time LogBufferElement::FLUSH_ERROR(0, 0);
+
+LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
+                                   uid_t uid, pid_t pid, const char *msg,
+                                   unsigned short len)
+        : mLogId(log_id)
+        , mUid(uid)
+        , mPid(pid)
+        , mMsgLen(len)
+        , mMonotonicTime(CLOCK_MONOTONIC)
+        , mRealTime(realtime) {
+    mMsg = new char[len];
+    memcpy(mMsg, msg, len);
+}
+
+LogBufferElement::~LogBufferElement() {
+    delete [] mMsg;
+}
+
+log_time LogBufferElement::flushTo(SocketClient *reader) {
+    struct logger_entry_v3 entry;
+    memset(&entry, 0, sizeof(struct logger_entry_v3));
+    entry.hdr_size = sizeof(struct logger_entry_v3);
+    entry.len = mMsgLen;
+    entry.lid = mLogId;
+    entry.pid = mPid;
+    entry.sec = mRealTime.tv_sec;
+    entry.nsec = mRealTime.tv_nsec;
+
+    struct iovec iovec[2];
+    iovec[0].iov_base = &entry;
+    iovec[0].iov_len = sizeof(struct logger_entry_v3);
+    iovec[1].iov_base = mMsg;
+    iovec[1].iov_len = mMsgLen;
+    if (reader->sendDatav(iovec, 2)) {
+        return FLUSH_ERROR;
+    }
+
+    return mMonotonicTime;
+}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
new file mode 100644
index 0000000..1da09ae
--- /dev/null
+++ b/logd/LogBufferElement.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012-2014 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.
+ */
+
+#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
+#define _LOGD_LOG_BUFFER_ELEMENT_H__
+
+#include <sys/types.h>
+#include <sysutils/SocketClient.h>
+#include <log/log.h>
+#include <log/log_read.h>
+
+class LogBufferElement {
+    const log_id_t mLogId;
+    const uid_t mUid;
+    const pid_t mPid;
+    char *mMsg;
+    const unsigned short mMsgLen;
+    const log_time mMonotonicTime;
+    const log_time mRealTime;
+
+public:
+    LogBufferElement(log_id_t log_id, log_time realtime,
+                     uid_t uid, pid_t pid, const char *msg, unsigned short len);
+    virtual ~LogBufferElement();
+
+    log_id_t getLogId() const { return mLogId; }
+    uid_t getUid(void) const { return mUid; }
+    pid_t getPid(void) const { return mPid; }
+    unsigned short getMsgLen() const { return mMsgLen; }
+    log_time getMonotonicTime(void) const { return mMonotonicTime; }
+    log_time getRealTime(void) const { return mRealTime; }
+
+    static const log_time FLUSH_ERROR;
+    log_time flushTo(SocketClient *writer);
+};
+
+#endif
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
new file mode 100644
index 0000000..0873e63
--- /dev/null
+++ b/logd/LogCommand.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012-2014 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "LogCommand.h"
+
+LogCommand::LogCommand(const char *cmd)
+        : FrameworkCommand(cmd) {
+}
+
+// gets a list of supplementary group IDs associated with
+// the socket peer.  This is implemented by opening
+// /proc/PID/status and look for the "Group:" line.
+//
+// This function introduces races especially since status
+// can change 'shape' while reading, the net result is err
+// on lack of permission.
+//
+// Race-free alternative is to introduce pairs of sockets
+// and threads for each command and reading, one each that
+// has open permissions, and one that has restricted
+// permissions.
+
+static bool groupIsLog(char *buf) {
+    char *ptr;
+    static const char ws[] = " \n";
+    bool ret = false;
+
+    for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
+        errno = 0;
+        gid_t Gid = strtol(buf, NULL, 10);
+        if (errno != 0) {
+            return false;
+        }
+        if (Gid == AID_LOG) {
+            ret = true;
+        }
+    }
+    return ret;
+}
+
+bool clientHasLogCredentials(SocketClient * cli) {
+    uid_t uid = cli->getUid();
+    if (uid == AID_ROOT) {
+        return true;
+    }
+
+    gid_t gid = cli->getGid();
+    if ((gid == AID_ROOT) || (gid == AID_LOG)) {
+        return true;
+    }
+
+    // FYI We will typically be here for 'adb logcat'
+    bool ret = false;
+
+    char filename[1024];
+    snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
+
+    FILE *file = fopen(filename, "r");
+    if (!file) {
+        return ret;
+    }
+
+    bool foundGid = false;
+    bool foundUid = false;
+
+    char line[1024];
+    while (fgets(line, sizeof(line), file)) {
+        static const char groups_string[] = "Groups:\t";
+        static const char uid_string[] = "Uid:\t";
+        static const char gid_string[] = "Gid:\t";
+
+        if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
+            ret = groupIsLog(line + strlen(groups_string));
+            if (!ret) {
+                break;
+            }
+        } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
+            uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+            sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
+                   &u[0], &u[1], &u[2], &u[3]);
+
+            // Protect against PID reuse by checking that the UID is the same
+            if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
+                ret = false;
+                break;
+            }
+            foundUid = true;
+        } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
+            gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+            sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
+                   &g[0], &g[1], &g[2], &g[3]);
+
+            // Protect against PID reuse by checking that the GID is the same
+            if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
+                ret = false;
+                break;
+            }
+            foundGid = true;
+        }
+    }
+
+    fclose(file);
+
+    if (!foundGid || !foundUid) {
+        ret = false;
+    }
+
+    return ret;
+}
diff --git a/logd/LogCommand.h b/logd/LogCommand.h
new file mode 100644
index 0000000..e3b96a2
--- /dev/null
+++ b/logd/LogCommand.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012-2014 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.
+ */
+
+#ifndef _LOGD_COMMAND_H
+#define _LOGD_COMMAND_H
+
+#include <sysutils/SocketClient.h>
+#include <sysutils/FrameworkCommand.h>
+
+class LogCommand : public FrameworkCommand {
+public:
+    LogCommand(const char *cmd);
+    virtual ~LogCommand() {}
+};
+
+bool clientHasLogCredentials(SocketClient * cli);
+
+#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
new file mode 100644
index 0000000..c6b248b
--- /dev/null
+++ b/logd/LogListener.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012-2014 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 <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <log/logger.h>
+
+#include "LogListener.h"
+
+LogListener::LogListener(LogBuffer *buf, LogReader *reader)
+        : SocketListener(getLogSocket(), false)
+        , logbuf(buf)
+        , reader(reader)
+{  }
+
+bool LogListener::onDataAvailable(SocketClient *cli) {
+    char buffer[1024];
+    struct iovec iov = { buffer, sizeof(buffer) };
+    memset(buffer, 0, sizeof(buffer));
+
+    char control[CMSG_SPACE(sizeof(struct ucred))];
+    struct msghdr hdr = {
+        NULL,
+        0,
+        &iov,
+        1,
+        control,
+        sizeof(control),
+        0,
+    };
+
+    int socket = cli->getSocket();
+
+    ssize_t n = recvmsg(socket, &hdr, 0);
+    if (n <= (ssize_t) sizeof_log_id_t) {
+        return false;
+    }
+
+    struct ucred *cred = NULL;
+
+    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
+    while (cmsg != NULL) {
+        if (cmsg->cmsg_level == SOL_SOCKET
+                && cmsg->cmsg_type  == SCM_CREDENTIALS) {
+            cred = (struct ucred *)CMSG_DATA(cmsg);
+            break;
+        }
+        cmsg = CMSG_NXTHDR(&hdr, cmsg);
+    }
+
+    if (cred == NULL) {
+        return false;
+    }
+
+    if (cred->uid == getuid()) {
+        // ignore log messages we send to ourself.
+        // Such log messages are often generated by libraries we depend on
+        // which use standard Android logging.
+        return false;
+    }
+
+    // First log element is always log_id.
+    log_id_t log_id = (log_id_t) *((typeof_log_id_t *) buffer);
+    if (log_id < 0 || log_id >= LOG_ID_MAX) {
+        return false;
+    }
+
+    char *msg = ((char *)buffer) + sizeof_log_id_t;
+    n -= sizeof_log_id_t;
+
+    log_time realtime(msg);
+    msg += sizeof(log_time);
+    n -= sizeof(log_time);
+
+    unsigned short len = n;
+    if (len == n) {
+        logbuf->log(log_id, realtime, cred->uid, cred->pid, msg, len);
+        reader->notifyNewLog();
+    }
+
+    return true;
+}
+
+int LogListener::getLogSocket() {
+    int sock = android_get_control_socket("logdw");
+    int on = 1;
+    if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
+        return -1;
+    }
+    return sock;
+}
diff --git a/logd/LogListener.h b/logd/LogListener.h
new file mode 100644
index 0000000..7099e13
--- /dev/null
+++ b/logd/LogListener.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012-2013 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.
+ */
+
+#ifndef _LOGD_LOG_LISTENER_H__
+#define _LOGD_LOG_LISTENER_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogReader.h"
+
+class LogListener : public SocketListener {
+    LogBuffer *logbuf;
+    LogReader *reader;
+
+public:
+    LogListener(LogBuffer *buf, LogReader *reader);
+
+protected:
+    virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+    static int getLogSocket();
+};
+
+#endif
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
new file mode 100644
index 0000000..5b540bf
--- /dev/null
+++ b/logd/LogReader.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012-2013 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 <poll.h>
+#include <sys/socket.h>
+#include <cutils/sockets.h>
+
+#include "LogReader.h"
+#include "FlushCommand.h"
+
+LogReader::LogReader(LogBuffer *logbuf)
+        : SocketListener("logdr", true)
+        , mLogbuf(*logbuf)
+{ }
+
+// When we are notified a new log entry is available, inform
+// all of our listening sockets.
+void LogReader::notifyNewLog() {
+    FlushCommand command(*this);
+    runOnEachSocket(&command);
+}
+
+bool LogReader::onDataAvailable(SocketClient *cli) {
+    char buffer[255];
+
+    int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
+    if (len <= 0) {
+        doSocketDelete(cli);
+        return false;
+    }
+    buffer[len] = '\0';
+
+    unsigned long tail = 0;
+    static const char _tail[] = " tail=";
+    char *cp = strstr(buffer, _tail);
+    if (cp) {
+        tail = atol(cp + sizeof(_tail) - 1);
+    }
+
+    unsigned int logMask = -1;
+    static const char _logIds[] = " lids=";
+    cp = strstr(buffer, _logIds);
+    if (cp) {
+        logMask = 0;
+        cp += sizeof(_logIds) - 1;
+        while (*cp && *cp != '\0') {
+            int val = 0;
+            while (('0' <= *cp) && (*cp <= '9')) {
+                val *= 10;
+                val += *cp - '0';
+                ++cp;
+            }
+            logMask |= 1 << val;
+            if (*cp != ',') {
+                break;
+            }
+            ++cp;
+        }
+    }
+
+    pid_t pid = 0;
+    static const char _pid[] = " pid=";
+    cp = strstr(buffer, _pid);
+    if (cp) {
+        pid = atol(cp + sizeof(_pid) - 1);
+    }
+
+    bool nonBlock = false;
+    if (strncmp(buffer, "dumpAndClose", 12) == 0) {
+        nonBlock = true;
+    }
+
+    FlushCommand command(*this, nonBlock, tail, logMask, pid);
+    command.runSocketCommand(cli);
+    return true;
+}
+
+void LogReader::doSocketDelete(SocketClient *cli) {
+    LastLogTimes &times = mLogbuf.mTimes;
+    LogTimeEntry::lock();
+    LastLogTimes::iterator it = times.begin();
+    while(it != times.end()) {
+        LogTimeEntry *entry = (*it);
+        if (entry->mClient == cli) {
+            times.erase(it);
+            entry->release_Locked();
+            break;
+        }
+        it++;
+    }
+    LogTimeEntry::unlock();
+}
diff --git a/logd/LogReader.h b/logd/LogReader.h
new file mode 100644
index 0000000..b267c75
--- /dev/null
+++ b/logd/LogReader.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012-2013 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.
+ */
+
+#ifndef _LOGD_LOG_WRITER_H__
+#define _LOGD_LOG_WRITER_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogBuffer.h"
+#include "LogTimes.h"
+
+class LogReader : public SocketListener {
+    LogBuffer &mLogbuf;
+
+public:
+    LogReader(LogBuffer *logbuf);
+    void notifyNewLog();
+
+    LogBuffer &logbuf(void) const { return mLogbuf; }
+
+protected:
+    virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+    void doSocketDelete(SocketClient *cli);
+
+};
+
+#endif
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
new file mode 100644
index 0000000..49ee50d
--- /dev/null
+++ b/logd/LogStatistics.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2014 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 <stdarg.h>
+#include <time.h>
+
+#include <log/logger.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String8.h>
+
+#include "LogStatistics.h"
+
+PidStatistics::PidStatistics(pid_t pid)
+        : pid(pid)
+        , mSizesTotal(0)
+        , mElementsTotal(0)
+        , mSizes(0)
+        , mElements(0) { }
+
+void PidStatistics::add(unsigned short size) {
+    mSizesTotal += size;
+    ++mElementsTotal;
+    mSizes += size;
+    ++mElements;
+}
+
+bool PidStatistics::subtract(unsigned short size) {
+    mSizes -= size;
+    --mElements;
+    return mElements == 0 && kill(pid, 0);
+}
+
+void PidStatistics::addTotal(size_t size, size_t element) {
+    if (pid == gone) {
+        mSizesTotal += size;
+        mElementsTotal += element;
+    }
+}
+
+UidStatistics::UidStatistics(uid_t uid)
+        : uid(uid) {
+    Pids.clear();
+}
+
+UidStatistics::~UidStatistics() {
+    PidStatisticsCollection::iterator it;
+    for (it = begin(); it != end();) {
+        delete (*it);
+        it = Pids.erase(it);
+    }
+}
+
+void UidStatistics::add(unsigned short size, pid_t pid) {
+    PidStatistics *p;
+    PidStatisticsCollection::iterator last;
+    PidStatisticsCollection::iterator it;
+    for (last = it = begin(); it != end(); last = it, ++it) {
+        p = *it;
+        if (pid == p->getPid()) {
+            p->add(size);
+            // poor-man sort, bubble upwards if bigger than last
+            if ((last != it) && ((*last)->sizesTotal() < p->sizesTotal())) {
+                Pids.erase(it);
+                Pids.insert(last, p);
+            }
+            return;
+        }
+    }
+    // poor-man sort, insert if bigger than last or last is the gone entry.
+    bool insert = (last != it)
+        && ((p->getPid() == p->gone)
+            || ((*last)->sizesTotal() < (size_t) size));
+    p = new PidStatistics(pid);
+    if (insert) {
+        Pids.insert(last, p);
+    } else {
+        Pids.push_back(p);
+    }
+    p->add(size);
+}
+
+void UidStatistics::subtract(unsigned short size, pid_t pid) {
+    PidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        PidStatistics *p = *it;
+        if (pid == p->getPid()) {
+            if (p->subtract(size)) {
+                size_t szsTotal = p->sizesTotal();
+                size_t elsTotal = p->elementsTotal();
+                delete p;
+                Pids.erase(it);
+                it = end();
+                --it;
+                if (it == end()) {
+                    p = new PidStatistics(p->gone);
+                    Pids.push_back(p);
+                } else {
+                    p = *it;
+                    if (p->getPid() != p->gone) {
+                        p = new PidStatistics(p->gone);
+                        Pids.push_back(p);
+                    }
+                }
+                p->addTotal(szsTotal, elsTotal);
+            }
+            return;
+        }
+    }
+}
+
+size_t UidStatistics::sizes(pid_t pid) {
+    size_t sizes = 0;
+    PidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        PidStatistics *p = *it;
+        if ((pid == pid_all) || (pid == p->getPid())) {
+            sizes += p->sizes();
+        }
+    }
+    return sizes;
+}
+
+size_t UidStatistics::elements(pid_t pid) {
+    size_t elements = 0;
+    PidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        PidStatistics *p = *it;
+        if ((pid == pid_all) || (pid == p->getPid())) {
+            elements += p->elements();
+        }
+    }
+    return elements;
+}
+
+size_t UidStatistics::sizesTotal(pid_t pid) {
+    size_t sizes = 0;
+    PidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        PidStatistics *p = *it;
+        if ((pid == pid_all) || (pid == p->getPid())) {
+            sizes += p->sizesTotal();
+        }
+    }
+    return sizes;
+}
+
+size_t UidStatistics::elementsTotal(pid_t pid) {
+    size_t elements = 0;
+    PidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        PidStatistics *p = *it;
+        if ((pid == pid_all) || (pid == p->getPid())) {
+            elements += p->elementsTotal();
+        }
+    }
+    return elements;
+}
+
+LidStatistics::LidStatistics() {
+    Uids.clear();
+}
+
+LidStatistics::~LidStatistics() {
+    UidStatisticsCollection::iterator it;
+    for (it = begin(); it != end();) {
+        delete (*it);
+        it = Uids.erase(it);
+    }
+}
+
+void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) {
+    UidStatistics *u;
+    UidStatisticsCollection::iterator it;
+    UidStatisticsCollection::iterator last;
+
+    if (uid == (uid_t) -1) { // init
+        uid = (uid_t) AID_ROOT;
+    }
+
+    for (last = it = begin(); it != end(); last = it, ++it) {
+        u = *it;
+        if (uid == u->getUid()) {
+            u->add(size, pid);
+            if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) {
+                Uids.erase(it);
+                Uids.insert(last, u);
+            }
+            return;
+        }
+    }
+    u = new UidStatistics(uid);
+    if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) {
+        Uids.insert(last, u);
+    } else {
+        Uids.push_back(u);
+    }
+    u->add(size, pid);
+}
+
+void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) {
+    UidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        UidStatistics *u = *it;
+        if (uid == u->getUid()) {
+            u->subtract(size, pid);
+            return;
+        }
+    }
+}
+
+size_t LidStatistics::sizes(uid_t uid, pid_t pid) {
+    size_t sizes = 0;
+    UidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        UidStatistics *u = *it;
+        if ((uid == uid_all) || (uid == u->getUid())) {
+            sizes += u->sizes(pid);
+        }
+    }
+    return sizes;
+}
+
+size_t LidStatistics::elements(uid_t uid, pid_t pid) {
+    size_t elements = 0;
+    UidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        UidStatistics *u = *it;
+        if ((uid == uid_all) || (uid == u->getUid())) {
+            elements += u->elements(pid);
+        }
+    }
+    return elements;
+}
+
+size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) {
+    size_t sizes = 0;
+    UidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        UidStatistics *u = *it;
+        if ((uid == uid_all) || (uid == u->getUid())) {
+            sizes += u->sizesTotal(pid);
+        }
+    }
+    return sizes;
+}
+
+size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) {
+    size_t elements = 0;
+    UidStatisticsCollection::iterator it;
+    for (it = begin(); it != end(); ++it) {
+        UidStatistics *u = *it;
+        if ((uid == uid_all) || (uid == u->getUid())) {
+            elements += u->elementsTotal(pid);
+        }
+    }
+    return elements;
+}
+
+LogStatistics::LogStatistics()
+        : start(CLOCK_MONOTONIC) {
+    log_id_for_each(i) {
+        mSizes[i] = 0;
+        mElements[i] = 0;
+    }
+}
+
+void LogStatistics::add(unsigned short size,
+                        log_id_t log_id, uid_t uid, pid_t pid) {
+    mSizes[log_id] += size;
+    ++mElements[log_id];
+    id(log_id).add(size, uid, pid);
+}
+
+void LogStatistics::subtract(unsigned short size,
+                             log_id_t log_id, uid_t uid, pid_t pid) {
+    mSizes[log_id] -= size;
+    --mElements[log_id];
+    id(log_id).subtract(size, uid, pid);
+}
+
+size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) {
+    if (log_id != log_id_all) {
+        return id(log_id).sizes(uid, pid);
+    }
+    size_t sizes = 0;
+    log_id_for_each(i) {
+        sizes += id(i).sizes(uid, pid);
+    }
+    return sizes;
+}
+
+size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) {
+    if (log_id != log_id_all) {
+        return id(log_id).elements(uid, pid);
+    }
+    size_t elements = 0;
+    log_id_for_each(i) {
+        elements += id(i).elements(uid, pid);
+    }
+    return elements;
+}
+
+size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) {
+    if (log_id != log_id_all) {
+        return id(log_id).sizesTotal(uid, pid);
+    }
+    size_t sizes = 0;
+    log_id_for_each(i) {
+        sizes += id(i).sizesTotal(uid, pid);
+    }
+    return sizes;
+}
+
+size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) {
+    if (log_id != log_id_all) {
+        return id(log_id).elementsTotal(uid, pid);
+    }
+    size_t elements = 0;
+    log_id_for_each(i) {
+        elements += id(i).elementsTotal(uid, pid);
+    }
+    return elements;
+}
+
+void LogStatistics::format(char **buf,
+                           uid_t uid, unsigned int logMask, log_time oldest) {
+    const unsigned short spaces_current = 13;
+    const unsigned short spaces_total = 19;
+
+    if (*buf) {
+        free(buf);
+        *buf = NULL;
+    }
+
+    android::String8 string("        span -> size/num");
+    size_t oldLength;
+    short spaces = 2;
+
+    log_id_for_each(i) {
+        if (logMask & (1 << i)) {
+            oldLength = string.length();
+            string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
+            spaces += spaces_total + oldLength - string.length();
+        }
+    }
+
+    spaces = 1;
+    log_time t(CLOCK_MONOTONIC);
+    unsigned long long d = t.nsec() - start.nsec();
+    string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu",
+                  d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
+                  (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
+
+    log_id_for_each(i) {
+        if (!(logMask & (1 << i))) {
+            continue;
+        }
+        oldLength = string.length();
+        string.appendFormat("%*s%zu/%zu", spaces, "",
+                            sizesTotal(i), elementsTotal(i));
+        spaces += spaces_total + oldLength - string.length();
+    }
+
+    spaces = 1;
+    d = t.nsec() - oldest.nsec();
+    string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu",
+                  d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
+                  (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
+
+    log_id_for_each(i) {
+        if (!(logMask & (1 << i))) {
+            continue;
+        }
+
+        size_t els = elements(i);
+        if (els) {
+            oldLength = string.length();
+            string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els);
+            spaces -= string.length() - oldLength;
+        }
+        spaces += spaces_total;
+    }
+
+    log_id_for_each(i) {
+        if (!(logMask & (1 << i))) {
+            continue;
+        }
+
+        bool header = false;
+        bool first = true;
+
+        UidStatisticsCollection::iterator ut;
+        for(ut = id(i).begin(); ut != id(i).end(); ++ut) {
+            UidStatistics *up = *ut;
+            if ((uid != AID_ROOT) && (uid != up->getUid())) {
+                continue;
+            }
+
+            PidStatisticsCollection::iterator pt = up->begin();
+            if (pt == up->end()) {
+                continue;
+            }
+
+            android::String8 intermediate;
+
+            if (!header) {
+                // header below tuned to match spaces_total and spaces_current
+                spaces = 0;
+                intermediate = string.format("%s: UID/PID Total size/num",
+                                             android_log_id_to_name(i));
+                string.appendFormat("\n\n%-31sNow          "
+                                         "UID/PID[?]  Total              Now",
+                                    intermediate.string());
+                intermediate.clear();
+                header = true;
+            }
+
+            bool oneline = ++pt == up->end();
+            --pt;
+
+            if (!oneline) {
+                first = true;
+            } else if (!first && spaces) {
+                string.appendFormat("%*s", spaces, "");
+            }
+            spaces = 0;
+
+            uid_t u = up->getUid();
+            pid_t p = (*pt)->getPid();
+
+            intermediate = string.format(oneline
+                                             ? ((p == PidStatistics::gone)
+                                                 ? "%d/?"
+                                                 : "%d/%d")
+                                             : "%d",
+                                         u, p);
+            string.appendFormat((first) ? "\n%-12s" : "%-12s",
+                                intermediate.string());
+            intermediate.clear();
+
+            size_t elsTotal = up->elementsTotal();
+            oldLength = string.length();
+            string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal);
+            spaces += spaces_total + oldLength - string.length();
+
+            size_t els = up->elements();
+            if (els == elsTotal) {
+                string.appendFormat("%*s=", spaces, "");
+                spaces = -1;
+            } else if (els) {
+                oldLength = string.length();
+                string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
+                spaces -= string.length() - oldLength;
+            }
+            spaces += spaces_current;
+
+            first = !first;
+
+            if (oneline) {
+                continue;
+            }
+
+            size_t gone_szs = 0;
+            size_t gone_els = 0;
+
+            for(; pt != up->end(); ++pt) {
+                PidStatistics *pp = *pt;
+                pid_t p = pp->getPid();
+
+                // If a PID no longer has any current logs, and is not
+                // active anymore, skip & report totals for gone.
+                elsTotal = pp->elementsTotal();
+                size_t szsTotal = pp->sizesTotal();
+                if (p == pp->gone) {
+                    gone_szs += szsTotal;
+                    gone_els += elsTotal;
+                    continue;
+                }
+                els = pp->elements();
+                bool gone = kill(p, 0);
+                if (gone && (els == 0)) {
+                    // ToDo: garbage collection: move this statistical bucket
+                    //       from its current UID/PID to UID/? (races and
+                    //       wrap around are our achilles heel). Below is
+                    //       merely lipservice to catch PIDs that were still
+                    //       around when the stats were pruned to zero.
+                    gone_szs += szsTotal;
+                    gone_els += elsTotal;
+                    continue;
+                }
+
+                if (!first && spaces) {
+                    string.appendFormat("%*s", spaces, "");
+                }
+                spaces = 0;
+
+                intermediate = string.format((gone) ? "%d/%d?" : "%d/%d", u, p);
+                string.appendFormat((first) ? "\n%-12s" : "%-12s",
+                                    intermediate.string());
+                intermediate.clear();
+
+                oldLength = string.length();
+                string.appendFormat("%zu/%zu", szsTotal, elsTotal);
+                spaces += spaces_total + oldLength - string.length();
+
+                if (els == elsTotal) {
+                    string.appendFormat("%*s=", spaces, "");
+                    spaces = -1;
+                } else if (els) {
+                    oldLength = string.length();
+                    string.appendFormat("%*s%zu/%zu", spaces, "",
+                                        pp->sizes(), els);
+                    spaces -= string.length() - oldLength;
+                }
+                spaces += spaces_current;
+
+                first = !first;
+            }
+
+            if (gone_els) {
+                if (!first && spaces) {
+                    string.appendFormat("%*s", spaces, "");
+                }
+
+                intermediate = string.format("%d/?", u);
+                string.appendFormat((first) ? "\n%-12s" : "%-12s",
+                                    intermediate.string());
+                intermediate.clear();
+
+                spaces = spaces_total + spaces_current;
+
+                oldLength = string.length();
+                string.appendFormat("%zu/%zu", gone_szs, gone_els);
+                spaces -= string.length() - oldLength;
+
+                first = !first;
+            }
+        }
+    }
+
+    *buf = strdup(string.string());
+}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
new file mode 100644
index 0000000..d44afa2
--- /dev/null
+++ b/logd/LogStatistics.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _LOGD_LOG_STATISTICS_H__
+#define _LOGD_LOG_STATISTICS_H__
+
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <log/log_read.h>
+#include <utils/List.h>
+
+#define log_id_for_each(i) \
+    for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
+
+class PidStatistics {
+    const pid_t pid;
+
+    // Total
+    size_t mSizesTotal;
+    size_t mElementsTotal;
+    // Current
+    size_t mSizes;
+    size_t mElements;
+
+public:
+    static const pid_t gone = (pid_t) -1;
+
+    PidStatistics(pid_t pid);
+
+    pid_t getPid() const { return pid; }
+
+    void add(unsigned short size);
+    bool subtract(unsigned short size); // returns true if stats and PID gone
+    void addTotal(size_t size, size_t element);
+
+    size_t sizes() const { return mSizes; }
+    size_t elements() const { return mElements; }
+
+    size_t sizesTotal() const { return mSizesTotal; }
+    size_t elementsTotal() const { return mElementsTotal; }
+};
+
+typedef android::List<PidStatistics *> PidStatisticsCollection;
+
+class UidStatistics {
+    const uid_t uid;
+
+    PidStatisticsCollection Pids;
+
+public:
+    UidStatistics(uid_t uid);
+    ~UidStatistics();
+
+    PidStatisticsCollection::iterator begin() { return Pids.begin(); }
+    PidStatisticsCollection::iterator end() { return Pids.end(); }
+
+    uid_t getUid() { return uid; }
+
+    void add(unsigned short size, pid_t pid);
+    void subtract(unsigned short size, pid_t pid);
+
+    static const pid_t pid_all = (pid_t) -1;
+
+    size_t sizes(pid_t pid = pid_all);
+    size_t elements(pid_t pid = pid_all);
+
+    size_t sizesTotal(pid_t pid = pid_all);
+    size_t elementsTotal(pid_t pid = pid_all);
+};
+
+typedef android::List<UidStatistics *> UidStatisticsCollection;
+
+class LidStatistics {
+    UidStatisticsCollection Uids;
+
+public:
+    LidStatistics();
+    ~LidStatistics();
+
+    UidStatisticsCollection::iterator begin() { return Uids.begin(); }
+    UidStatisticsCollection::iterator end() { return Uids.end(); }
+
+    void add(unsigned short size, uid_t uid, pid_t pid);
+    void subtract(unsigned short size, uid_t uid, pid_t pid);
+
+    static const pid_t pid_all = (pid_t) -1;
+    static const uid_t uid_all = (uid_t) -1;
+
+    size_t sizes(uid_t uid = uid_all, pid_t pid = pid_all);
+    size_t elements(uid_t uid = uid_all, pid_t pid = pid_all);
+
+    size_t sizesTotal(uid_t uid = uid_all, pid_t pid = pid_all);
+    size_t elementsTotal(uid_t uid = uid_all, pid_t pid = pid_all);
+};
+
+// Log Statistics
+class LogStatistics {
+    LidStatistics LogIds[LOG_ID_MAX];
+
+    size_t mSizes[LOG_ID_MAX];
+    size_t mElements[LOG_ID_MAX];
+
+public:
+    const log_time start;
+
+    LogStatistics();
+
+    LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; }
+
+    void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
+    void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
+
+    // fast track current value by id only
+    size_t sizes(log_id_t id) const { return mSizes[id]; }
+    size_t elements(log_id_t id) const { return mElements[id]; }
+
+    // statistical track
+    static const log_id_t log_id_all = (log_id_t) -1;
+    static const uid_t uid_all = (uid_t) -1;
+    static const pid_t pid_all = (pid_t) -1;
+
+    size_t sizes(log_id_t id, uid_t uid, pid_t pid = pid_all);
+    size_t elements(log_id_t id, uid_t uid, pid_t pid = pid_all);
+    size_t sizes() { return sizes(log_id_all, uid_all); }
+    size_t elements() { return elements(log_id_all, uid_all); }
+
+    size_t sizesTotal(log_id_t id = log_id_all,
+                      uid_t uid = uid_all,
+                      pid_t pid = pid_all);
+    size_t elementsTotal(log_id_t id = log_id_all,
+                         uid_t uid = uid_all,
+                         pid_t pid = pid_all);
+
+    // *strp = malloc, balance with free
+    void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+};
+
+#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
new file mode 100644
index 0000000..67cc65e
--- /dev/null
+++ b/logd/LogTimes.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014 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 "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogTimes.h"
+#include "LogReader.h"
+
+pthread_mutex_t LogTimeEntry::timesLock = PTHREAD_MUTEX_INITIALIZER;
+
+const struct timespec LogTimeEntry::EPOCH = { 0, 1 };
+
+LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
+                           bool nonBlock, unsigned long tail,
+                           unsigned int logMask, pid_t pid)
+        : mRefCount(1)
+        , mRelease(false)
+        , mError(false)
+        , threadRunning(false)
+        , threadTriggered(true)
+        , mReader(reader)
+        , mLogMask(logMask)
+        , mPid(pid)
+        , skipAhead(0)
+        , mCount(0)
+        , mTail(tail)
+        , mIndex(0)
+        , mClient(client)
+        , mStart(EPOCH)
+        , mNonBlock(nonBlock)
+        , mEnd(CLOCK_MONOTONIC)
+{ }
+
+void LogTimeEntry::startReader_Locked(void) {
+    threadRunning = true;
+    if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) {
+        threadRunning = false;
+        if (mClient) {
+            mClient->decRef();
+        }
+        decRef_Locked();
+    }
+}
+
+void LogTimeEntry::threadStop(void *obj) {
+    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+    lock();
+
+    me->threadRunning = false;
+    if (me->mNonBlock) {
+        me->error_Locked();
+    }
+
+    SocketClient *client = me->mClient;
+
+    if (me->isError_Locked()) {
+        LogReader &reader = me->mReader;
+        LastLogTimes &times = reader.logbuf().mTimes;
+
+        LastLogTimes::iterator it = times.begin();
+        while(it != times.end()) {
+            if (*it == me) {
+                times.erase(it);
+                me->release_Locked();
+                break;
+            }
+            it++;
+        }
+
+        me->mClient = NULL;
+        reader.release(client);
+    }
+
+    if (client) {
+        client->decRef();
+    }
+
+    me->decRef_Locked();
+
+    unlock();
+}
+
+void *LogTimeEntry::threadStart(void *obj) {
+    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+    pthread_cleanup_push(threadStop, obj);
+
+    SocketClient *client = me->mClient;
+    if (!client) {
+        me->error();
+        pthread_exit(NULL);
+    }
+
+    LogBuffer &logbuf = me->mReader.logbuf();
+
+    bool privileged = FlushCommand::hasReadLogs(client);
+
+    lock();
+
+    me->threadTriggered = true;
+
+    while(me->threadTriggered && !me->isError_Locked()) {
+
+        me->threadTriggered = false;
+
+        log_time start = me->mStart;
+
+        unlock();
+
+        if (me->mTail) {
+            logbuf.flushTo(client, start, privileged, FilterFirstPass, me);
+        }
+        start = logbuf.flushTo(client, start, privileged, FilterSecondPass, me);
+
+        if (start == LogBufferElement::FLUSH_ERROR) {
+            me->error();
+        }
+
+        if (me->mNonBlock) {
+            lock();
+            break;
+        }
+
+        sched_yield();
+
+        lock();
+    }
+
+    unlock();
+
+    pthread_exit(NULL);
+
+    pthread_cleanup_pop(true);
+
+    return NULL;
+}
+
+// A first pass to count the number of elements
+bool LogTimeEntry::FilterFirstPass(const LogBufferElement *element, void *obj) {
+    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+    LogTimeEntry::lock();
+
+    if (me->mCount == 0) {
+        me->mStart = element->getMonotonicTime();
+    }
+
+    if ((!me->mPid || (me->mPid == element->getPid()))
+            && (me->mLogMask & (1 << element->getLogId()))) {
+        ++me->mCount;
+    }
+
+    LogTimeEntry::unlock();
+
+    return false;
+}
+
+// A second pass to send the selected elements
+bool LogTimeEntry::FilterSecondPass(const LogBufferElement *element, void *obj) {
+    LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
+
+    LogTimeEntry::lock();
+
+    if (me->skipAhead) {
+        me->skipAhead--;
+    }
+
+    me->mStart = element->getMonotonicTime();
+
+    // Truncate to close race between first and second pass
+    if (me->mNonBlock && me->mTail && (me->mIndex >= me->mCount)) {
+        goto skip;
+    }
+
+    if ((me->mLogMask & (1 << element->getLogId())) == 0) {
+        goto skip;
+    }
+
+    if (me->mPid && (me->mPid != element->getPid())) {
+        goto skip;
+    }
+
+    if (me->isError_Locked()) {
+        goto skip;
+    }
+
+    if (!me->mTail) {
+        goto ok;
+    }
+
+    ++me->mIndex;
+
+    if ((me->mCount > me->mTail) && (me->mIndex <= (me->mCount - me->mTail))) {
+        goto skip;
+    }
+
+    if (!me->mNonBlock) {
+        me->mTail = 0;
+    }
+
+ok:
+    if (!me->skipAhead) {
+        LogTimeEntry::unlock();
+        return true;
+    }
+    // FALLTHRU
+
+skip:
+    LogTimeEntry::unlock();
+    return false;
+}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
new file mode 100644
index 0000000..cb6f566
--- /dev/null
+++ b/logd/LogTimes.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012-2013 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.
+ */
+
+#ifndef _LOGD_LOG_TIMES_H__
+#define _LOGD_LOG_TIMES_H__
+
+#include <pthread.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sysutils/SocketClient.h>
+#include <utils/List.h>
+
+class LogReader;
+
+class LogTimeEntry {
+    static pthread_mutex_t timesLock;
+    unsigned int mRefCount;
+    bool mRelease;
+    bool mError;
+    bool threadRunning;
+    bool threadTriggered;
+    pthread_t mThread;
+    LogReader &mReader;
+    static void *threadStart(void *me);
+    static void threadStop(void *me);
+    const unsigned int mLogMask;
+    const pid_t mPid;
+    unsigned int skipAhead;
+    unsigned long mCount;
+    unsigned long mTail;
+    unsigned long mIndex;
+
+public:
+    LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
+                 unsigned long tail, unsigned int logMask, pid_t pid);
+
+    SocketClient *mClient;
+    static const struct timespec EPOCH;
+    log_time mStart;
+    const bool mNonBlock;
+    const log_time mEnd; // only relevant if mNonBlock
+
+    // Protect List manipulations
+    static void lock(void) { pthread_mutex_lock(&timesLock); }
+    static void unlock(void) { pthread_mutex_unlock(&timesLock); }
+
+    void startReader_Locked(void);
+
+    bool runningReader_Locked(void) const {
+        return threadRunning || mRelease || mError || mNonBlock;
+    }
+    void triggerReader_Locked(void) { threadTriggered = true; }
+    void triggerSkip_Locked(unsigned int skip) { skipAhead = skip; }
+
+    // Called after LogTimeEntry removed from list, lock implicitly held
+    void release_Locked(void) {
+        mRelease = true;
+        if (mRefCount || threadRunning) {
+            return;
+        }
+        // No one else is holding a reference to this
+        delete this;
+    }
+
+    // Called to mark socket in jeopardy
+    void error_Locked(void) { mError = true; }
+    void error(void) { lock(); mError = true; unlock(); }
+
+    bool isError_Locked(void) const { return mRelease || mError; }
+
+    // Mark Used
+    //  Locking implied, grabbed when protection around loop iteration
+    void incRef_Locked(void) { ++mRefCount; }
+
+    bool owned_Locked(void) const { return mRefCount != 0; }
+
+    void decRef_Locked(void) {
+        if ((mRefCount && --mRefCount) || !mRelease || threadRunning) {
+            return;
+        }
+        // No one else is holding a reference to this
+        delete this;
+    }
+
+    // flushTo filter callbacks
+    static bool FilterFirstPass(const LogBufferElement *element, void *me);
+    static bool FilterSecondPass(const LogBufferElement *element, void *me);
+};
+
+typedef android::List<LogTimeEntry *> LastLogTimes;
+
+#endif
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
new file mode 100644
index 0000000..d0ceb9f
--- /dev/null
+++ b/logd/LogWhiteBlackList.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifdef USERDEBUG_BUILD
+
+#include <ctype.h>
+
+#include <utils/String8.h>
+
+#include "LogWhiteBlackList.h"
+
+// White and Black list
+
+Prune::Prune(uid_t uid, pid_t pid)
+        : mUid(uid)
+        , mPid(pid)
+{ }
+
+int Prune::cmp(uid_t uid, pid_t pid) const {
+    if ((mUid == uid_all) || (mUid == uid)) {
+        if (mPid == pid_all) {
+            return 0;
+        }
+        return pid - mPid;
+    }
+    return uid - mUid;
+}
+
+void Prune::format(char **strp) {
+    if (mUid != uid_all) {
+        asprintf(strp, (mPid != pid_all) ? "%u/%u" : "%u", mUid, mPid);
+    } else {
+        // NB: mPid == pid_all can not happen if mUid == uid_all
+        asprintf(strp, (mPid != pid_all) ? "/%u" : "/", mPid);
+    }
+}
+
+PruneList::PruneList()
+        : mWorstUidEnabled(true) {
+    mNaughty.clear();
+    mNice.clear();
+}
+
+PruneList::~PruneList() {
+    PruneCollection::iterator it;
+    for (it = mNice.begin(); it != mNice.end();) {
+        delete (*it);
+        it = mNice.erase(it);
+    }
+    for (it = mNaughty.begin(); it != mNaughty.end();) {
+        delete (*it);
+        it = mNaughty.erase(it);
+    }
+}
+
+int PruneList::init(char *str) {
+    mWorstUidEnabled = true;
+    PruneCollection::iterator it;
+    for (it = mNice.begin(); it != mNice.end();) {
+        delete (*it);
+        it = mNice.erase(it);
+    }
+    for (it = mNaughty.begin(); it != mNaughty.end();) {
+        delete (*it);
+        it = mNaughty.erase(it);
+    }
+
+    if (!str) {
+        return 0;
+    }
+
+    mWorstUidEnabled = false;
+
+    for(; *str; ++str) {
+        if (isspace(*str)) {
+            continue;
+        }
+
+        PruneCollection *list;
+        if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
+            ++str;
+            // special case, translates to worst UID at priority in blacklist
+            if (*str == '!') {
+                mWorstUidEnabled = true;
+                ++str;
+                if (!*str) {
+                    break;
+                }
+                if (!isspace(*str)) {
+                    return 1;
+                }
+                continue;
+            }
+            if (!*str) {
+                return 1;
+            }
+            list = &mNaughty;
+        } else {
+            list = &mNice;
+        }
+
+        uid_t uid = Prune::uid_all;
+        if (isdigit(*str)) {
+            uid = 0;
+            do {
+                uid = uid * 10 + *str++ - '0';
+            } while (isdigit(*str));
+        }
+
+        pid_t pid = Prune::pid_all;
+        if (*str == '/') {
+            ++str;
+            if (isdigit(*str)) {
+                pid = 0;
+                do {
+                    pid = pid * 10 + *str++ - '0';
+                } while (isdigit(*str));
+            }
+        }
+
+        if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
+            return 1;
+        }
+
+        if (*str && !isspace(*str)) {
+            return 1;
+        }
+
+        // insert sequentially into list
+        PruneCollection::iterator it = list->begin();
+        while (it != list->end()) {
+            Prune *p = *it;
+            int m = uid - p->mUid;
+            if (m == 0) {
+                if (p->mPid == p->pid_all) {
+                    break;
+                }
+                if ((pid == p->pid_all) && (p->mPid != p->pid_all)) {
+                    it = list->erase(it);
+                    continue;
+                }
+                m = pid - p->mPid;
+            }
+            if (m >= 0) {
+                if (m > 0) {
+                    list->insert(it, new Prune(uid,pid));
+                }
+                break;
+            }
+            ++it;
+        }
+        if (it == list->end()) {
+            list->push_back(new Prune(uid,pid));
+        }
+        if (!*str) {
+            break;
+        }
+    }
+
+    return 0;
+}
+
+void PruneList::format(char **strp) {
+    if (*strp) {
+        free(*strp);
+        *strp = NULL;
+    }
+
+    static const char nice_format[] = " %s";
+    const char *fmt = nice_format + 1;
+
+    android::String8 string;
+
+    if (mWorstUidEnabled) {
+        string.setTo("~!");
+        fmt = nice_format;
+    }
+
+    PruneCollection::iterator it;
+
+    for (it = mNice.begin(); it != mNice.end(); ++it) {
+        char *a = NULL;
+        (*it)->format(&a);
+
+        string.appendFormat(fmt, a);
+        fmt = nice_format;
+
+        free(a);
+    }
+
+    static const char naughty_format[] = " ~%s";
+    fmt = naughty_format + (*fmt != ' ');
+    for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+        char *a = NULL;
+        (*it)->format(&a);
+
+        string.appendFormat(fmt, a);
+        fmt = naughty_format;
+
+        free(a);
+    }
+
+    *strp = strdup(string.string());
+}
+
+// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
+// If there is scaling issues, resort to a better algorithm than linear
+// based on these assumptions.
+
+bool PruneList::naughty(LogBufferElement *element) {
+    PruneCollection::iterator it;
+    for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+        if (!(*it)->cmp(element)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool PruneList::nice(LogBufferElement *element) {
+    PruneCollection::iterator it;
+    for (it = mNice.begin(); it != mNice.end(); ++it) {
+        if (!(*it)->cmp(element)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+#endif // USERDEBUG_BUILD
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
new file mode 100644
index 0000000..769d651
--- /dev/null
+++ b/logd/LogWhiteBlackList.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef _LOGD_LOG_WHITE_BLACK_LIST_H__
+#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+#include <LogBufferElement.h>
+
+// White and Blacklist
+
+class Prune {
+    friend class PruneList;
+
+    const uid_t mUid;
+    const pid_t mPid;
+    int cmp(uid_t uid, pid_t pid) const;
+
+public:
+    static const uid_t uid_all = (uid_t) -1;
+    static const pid_t pid_all = (pid_t) -1;
+
+    Prune(uid_t uid, pid_t pid);
+
+    uid_t getUid() const { return mUid; }
+    pid_t getPid() const { return mPid; }
+
+    int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); }
+
+    // *strp is malloc'd, use free to release
+    void format(char **strp);
+};
+
+typedef android::List<Prune *> PruneCollection;
+
+class PruneList {
+    PruneCollection mNaughty;
+    PruneCollection mNice;
+    bool mWorstUidEnabled;
+
+public:
+    PruneList();
+    ~PruneList();
+
+    int init(char *str);
+
+    bool naughty(LogBufferElement *element);
+    bool nice(LogBufferElement *element);
+    bool worstUidEnabled() const { return mWorstUidEnabled; }
+
+    // *strp is malloc'd, use free to release
+    void format(char **strp);
+};
+
+#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__
diff --git a/logd/main.cpp b/logd/main.cpp
new file mode 100644
index 0000000..6216b95
--- /dev/null
+++ b/logd/main.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012-2013 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/prctl.h>
+
+#include "private/android_filesystem_config.h"
+#include "CommandListener.h"
+#include "LogBuffer.h"
+#include "LogListener.h"
+
+static int drop_privs() {
+    struct sched_param param;
+    memset(&param, 0, sizeof(param));
+
+    if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
+        return -1;
+    }
+
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+        return -1;
+    }
+
+    if (setgid(AID_LOGD) != 0) {
+        return -1;
+    }
+
+    if (setuid(AID_LOGD) != 0) {
+        return -1;
+    }
+
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    capheader.pid = 0;
+
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[0].inheritable = 0;
+    capdata[1].inheritable = 0;
+
+    if (capset(&capheader, &capdata[0]) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+// Foreground waits for exit of the three main persistent threads that
+// are started here.  The three threads are created to manage UNIX
+// domain client sockets for writing, reading and controlling the user
+// space logger.  Additional transitory per-client threads are created
+// for each reader once they register.
+int main() {
+    if (drop_privs() != 0) {
+        return -1;
+    }
+
+    // Serves the purpose of managing the last logs times read on a
+    // socket connection, and as a reader lock on a range of log
+    // entries.
+
+    LastLogTimes *times = new LastLogTimes();
+
+    // LogBuffer is the object which is responsible for holding all
+    // log entries.
+
+    LogBuffer *logBuf = new LogBuffer(times);
+
+    // LogReader listens on /dev/socket/logdr. When a client
+    // connects, log entries in the LogBuffer are written to the client.
+
+    LogReader *reader = new LogReader(logBuf);
+    if (reader->startListener()) {
+        exit(1);
+    }
+
+    // LogListener listens on /dev/socket/logdw for client
+    // initiated log messages. New log entries are added to LogBuffer
+    // and LogReader is notified to send updates to connected clients.
+
+    LogListener *swl = new LogListener(logBuf, reader);
+    // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
+    if (swl->startListener(300)) {
+        exit(1);
+    }
+
+    // Command listener listens on /dev/socket/logd for incoming logd
+    // administrative commands.
+
+    CommandListener *cl = new CommandListener(logBuf, reader, swl);
+    if (cl->startListener()) {
+        exit(1);
+    }
+
+    pause();
+    exit(0);
+}
+
diff --git a/reboot/reboot.c b/reboot/reboot.c
index d9a4227..007dfba 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -35,7 +35,7 @@
 
         c = getopt(argc, argv, "p");
 
-        if (c == EOF) {
+        if (c == -1) {
             break;
         }
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 8a1cb26..1619100 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -104,6 +104,7 @@
     write /proc/sys/kernel/dmesg_restrict 1
     write /proc/sys/vm/mmap_min_addr 32768
     write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
+    write /proc/sys/net/unix/max_dgram_qlen 300
     write /proc/sys/kernel/sched_rt_runtime_us 950000
     write /proc/sys/kernel/sched_rt_period_us 1000000
 
@@ -223,23 +224,16 @@
     mkdir /data/misc/radio 0770 system radio
     mkdir /data/misc/sms 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
-    restorecon_recursive /data/misc/zoneinfo
     mkdir /data/misc/vpn 0770 system vpn
     mkdir /data/misc/systemkeys 0700 system system
     mkdir /data/misc/wifi 0770 wifi wifi
     mkdir /data/misc/wifi/sockets 0770 wifi wifi
-    restorecon_recursive /data/misc/wifi/sockets
     mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi
     mkdir /data/misc/dhcp 0770 dhcp dhcp
     # give system access to wpa_supplicant.conf for backup and restore
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
     mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
-    restorecon_recursive /data/misc/media
-
-    # Set security context of any pre-existing /data/misc/adb/adb_keys file.
-    restorecon /data/misc/adb
-    restorecon /data/misc/adb/adb_keys
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -271,7 +265,6 @@
     # create directory for MediaDrm plug-ins - give drm the read/write access to
     # the following directory.
     mkdir /data/mediadrm 0770 mediadrm mediadrm
-    restorecon_recursive /data/mediadrm
 
     # symlink to bugreport storage location
     symlink /data/data/com.android.shell/files/bugreports /data/bugreports
@@ -282,6 +275,9 @@
     # Reload policy from /data/security if present.
     setprop selinux.reload_policy 1
 
+    # Set SELinux security contexts on upgrade or policy update.
+    restorecon_recursive /data
+
     # If there is no fs-post-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.
@@ -470,6 +466,12 @@
     critical
     socket lmkd seqpacket 0660 system system
 
+service logd /system/bin/logd
+    class main
+    socket logd stream 0666 logd logd
+    socket logdr seqpacket 0666 logd logd
+    socket logdw dgram 0222 logd logd
+
 service servicemanager /system/bin/servicemanager
     class core
     user system
@@ -495,6 +497,9 @@
 service debuggerd /system/bin/debuggerd
     class main
 
+service debuggerd64 /system/bin/debuggerd64
+    class main
+
 service ril-daemon /system/bin/rild
     class main
     socket rild stream 660 root radio
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 05fbfba..6a9c2eb 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -200,7 +200,7 @@
 }
 
 static int int_hash(void *key) {
-    return (int) key;
+    return (int) (uintptr_t) key;
 }
 
 static bool int_equals(void *keyA, void *keyB) {
@@ -232,7 +232,7 @@
      * buffer at the same time.  This allows us to share the underlying storage. */
     union {
         __u8 request_buffer[MAX_REQUEST_SIZE];
-        __u8 read_buffer[MAX_READ];
+        __u8 read_buffer[MAX_READ + PAGESIZE];
     };
 };
 
@@ -487,7 +487,7 @@
         break;
     case PERM_ANDROID_DATA:
     case PERM_ANDROID_OBB:
-        appid = (appid_t) hashmapGet(fuse->package_to_appid, node->name);
+        appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name);
         if (appid != 0) {
             node->uid = multiuser_get_uid(parent->userid, appid);
         }
@@ -511,7 +511,7 @@
     }
 
     appid_t appid = multiuser_get_app_id(hdr->uid);
-    return hashmapContainsKey(fuse->appid_with_rw, (void*) appid);
+    return hashmapContainsKey(fuse->appid_with_rw, (void*) (uintptr_t) appid);
 }
 
 /* Kernel has already enforced everything we returned through
@@ -1218,6 +1218,7 @@
     __u32 size = req->size;
     __u64 offset = req->offset;
     int res;
+    __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGESIZE) & ~((uintptr_t)PAGESIZE-1));
 
     /* Don't access any other fields of hdr or req beyond this point, the read buffer
      * overlaps the request buffer and will clobber data in the request.  This
@@ -1225,14 +1226,14 @@
 
     TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token,
             h, h->fd, size, offset);
-    if (size > sizeof(handler->read_buffer)) {
+    if (size > MAX_READ) {
         return -EINVAL;
     }
-    res = pread64(h->fd, handler->read_buffer, size, offset);
+    res = pread64(h->fd, read_buffer, size, offset);
     if (res < 0) {
         return -errno;
     }
-    fuse_reply(fuse, unique, handler->read_buffer, res);
+    fuse_reply(fuse, unique, read_buffer, res);
     return NO_STATUS;
 }
 
@@ -1243,6 +1244,12 @@
     struct fuse_write_out out;
     struct handle *h = id_to_ptr(req->fh);
     int res;
+    __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGESIZE)));
+    
+    if (req->flags & O_DIRECT) {
+        memcpy(aligned_buffer, buffer, req->size);
+        buffer = (const __u8*) aligned_buffer;
+    }
 
     TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token,
             h, h->fd, req->size, req->offset);
@@ -1496,7 +1503,8 @@
         return handle_release(fuse, handler, hdr, req);
     }
 
-    case FUSE_FSYNC: {
+    case FUSE_FSYNC:
+    case FUSE_FSYNCDIR: {
         const struct fuse_fsync_in *req = data;
         return handle_fsync(fuse, handler, hdr, req);
     }
@@ -1524,7 +1532,6 @@
         return handle_releasedir(fuse, handler, hdr, req);
     }
 
-//    case FUSE_FSYNCDIR:
     case FUSE_INIT: { /* init_in -> init_out */
         const struct fuse_init_in *req = data;
         return handle_init(fuse, handler, hdr, req);
@@ -1621,12 +1628,12 @@
 
         if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
             char* package_name_dup = strdup(package_name);
-            hashmapPut(fuse->package_to_appid, package_name_dup, (void*) appid);
+            hashmapPut(fuse->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
 
             char* token = strtok(gids, ",");
             while (token != NULL) {
                 if (strtoul(token, NULL, 10) == fuse->write_gid) {
-                    hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1);
+                    hashmapPut(fuse->appid_with_rw, (void*) (uintptr_t) appid, (void*) (uintptr_t) 1);
                     break;
                 }
                 token = strtok(NULL, ",");
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 4fff9f5..3deb3e7 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -80,15 +80,23 @@
 	grep
 
 LOCAL_SRC_FILES := \
+	cp/cp.c \
+	cp/utils.c \
 	dynarray.c \
-	toolbox.c \
+	grep/fastgrep.c \
+	grep/file.c \
+	grep/grep.c \
+	grep/queue.c \
+	grep/util.c \
 	$(patsubst %,%.c,$(TOOLS)) \
-	cp/cp.c cp/utils.c \
-	grep/grep.c grep/fastgrep.c grep/file.c grep/queue.c grep/util.c
+	toolbox.c \
+	uid_from_user.c \
 
 LOCAL_C_INCLUDES := bionic/libc/bionic
 
-LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += \
+    -Wno-unused-parameter \
+    -include bsd-compatibility.h \
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
diff --git a/toolbox/bsd-compatibility.h b/toolbox/bsd-compatibility.h
new file mode 100644
index 0000000..a304631
--- /dev/null
+++ b/toolbox/bsd-compatibility.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+/* We want chown to support user.group as well as user:group. */
+#define SUPPORT_DOT
+
+__BEGIN_DECLS
+
+extern int uid_from_user(const char* name, uid_t* uid);
+
+__END_DECLS
diff --git a/toolbox/chown.c b/toolbox/chown.c
index 92efee6..6ac2233 100644
--- a/toolbox/chown.c
+++ b/toolbox/chown.c
@@ -1,73 +1,302 @@
+/*	$NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $	*/
+
+/*
+ * Copyright (c) 1988, 1993, 1994, 2003
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994, 2003\
+ The Regents of the University of California.  All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 4/4/94";
+#else
+__RCSID("$NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-#include <pwd.h>
-#include <grp.h>
-
 #include <unistd.h>
-#include <time.h>
+#include <getopt.h>
 
-int chown_main(int argc, char **argv)
+static void	a_gid(const char *);
+static void	a_uid(const char *);
+static id_t	id(const char *, const char *);
+__dead static void	usage(void);
+
+static uid_t uid;
+static gid_t gid;
+static int ischown;
+static const char *myname;
+
+struct option chown_longopts[] = {
+	{ "reference",		required_argument,	0,
+						1 },
+	{ NULL,			0,			0,
+						0 },
+};
+
+int
+chown_main(int argc, char **argv)
 {
-    int i;
+	FTS *ftsp;
+	FTSENT *p;
+	int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, vflag;
+	char *cp, *reference;
+	int (*change_owner)(const char *, uid_t, gid_t);
 
-    if (argc < 3) {
-        fprintf(stderr, "Usage: chown <USER>[:GROUP] <FILE1> [FILE2] ...\n");
-        return 10;
-    }
+	setprogname(*argv);
 
-    // Copy argv[1] to 'user' so we can truncate it at the period
-    // if a group id specified.
-    char user[32];
-    char *group = NULL;
-    strncpy(user, argv[1], sizeof(user));
-    if ((group = strchr(user, ':')) != NULL) {
-        *group++ = '\0';
-    } else if ((group = strchr(user, '.')) != NULL) {
-        *group++ = '\0';
-    }
+	(void)setlocale(LC_ALL, "");
 
-    // Lookup uid (and gid if specified)
-    struct passwd *pw;
-    struct group *grp = NULL;
-    uid_t uid;
-    gid_t gid = -1; // passing -1 to chown preserves current group
+	myname = getprogname();
+	ischown = (myname[2] == 'o');
+	reference = NULL;
 
-    pw = getpwnam(user);
-    if (pw != NULL) {
-        uid = pw->pw_uid;
-    } else {
-        char* endptr;
-        uid = (int) strtoul(user, &endptr, 0);
-        if (endptr == user) {  // no conversion
-          fprintf(stderr, "No such user '%s'\n", user);
-          return 10;
-        }
-    }
+	Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
+	while ((ch = getopt_long(argc, argv, "HLPRfhv",
+	    chown_longopts, NULL)) != -1)
+		switch (ch) {
+		case 1:
+			reference = optarg;
+			break;
+		case 'H':
+			Hflag = 1;
+			Lflag = 0;
+			break;
+		case 'L':
+			Lflag = 1;
+			Hflag = 0;
+			break;
+		case 'P':
+			Hflag = Lflag = 0;
+			break;
+		case 'R':
+			Rflag = 1;
+			break;
+		case 'f':
+			fflag = 1;
+			break;
+		case 'h':
+			/*
+			 * In System V the -h option causes chown/chgrp to
+			 * change the owner/group of the symbolic link.
+			 * 4.4BSD's symbolic links didn't have owners/groups,
+			 * so it was an undocumented noop.
+			 * In NetBSD 1.3, lchown(2) is introduced.
+			 */
+			hflag = 1;
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	argv += optind;
+	argc -= optind;
 
-    if (group != NULL) {
-        grp = getgrnam(group);
-        if (grp != NULL) {
-            gid = grp->gr_gid;
-        } else {
-            char* endptr;
-            gid = (int) strtoul(group, &endptr, 0);
-            if (endptr == group) {  // no conversion
-                fprintf(stderr, "No such group '%s'\n", group);
-                return 10;
-            }
-        }
-    }
+	if (argc == 0 || (argc == 1 && reference == NULL))
+		usage();
 
-    for (i = 2; i < argc; i++) {
-        if (chown(argv[i], uid, gid) < 0) {
-            fprintf(stderr, "Unable to chown %s: %s\n", argv[i], strerror(errno));
-            return 10;
-        }
-    }
+	fts_options = FTS_PHYSICAL;
+	if (Rflag) {
+		if (Hflag)
+			fts_options |= FTS_COMFOLLOW;
+		if (Lflag) {
+			if (hflag)
+				errx(EXIT_FAILURE,
+				    "the -L and -h options "
+				    "may not be specified together.");
+			fts_options &= ~FTS_PHYSICAL;
+			fts_options |= FTS_LOGICAL;
+		}
+	} else if (!hflag)
+		fts_options |= FTS_COMFOLLOW;
 
-    return 0;
+	uid = (uid_t)-1;
+	gid = (gid_t)-1;
+	if (reference == NULL) {
+		if (ischown) {
+			if ((cp = strchr(*argv, ':')) != NULL) {
+				*cp++ = '\0';
+				a_gid(cp);
+			}
+#ifdef SUPPORT_DOT
+			else if ((cp = strrchr(*argv, '.')) != NULL) {
+				if (uid_from_user(*argv, &uid) == -1) {
+					*cp++ = '\0';
+					a_gid(cp);
+				}
+			}
+#endif
+			a_uid(*argv);
+		} else
+			a_gid(*argv);
+		argv++;
+	} else {
+		struct stat st;
+
+		if (stat(reference, &st) == -1)
+			err(EXIT_FAILURE, "Cannot stat `%s'", reference);
+		if (ischown)
+			uid = st.st_uid;
+		gid = st.st_gid;
+	}
+
+	if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
+		err(EXIT_FAILURE, "fts_open");
+
+	for (rval = EXIT_SUCCESS; (p = fts_read(ftsp)) != NULL;) {
+		change_owner = chown;
+		switch (p->fts_info) {
+		case FTS_D:
+			if (!Rflag)		/* Change it at FTS_DP. */
+				fts_set(ftsp, p, FTS_SKIP);
+			continue;
+		case FTS_DNR:			/* Warn, chown, continue. */
+			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+			rval = EXIT_FAILURE;
+			break;
+		case FTS_ERR:			/* Warn, continue. */
+		case FTS_NS:
+			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+			rval = EXIT_FAILURE;
+			continue;
+		case FTS_SL:			/* Ignore unless -h. */
+			/*
+			 * All symlinks we found while doing a physical
+			 * walk end up here.
+			 */
+			if (!hflag)
+				continue;
+			/*
+			 * Note that if we follow a symlink, fts_info is
+			 * not FTS_SL but FTS_F or whatever.  And we should
+			 * use lchown only for FTS_SL and should use chown
+			 * for others.
+			 */
+			change_owner = lchown;
+			break;
+		case FTS_SLNONE:		/* Ignore. */
+			/*
+			 * The only symlinks that end up here are ones that
+			 * don't point to anything.  Note that if we are
+			 * doing a phisycal walk, we never reach here unless
+			 * we asked to follow explicitly.
+			 */
+			continue;
+		default:
+			break;
+		}
+
+		if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) {
+			warn("%s", p->fts_path);
+			rval = EXIT_FAILURE;
+		} else {
+			if (vflag)
+				printf("%s\n", p->fts_path);
+		}
+	}
+	if (errno)
+		err(EXIT_FAILURE, "fts_read");
+	exit(rval);
+	/* NOTREACHED */
+}
+
+static void
+a_gid(const char *s)
+{
+	struct group *gr;
+
+	if (*s == '\0')			/* Argument was "uid[:.]". */
+		return;
+	gr = *s == '#' ? NULL : getgrnam(s);
+	if (gr == NULL)
+		gid = id(s, "group");
+	else
+		gid = gr->gr_gid;
+	return;
+}
+
+static void
+a_uid(const char *s)
+{
+	if (*s == '\0')			/* Argument was "[:.]gid". */
+		return;
+	if (*s == '#' || uid_from_user(s, &uid) == -1) {
+		uid = id(s, "user");
+	}
+	return;
+}
+
+static id_t
+id(const char *name, const char *type)
+{
+	id_t val;
+	char *ep;
+
+	errno = 0;
+	if (*name == '#')
+		name++;
+	val = (id_t)strtoul(name, &ep, 10);
+	if (errno)
+		err(EXIT_FAILURE, "%s", name);
+	if (*ep != '\0')
+		errx(EXIT_FAILURE, "%s: invalid %s name", name, type);
+	return (val);
+}
+
+static void
+usage(void)
+{
+
+	(void)fprintf(stderr,
+	    "Usage: %s [-R [-H | -L | -P]] [-fhv] %s file ...\n"
+	    "\t%s [-R [-H | -L | -P]] [-fhv] --reference=rfile file ...\n",
+	    myname, ischown ? "owner:group|owner|:group" : "group",
+	    myname);
+	exit(EXIT_FAILURE);
 }
diff --git a/toolbox/grep/grep.c b/toolbox/grep/grep.c
index b5bb2ef..5a4fa0c 100644
--- a/toolbox/grep/grep.c
+++ b/toolbox/grep/grep.c
@@ -294,10 +294,8 @@
 		err(2, "%s", fn);
 	line = NULL;
 	len = 0;
-#ifndef ANDROID
 	while ((rlen = getline(&line, &len, f)) != -1)
 		add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
-#endif
 	free(line);
 	if (ferror(f))
 		err(2, "%s", fn);
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
index 113c120..af321af 100644
--- a/toolbox/lsof.c
+++ b/toolbox/lsof.c
@@ -113,7 +113,7 @@
     if (!maps)
         goto out;
 
-    while (fscanf(maps, "%*x-%*x %*s %zx %5s %ld %s\n", &offset, device, &inode,
+    while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode,
             file) == 4) {
         // We don't care about non-file maps
         if (inode == 0 || !strcmp(device, "00:00"))
diff --git a/toolbox/mkswap.c b/toolbox/mkswap.c
index 1710ef6..0904152 100644
--- a/toolbox/mkswap.c
+++ b/toolbox/mkswap.c
@@ -1,6 +1,5 @@
 #include <stdio.h>
 #include <unistd.h>
-#include <asm/page.h>
 #include <sys/swap.h>
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/toolbox/nandread.c b/toolbox/nandread.c
index d43b2fe..971c232 100644
--- a/toolbox/nandread.c
+++ b/toolbox/nandread.c
@@ -177,11 +177,7 @@
 
     if (rawmode) {
         rawmode = mtdinfo.oobsize;
-#if !defined(MTD_STUPID_LOCK) /* using uapi kernel headers */
         ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW);
-#else /* still using old kernel headers */
-        ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW);
-#endif
         if (ret) {
             fprintf(stderr, "failed set raw mode for %s, %s\n",
                     devname, strerror(errno));
diff --git a/toolbox/restorecon.c b/toolbox/restorecon.c
index f9f604f..3568625 100644
--- a/toolbox/restorecon.c
+++ b/toolbox/restorecon.c
@@ -2,76 +2,44 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fts.h>
 #include <selinux/selinux.h>
-#include <selinux/label.h>
 #include <selinux/android.h>
 
-static struct selabel_handle *sehandle;
 static const char *progname;
-static int nochange;
-static int verbose;
 
 static void usage(void)
 {
-    fprintf(stderr, "usage:  %s [-nrRv] pathname...\n", progname);
+    fprintf(stderr, "usage:  %s [-DFnrRv] pathname...\n", progname);
     exit(1);
 }
 
-static int restore(const char *pathname, const struct stat *sb)
-{
-    char *oldcontext, *newcontext;
-
-    if (lgetfilecon(pathname, &oldcontext) < 0) {
-        fprintf(stderr, "Could not get context of %s:  %s\n",
-                pathname, strerror(errno));
-        return -1;
-    }
-    if (selabel_lookup(sehandle, &newcontext, pathname, sb->st_mode) < 0) {
-        fprintf(stderr, "Could not lookup context for %s:  %s\n", pathname,
-                strerror(errno));
-        return -1;
-    }
-    if (strcmp(newcontext, "<<none>>") &&
-        strcmp(oldcontext, newcontext)) {
-        if (verbose)
-            printf("Relabeling %s from %s to %s.\n", pathname, oldcontext, newcontext);
-        if (!nochange) {
-            if (lsetfilecon(pathname, newcontext) < 0) {
-                fprintf(stderr, "Could not label %s with %s:  %s\n",
-                        pathname, newcontext, strerror(errno));
-                return -1;
-            }
-        }
-    }
-    freecon(oldcontext);
-    freecon(newcontext);
-    return 0;
-}
-
 int restorecon_main(int argc, char **argv)
 {
-    int ch, recurse = 0, ftsflags = FTS_PHYSICAL;
-    int i = 0;
+    int ch, i, rc;
+    unsigned int flags = 0;
 
     progname = argv[0];
 
     do {
-        ch = getopt(argc, argv, "nrRv");
+        ch = getopt(argc, argv, "DFnrRv");
         if (ch == EOF)
             break;
         switch (ch) {
+        case 'D':
+            flags |= SELINUX_ANDROID_RESTORECON_DATADATA;
+            break;
+        case 'F':
+            flags |= SELINUX_ANDROID_RESTORECON_FORCE;
+            break;
         case 'n':
-            nochange = 1;
+            flags |= SELINUX_ANDROID_RESTORECON_NOCHANGE;
             break;
         case 'r':
         case 'R':
-            recurse = 1;
+            flags |= SELINUX_ANDROID_RESTORECON_RECURSE;
             break;
         case 'v':
-            verbose = 1;
+            flags |= SELINUX_ANDROID_RESTORECON_VERBOSE;
             break;
         default:
             usage();
@@ -83,53 +51,11 @@
     if (!argc)
         usage();
 
-    sehandle = selinux_android_file_context_handle();
-
-    if (!sehandle) {
-        fprintf(stderr, "Could not load file_contexts:  %s\n",
-                strerror(errno));
-        return -1;
-    }
-
-    if (recurse) {
-        FTS *fts;
-        FTSENT *ftsent;
-        fts = fts_open(argv, ftsflags, NULL);
-        if (!fts) {
-            fprintf(stderr, "Could not traverse filesystems (first was %s):  %s\n",
-                    argv[0], strerror(errno));
-            return -1;
-        }
-        while ((ftsent = fts_read(fts))) {
-            switch (ftsent->fts_info) {
-            case FTS_DP:
-                break;
-            case FTS_DNR:
-            case FTS_ERR:
-            case FTS_NS:
-                fprintf(stderr, "Could not access %s:  %s\n", ftsent->fts_path,
-                        strerror(errno));
-                fts_set(fts, ftsent, FTS_SKIP);
-                break;
-            default:
-                if (restore(ftsent->fts_path, ftsent->fts_statp) < 0)
-                    fts_set(fts, ftsent, FTS_SKIP);
-                break;
-            }
-        }
-    } else {
-        int i, rc;
-        struct stat sb;
-
-        for (i = 0; i < argc; i++) {
-            rc = lstat(argv[i], &sb);
-            if (rc < 0) {
-                fprintf(stderr, "Could not stat %s:  %s\n", argv[i],
-                        strerror(errno));
-                continue;
-            }
-            restore(argv[i], &sb);
-        }
+    for (i = 0; i < argc; i++) {
+        rc = selinux_android_restorecon(argv[i], flags);
+        if (rc < 0)
+            fprintf(stderr, "Could not restorecon %s:  %s\n", argv[i],
+                    strerror(errno));
     }
 
     return 0;
diff --git a/toolbox/swapoff.c b/toolbox/swapoff.c
index 8f14158..d8f6a00 100644
--- a/toolbox/swapoff.c
+++ b/toolbox/swapoff.c
@@ -1,6 +1,5 @@
 #include <stdio.h>
 #include <unistd.h>
-#include <asm/page.h>
 #include <sys/swap.h>
 
 int swapoff_main(int argc, char **argv)
diff --git a/toolbox/swapon.c b/toolbox/swapon.c
index a810b3d..21d2287 100644
--- a/toolbox/swapon.c
+++ b/toolbox/swapon.c
@@ -2,7 +2,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <getopt.h>
-#include <asm/page.h>
 #include <sys/swap.h>
 
 void usage(char *name)
diff --git a/toolbox/uid_from_user.c b/toolbox/uid_from_user.c
new file mode 100644
index 0000000..fd48d3c
--- /dev/null
+++ b/toolbox/uid_from_user.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <pwd.h>
+#include <sys/types.h>
+
+int uid_from_user(const char* name, uid_t* uid) {
+  struct passwd* pw = getpwnam(name);
+  if (pw == NULL) {
+    return -1;
+  }
+  *uid = pw->pw_uid;
+  return 0;
+}