Merge "Fix adb forward --list when forwarding a lot"
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 4c2f247..dcda005 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -116,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);
+            }
         }
     }
 
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..8810615 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;
     }
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/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/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 33af0a3..2e56756 100755
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -47,7 +47,7 @@
 	libgccdemangle \
 
 # To enable using libunwind on each arch, add it to this list.
-libunwind_architectures := arm arm64 x86 x86_64
+libunwind_architectures := arm arm64 mips x86 x86_64
 
 ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
 libbacktrace_src_files += \
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/liblog/Android.mk b/liblog/Android.mk
index 0d6c970..174ca55 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -16,7 +16,11 @@
 LOCAL_PATH := $(my-dir)
 include $(CLEAR_VARS)
 
+ifeq ($(TARGET_USES_LOGD),true)
 liblog_sources := logd_write.c
+else
+liblog_sources := logd_write_kern.c
+endif
 
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
@@ -42,7 +46,11 @@
 endif
 
 liblog_host_sources := $(liblog_sources) fake_log_device.c
+ifeq ($(TARGET_USES_LOGD),true)
 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
 # ========================================================
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 47aa711..889442d 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,73 +252,81 @@
         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);
-        return ret;
+    ret = write(sock, buf, strlen(buf) + 1);
+    if (ret <= 0) {
+        goto done;
     }
 
-    f = open(n, mode);
-    free(n);
-    if (f < 0) {
-        return f;
+    ret = read(sock, buf, buf_size);
+
+done:
+    if ((ret == -1) && errno) {
+        ret = -errno;
     }
-
-    ret = ioctl(f, cmd);
-    close (f);
-
+    close(sock);
     return ret;
 }
 
 int android_logger_clear(struct logger *logger)
 {
-    return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY);
+    char buf[512];
+
+    ssize_t ret = send_log_msg(logger, "clear %d", buf, sizeof(buf));
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (strncmp(buf, "success", 7)) {
+        return -1;
+    }
+
+    return 0;
 }
 
 /* returns the total size of the log's ring buffer */
 int 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 atoi(buf);
 }
 
 /*
@@ -234,16 +335,26 @@
  */
 int 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 atoi(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;
 }
 
 struct logger_list *android_logger_list_alloc(int mode,
@@ -256,10 +367,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 +403,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 +433,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 +580,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..b454729
--- /dev/null
+++ b/liblog/log_read_kern.c
@@ -0,0 +1,695 @@
+/*
+** 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))
+
+/* 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 */
+int android_logger_get_log_size(struct logger *logger)
+{
+    return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR);
+}
+
+/*
+ * 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)
+{
+    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;
+}
+
+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/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 d71d97a..ffb7fd1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -171,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/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index c24384c..484cf50 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -34,7 +34,7 @@
 	col32cb16blend.S \
 	t32cb16blend.S \
 
-ifeq ($(ARCH_ARM_HAVE_NEON),armv7-a)
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
 PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
 endif
 
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index cd78713..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.");
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/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 8ef0962..01f9249 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -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/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..744c9d3
--- /dev/null
+++ b/logd/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= logd
+
+LOCAL_SRC_FILES := \
+    main.cpp \
+    LogCommand.cpp \
+    CommandListener.cpp \
+    LogListener.cpp \
+    LogReader.cpp \
+    FlushCommand.cpp \
+    LogBuffer.cpp \
+    LogBufferElement.cpp \
+    LogTimes.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libsysutils \
+    liblog \
+    libcutils
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
new file mode 100644
index 0000000..202d542
--- /dev/null
+++ b/logd/CommandListener.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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));
+    registerCmd(new GetBufSizeUsedCmd(buf));
+}
+
+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;
+}
+
+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;
+}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
new file mode 100644
index 0000000..861abbf
--- /dev/null
+++ b/logd/CommandListener.h
@@ -0,0 +1,59 @@
+/*
+ * 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)
+    LogBufferCmd(GetBufSizeUsed)
+};
+
+#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..c5760f7
--- /dev/null
+++ b/logd/LogBuffer.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 "LogReader.h"
+
+#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+
+LogBuffer::LogBuffer(LastLogTimes *times)
+        : mTimes(*times) {
+    int i;
+    for (i = 0; i < LOG_ID_MAX; i++) {
+        mSizes[i] = 0;
+        mElements[i] = 0;
+    }
+    pthread_mutex_init(&mLogElementsLock, NULL);
+}
+
+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();
+    }
+
+    mSizes[log_id] += len;
+    mElements[log_id]++;
+    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) {
+    unsigned long sizes = mSizes[id];
+    if (sizes > LOG_BUFFER_SIZE) {
+        unsigned long sizeOver90Percent = sizes - ((LOG_BUFFER_SIZE * 9) / 10);
+        unsigned long elements = mElements[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 = mLogElements.begin();
+    while((pruneRows > 0) && (it != mLogElements.end())) {
+        LogBufferElement *e = *it;
+        if (e->getLogId() == id) {
+            if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
+                if (mSizes[id] > (2 * LOG_BUFFER_SIZE)) {
+                    // kick a misbehaving log reader client off the island
+                    oldest->release_Locked();
+                } else {
+                    oldest->triggerSkip_Locked(pruneRows);
+                }
+                break;
+            }
+            it = mLogElements.erase(it);
+            mSizes[id] -= e->getMsgLen();
+            mElements[id]--;
+            delete e;
+            pruneRows--;
+        } else {
+            it++;
+        }
+    }
+
+    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);
+    unsigned long retval = mSizes[id];
+    pthread_mutex_unlock(&mLogElementsLock);
+    return retval;
+}
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t /*id*/) {
+    return LOG_BUFFER_SIZE;
+}
+
+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;
+}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
new file mode 100644
index 0000000..1b50a8f
--- /dev/null
+++ b/logd/LogBuffer.h
@@ -0,0 +1,60 @@
+/*
+ * 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"
+
+typedef android::List<LogBufferElement *> LogBufferElementCollection;
+
+class LogBuffer {
+    LogBufferElementCollection mLogElements;
+    pthread_mutex_t mLogElementsLock;
+
+    unsigned long mSizes[LOG_ID_MAX];
+    unsigned long mElements[LOG_ID_MAX];
+
+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);
+    unsigned long getSizeUsed(log_id_t id);
+
+private:
+    void maybePrune(log_id_t id);
+    void prune(log_id_t id, unsigned long pruneRows);
+
+};
+
+#endif
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/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/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 46fb8bd..ffbc9fa 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -100,6 +100,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
 
@@ -214,23 +215,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
@@ -262,7 +256,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
@@ -273,6 +266,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.
@@ -456,6 +452,12 @@
 on property:ro.kernel.qemu=1
     start adbd
 
+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
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/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;
+}