Merge "rootdir: reduce permissions on /dev/rtc0"
diff --git a/adb/SYNC.TXT b/adb/SYNC.TXT
new file mode 100644
index 0000000..e74d217
--- /dev/null
+++ b/adb/SYNC.TXT
@@ -0,0 +1,84 @@
+This file tries to document file related requests a client can make
+to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here. See the SERVICES.TXT to learn more
+about the other requests that are possible.
+
+SYNC SERVICES:
+
+
+Requesting the sync service ("sync:") using the protocol as described in
+SERVICES.TXT sets the connection in sync mode. This mode is a binary mode that
+differ from the regular adb protocol. The connection stays in sync mode until
+explicitly terminated (see below).
+
+After the initial "sync:" command is sent the server must respond with either
+"OKAY" or "FAIL" as per usual. 
+
+In sync mode both the server and the client will frequently use eight-byte
+packets to communicate in this document called sync request and sync
+responses. The first four bytes is an id and specifies sync request is
+represented by four utf-8 characters. The last four bytes is a Little-Endian
+integer, with various uses. This number will be called "length" below. In fact
+all binary integers are Little-Endian in the sync mode. Sync mode is
+implicitly exited after each sync request, and normal adb communication
+follows as described in SERVICES.TXT.
+
+The following sync requests are accepted:
+LIST - List the files in a folder
+SEND - Send a file to device
+RECV - Retreive a file from device
+
+Not yet documented:
+STAT - Stat a file
+ULNK - Unlink (remove) a file. (Not currently supported)
+
+For all of the sync request above the must be followed by length number of
+bytes containing an utf-8 string with a remote filename.
+
+LIST:
+Lists files in the directory specified by the remote filename. The server will
+respond with zero or more directory entries or "dents".
+
+The directory entries will be returned in the following form
+1. A four-byte sync response id beeing "DENT"
+2. A four-byte integer representing file mode.
+3. A four-byte integer representing file size.
+4. A four-byte integer representing last modified time.
+5. A four-byte integer representing file name length.
+6. length number of bytes containing an utf-8 string representing the file
+   name.
+
+When an sync response "DONE" is received the listing is done.
+
+SEND:
+The remote file name is split into two parts separated by the last
+comma (","). The first part is the actual path, while the second is a decimal
+encoded file mode containing the permissions of the file on device.
+
+Note that some file types will be deleted before the copying starts, and if
+the transfer fails. Some file types will not be deleted, which allows
+  adb push disk_image /some_block_device
+to work.
+
+After this the actual file is sent in chunks. Each chucks has the following
+format.
+A sync request with id "DATA" and length equal to the chunk size. After
+follows chunk size number of bytes. This is repeated until the file is
+transfered. Each chunk must not be larger than 64k.
+
+When the file is tranfered a sync request "DONE" is sent, where length is set
+to the last modified time for the file. The server responds to this last
+request (but not to chuck requests) with an "OKAY" sync response (length can
+be ignored).
+
+
+RECV:
+Retrieves a file from device to a local file. The remote path is the path to
+the file that will be returned. Just as for the SEND sync request the file
+received is split up into chunks. The sync response id is "DATA" and length is
+the chuck size. After follows chunk size number of bytes. This is repeated
+until the file is transfered. Each chuck will not be larger than 64k.
+
+When the file is transfered a sync resopnse "DONE" is retrieved where the
+length can be ignored.
+
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index cd5885e..6fc55df 100755
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -90,6 +90,8 @@
 #define VENDOR_ID_INQ_MOBILE    0x2314
 // Intel's USB Vendor ID
 #define VENDOR_ID_INTEL         0x8087
+// Intermec's USB Vendor ID
+#define VENDOR_ID_INTERMEC      0x067e
 // IRiver's USB Vendor ID
 #define VENDOR_ID_IRIVER        0x2420
 // K-Touch's USB Vendor ID
@@ -210,6 +212,7 @@
     VENDOR_ID_HUAWEI,
     VENDOR_ID_INQ_MOBILE,
     VENDOR_ID_INTEL,
+    VENDOR_ID_INTERMEC,
     VENDOR_ID_IRIVER,
     VENDOR_ID_KOBO,
     VENDOR_ID_K_TOUCH,
diff --git a/charger/charger.c b/charger/charger.c
index 402d0e8..e3cadb1 100644
--- a/charger/charger.c
+++ b/charger/charger.c
@@ -973,7 +973,7 @@
     charger->uevent_fd = fd;
     coldboot(charger, "/sys/class/power_supply", "add");
 
-    ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
+    ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
     if (ret < 0) {
         LOGE("Cannot load battery_fail image\n");
         charger->surf_unknown = NULL;
@@ -983,7 +983,7 @@
 
     gr_surface* scale_frames;
     int scale_count;
-    ret = res_create_multi_surface("charger/battery_scale", &scale_count, &scale_frames);
+    ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
         charger->batt_anim->num_frames = 0;
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 77fcbe0..f6d8f0c 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -1,35 +1,93 @@
 LOCAL_PATH:= $(call my-dir)
 
-debuggerd_2nd_arch_var_prefix :=
-include $(LOCAL_PATH)/debuggerd.mk
-
-ifdef TARGET_2ND_ARCH
-debuggerd_2nd_arch_var_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)
-include $(LOCAL_PATH)/debuggerd.mk
-endif
-
-ifeq ($(ARCH_ARM_HAVE_VFP),true)
 include $(CLEAR_VARS)
 
-LOCAL_CFLAGS += -DWITH_VFP
+LOCAL_SRC_FILES:= \
+	backtrace.cpp \
+	debuggerd.cpp \
+	getevent.cpp \
+	tombstone.cpp \
+	utility.cpp \
+
+LOCAL_SRC_FILES_arm    := arm/machine.cpp
+LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
+LOCAL_SRC_FILES_mips   := mips/machine.cpp
+LOCAL_SRC_FILES_x86    := x86/machine.cpp
+LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
+
+LOCAL_CONLYFLAGS := -std=gnu99
+LOCAL_CPPFLAGS := -std=gnu++11
+LOCAL_CFLAGS := \
+	-Wall \
+	-Wno-array-bounds \
+	-Werror \
+	-Wno-unused-parameter \
+
+ifeq ($(ARCH_ARM_HAVE_VFP),true)
+LOCAL_CFLAGS_arm += -DWITH_VFP
+endif # ARCH_ARM_HAVE_VFP
 ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_CFLAGS += -DWITH_VFP_D32
+LOCAL_CFLAGS_arm += -DWITH_VFP_D32
 endif # ARCH_ARM_HAVE_VFP_D32
 
-LOCAL_SRC_FILES := vfp-crasher.c arm/vfp.S
+LOCAL_SHARED_LIBRARIES := \
+	libbacktrace \
+	libc \
+	libcutils \
+	liblog \
+	libselinux \
+
+include external/stlport/libstlport.mk
+
+LOCAL_MODULE := debuggerd
+LOCAL_MODULE_STEM_32 := debuggerd
+LOCAL_MODULE_STEM_64 := debuggerd64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.c
+LOCAL_SRC_FILES_arm    := arm/crashglue.S
+LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
+LOCAL_SRC_FILES_mips   := mips/crashglue.S
+LOCAL_SRC_FILES_x86    := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+
+LOCAL_MODULE := crasher
+LOCAL_MODULE_STEM_32 := crasher
+LOCAL_MODULE_STEM_64 := crasher64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+ifeq ($(ARCH_ARM_HAVE_VFP),true)
+LOCAL_MODULE_TARGET_ARCH += arm
+LOCAL_SRC_FILES_arm := arm/vfp.S
+LOCAL_CFLAGS_arm += -DWITH_VFP
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_CFLAGS_arm += -DWITH_VFP_D32
+endif # ARCH_ARM_HAVE_VFP_D32
+endif # ARCH_ARM_HAVE_VFP == true
+
+LOCAL_SRC_FILES_arm64 := arm64/vfp.S
+LOCAL_MODULE_TARGET_ARCH += arm64
+
+LOCAL_SRC_FILES := vfp-crasher.c
 LOCAL_MODULE := vfp-crasher
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-LOCAL_MODULE_TARGET_ARCH := arm
-include $(BUILD_EXECUTABLE)
-endif # ARCH_ARM_HAVE_VFP == true
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := vfp-crasher.c arm64/vfp.S
-LOCAL_MODULE := vfp-crasher64
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-LOCAL_MODULE_TARGET_ARCH := arm64
+LOCAL_MODULE_STEM_32 := vfp-crasher
+LOCAL_MODULE_STEM_64 := vfp-crasher64
+LOCAL_MULTILIB := both
+
 include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/debuggerd.mk b/debuggerd/debuggerd.mk
deleted file mode 100644
index a3982c1..0000000
--- a/debuggerd/debuggerd.mk
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	backtrace.cpp \
-	debuggerd.cpp \
-	getevent.cpp \
-	tombstone.cpp \
-	utility.cpp \
-
-LOCAL_SRC_FILES_arm    := arm/machine.cpp
-LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
-LOCAL_SRC_FILES_mips   := mips/machine.cpp
-LOCAL_SRC_FILES_x86    := x86/machine.cpp
-LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
-
-LOCAL_CONLYFLAGS := -std=gnu99
-LOCAL_CPPFLAGS := -std=gnu++11
-LOCAL_CFLAGS := \
-	-Wall \
-	-Wno-array-bounds \
-	-Werror \
-	-Wno-unused-parameter \
-
-ifeq ($(ARCH_ARM_HAVE_VFP),true)
-LOCAL_CFLAGS_arm += -DWITH_VFP
-endif # ARCH_ARM_HAVE_VFP
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_CFLAGS_arm += -DWITH_VFP_D32
-endif # ARCH_ARM_HAVE_VFP_D32
-
-LOCAL_SHARED_LIBRARIES := \
-	libbacktrace \
-	libc \
-	libcutils \
-	liblog \
-	libselinux \
-
-include external/stlport/libstlport.mk
-
-ifeq ($(TARGET_IS_64_BIT)|$(debuggerd_2nd_arch_var_prefix),true|)
-LOCAL_MODULE := debuggerd64
-LOCAL_NO_2ND_ARCH := true
-else
-LOCAL_MODULE := debuggerd
-LOCAL_32_BIT_ONLY := true
-endif
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.c
-LOCAL_SRC_FILES_arm    := arm/crashglue.S
-LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
-LOCAL_SRC_FILES_mips   := mips/crashglue.S
-LOCAL_SRC_FILES_x86    := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-
-LOCAL_2ND_ARCH_VAR_PREFIX := $(debuggerd_2nd_arch_var_prefix)
-
-ifeq ($(TARGET_IS_64_BIT)|$(debuggerd_2nd_arch_var_prefix),true|)
-LOCAL_MODULE := crasher64
-LOCAL_NO_2ND_ARCH := true
-else
-LOCAL_MODULE := crasher
-LOCAL_32_BIT_ONLY := true
-endif
-include $(BUILD_EXECUTABLE)
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk
index 0f32dbf..6aa7400 100644
--- a/fastbootd/Android.mk
+++ b/fastbootd/Android.mk
@@ -45,19 +45,17 @@
 LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT
 LOCAL_LDFLAGS := -ldl
 
-LOCAL_SHARED_LIBRARIES := \
-    libhardware \
-    libcrypto \
-    libhardware_legacy \
-    libmdnssd
-
 LOCAL_STATIC_LIBRARIES := \
-    libsparse_static \
     libc \
+    libcrypto_static \
     libcutils \
+    libmdnssd \
+    libsparse_static \
     libz
 
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_HAL_STATIC_LIBRARIES := libvendortrigger
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_EXECUTABLE)
 
@@ -84,21 +82,11 @@
 
 include $(BUILD_EXECUTABLE)
 
+# vendor trigger HAL
 include $(CLEAR_VARS)
-
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include \
-
-LOCAL_STATIC_LIBRARIES := \
-    $(EXTRA_STATIC_LIBS) \
-    libcutils
-
-LOCAL_SRC_FILES := \
-    other/vendor_trigger.c
-
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_MODULE := libvendortrigger.default
 LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
-
-
-include $(BUILD_SHARED_LIBRARY)
+LOCAL_SRC_FILES := vendor_trigger_default.c
+LOCAL_STATIC_LIBRARIES := libcutils
+include $(BUILD_STATIC_LIBRARY)
diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c
deleted file mode 100644
index 101959b..0000000
--- a/fastbootd/other/vendor_trigger.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2009-2013, Google Inc.
- * 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.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE 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 <stdlib.h>
-
-#include "vendor_trigger.h"
-#include "debug.h"
-
-unsigned int debug_level = DEBUG;
-
-static const int version = 1;
-
-int check_version(const int fastboot_version, int *libversion) {
-    *libversion = version;
-    return !(fastboot_version == version);
-}
-
-int gpt_layout(struct GPT_content *table) {
-    D(DEBUG, "message from libvendor");
-    return 0;
-}
-
-int oem_cmd(const char *arg, const char **response) {
-    D(DEBUG, "message from libvendor, oem catched request %s", arg);
-    return 0;
-}
-
-static int close_triggers(struct vendor_trigger_t *dev)
-{
-    if (dev)
-        free(dev);
-
-    return 0;
-}
-
-static int open_triggers(const struct hw_module_t *module, char const *name,
-                         struct hw_device_t **device) {
-    struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t));
-    klog_init();
-    klog_set_level(6);
-
-    memset(dev, 0, sizeof(*dev));
-    dev->common.module = (struct hw_module_t *) module;
-    dev->common.close  = (int (*)(struct hw_device_t *)) close_triggers;
-
-    dev->gpt_layout = gpt_layout;
-    dev->oem_cmd = oem_cmd;
-
-    *device = (struct hw_device_t *) dev;
-
-    return 0;
-}
-
-
-static struct hw_module_methods_t trigger_module_methods = {
-    .open = open_triggers,
-};
-
-struct hw_module_t HAL_MODULE_INFO_SYM = {
-    .tag = HARDWARE_MODULE_TAG,
-    .version_major = 1,
-    .version_minor = 0,
-    .id = TRIGGER_MODULE_ID,
-    .name = "vendor trigger library for fastbootd",
-    .author = "Google, Inc.",
-    .methods = &trigger_module_methods,
-};
-
diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c
index e63e64d..df0f895 100644
--- a/fastbootd/trigger.c
+++ b/fastbootd/trigger.c
@@ -39,52 +39,19 @@
 
 static const int version = 1;
 
-static struct vendor_trigger_t *triggers = NULL;
-
 int load_trigger() {
-    int err;
-    hw_module_t* module;
-    hw_device_t* device;
     int libversion;
 
-    err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module);
-
-    if (err == 0) {
-        err = module->methods->open(module, NULL, &device);
-
-        if (err == 0) {
-            triggers = (struct vendor_trigger_t *) device;
-        } else {
-            D(WARN, "Libvendor load error");
-            return 1;
-        }
-    }
-    else {
-        D(WARN, "Libvendor not load: %s", strerror(-err));
-        return 0;
+    if (trigger_init() != 0) {
+        D(ERR, "libvendortrigger failed to initialize");
+        return 1;
     }
 
-    if (triggers->check_version != NULL &&
-        triggers->check_version(version, &libversion)) {
-
-        triggers = NULL;
+    if (trigger_check_version(version, &libversion)) {
         D(ERR, "Library report incompability");
         return 1;
     }
+
     D(INFO, "libvendortrigger loaded");
-
     return 0;
 }
-
-int trigger_oem_cmd(const char *arg, const char **response) {
-    if (triggers != NULL && triggers->oem_cmd != NULL)
-        return triggers->oem_cmd(arg, response);
-    return 0;
-}
-
-int trigger_gpt_layout(struct GPT_content *table) {
-    if (triggers != NULL && triggers->gpt_layout != NULL)
-        return triggers->gpt_layout(table);
-    return 0;
-}
-
diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h
index 404acb4..d2d9573 100644
--- a/fastbootd/trigger.h
+++ b/fastbootd/trigger.h
@@ -37,9 +37,4 @@
 
 int load_trigger();
 
-/* same as in struct triggers */
-
-int trigger_gpt_layout(struct GPT_content *table);
-int trigger_oem_cmd(const char *arg, const char **response);
-
 #endif
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/vendor_trigger.h
similarity index 68%
rename from fastbootd/include/vendor_trigger.h
rename to fastbootd/vendor_trigger.h
index 51204fa..0c83be6 100644
--- a/fastbootd/include/vendor_trigger.h
+++ b/fastbootd/vendor_trigger.h
@@ -32,38 +32,37 @@
 #ifndef __VENDOR_TRIGGER_H_
 #define __VENDOR_TRIGGER_H_
 
-#define TRIGGER_MODULE_ID "fastbootd"
-#include <hardware/hardware.h>
-
 __BEGIN_DECLS
 
 struct GPT_entry_raw;
 struct GPT_content;
 
 /*
- * Structer with function pointers may become longer in the future
+ * Implemented in libvendortrigger to handle platform-specific behavior.
  */
 
-struct vendor_trigger_t {
-    struct hw_device_t common;
+/*
+ * trigger_init() is called once at startup time before calling any other method
+ *
+ * returns 0 on success and nonzero on error
+ */
+int trigger_init(void);
 
-    /*
-     * This function runs at the beggining and shoud never be changed
-     *
-     * version is number parameter indicating version on the fastbootd side
-     * libversion is version indicateing version of the library version
-     *
-     * returns 0 if it can cooperate with the current version and 1 in opposite
-     */
-    int (*check_version)(const int version, int *libversion);
+/*
+ * This function runs once after trigger_init completes.
+ *
+ * version is number parameter indicating version on the fastbootd side
+ * libversion is version indicateing version of the library version
+ *
+ * returns 0 if it can cooperate with the current version and 1 in opposite
+ */
+int trigger_check_version(const int version, int *libversion);
 
-
-    /*
-     * Return value -1 forbid the action from the vendor site and sets errno
-     */
-    int (* gpt_layout)(struct GPT_content *);
-    int (* oem_cmd)(const char *arg, const char **response);
-};
+/*
+ * Return value -1 forbid the action from the vendor site and sets errno
+ */
+int trigger_gpt_layout(struct GPT_content *);
+int trigger_oem_cmd(const char *arg, const char **response);
 
 __END_DECLS
 
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/vendor_trigger_default.c
similarity index 62%
copy from fastbootd/include/vendor_trigger.h
copy to fastbootd/vendor_trigger_default.c
index 51204fa..3627024 100644
--- a/fastbootd/include/vendor_trigger.h
+++ b/fastbootd/vendor_trigger_default.c
@@ -29,42 +29,30 @@
  * SUCH DAMAGE.
  */
 
-#ifndef __VENDOR_TRIGGER_H_
-#define __VENDOR_TRIGGER_H_
+#include <stdlib.h>
+#include <cutils/klog.h>
+#include <vendor_trigger.h>
 
-#define TRIGGER_MODULE_ID "fastbootd"
-#include <hardware/hardware.h>
+static const int version = 1;
 
-__BEGIN_DECLS
+int trigger_init(void) {
+    klog_init();
+    klog_set_level(7);
+    return 0;
+}
 
-struct GPT_entry_raw;
-struct GPT_content;
+int trigger_check_version(const int fastboot_version, int *libversion) {
+    KLOG_DEBUG("fastbootd", "%s: %d (%d)", __func__, fastboot_version, version);
+    *libversion = version;
+    return !(fastboot_version == version);
+}
 
-/*
- * Structer with function pointers may become longer in the future
- */
+int trigger_gpt_layout(struct GPT_content *table) {
+    KLOG_DEBUG("fastbootd", "%s: %p", __func__, table);
+    return 0;
+}
 
-struct vendor_trigger_t {
-    struct hw_device_t common;
-
-    /*
-     * This function runs at the beggining and shoud never be changed
-     *
-     * version is number parameter indicating version on the fastbootd side
-     * libversion is version indicateing version of the library version
-     *
-     * returns 0 if it can cooperate with the current version and 1 in opposite
-     */
-    int (*check_version)(const int version, int *libversion);
-
-
-    /*
-     * Return value -1 forbid the action from the vendor site and sets errno
-     */
-    int (* gpt_layout)(struct GPT_content *);
-    int (* oem_cmd)(const char *arg, const char **response);
-};
-
-__END_DECLS
-
-#endif
+int trigger_oem_cmd(const char *arg, const char **response) {
+    KLOG_DEBUG("fastbootd", "%s: %s", __func__, arg);
+    return 0;
+}
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index 1c8f107..dbd5e25 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -17,13 +17,14 @@
 #ifndef _LIBS_CUTILS_TRACE_H
 #define _LIBS_CUTILS_TRACE_H
 
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
-#include <stdint.h>
-#include <stdbool.h>
 #include <unistd.h>
-#include <cutils/compiler.h>
 
+#include <cutils/compiler.h>
 #ifdef ANDROID_SMP
 #include <cutils/atomic-inline.h>
 #else
@@ -217,8 +218,8 @@
         char buf[ATRACE_MESSAGE_LENGTH];
         size_t len;
 
-        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%d", getpid(),
-                name, cookie);
+        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32,
+                getpid(), name, cookie);
         write(atrace_marker_fd, buf, len);
     }
 }
@@ -235,8 +236,8 @@
         char buf[ATRACE_MESSAGE_LENGTH];
         size_t len;
 
-        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%d", getpid(),
-                name, cookie);
+        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32,
+                getpid(), name, cookie);
         write(atrace_marker_fd, buf, len);
     }
 }
@@ -253,7 +254,7 @@
         char buf[ATRACE_MESSAGE_LENGTH];
         size_t len;
 
-        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%d",
+        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32,
                 getpid(), name, value);
         write(atrace_marker_fd, buf, len);
     }
@@ -270,7 +271,7 @@
         char buf[ATRACE_MESSAGE_LENGTH];
         size_t len;
 
-        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%lld",
+        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64,
                 getpid(), name, value);
         write(atrace_marker_fd, buf, len);
     }
diff --git a/init/property_service.c b/init/property_service.c
index ac63377e..fe7cbb5 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -439,40 +439,73 @@
     *sz = pa_workspace.size;
 }
 
-static void load_properties(char *data, char *prefix)
-{
-    char *key, *value, *eol, *sol, *tmp;
-    size_t plen;
+static void load_properties_from_file(const char *, const char *);
 
-    if (prefix)
-        plen = strlen(prefix);
+/*
+ * Filter is used to decide which properties to load: NULL loads all keys,
+ * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
+ */
+static void load_properties(char *data, const char *filter)
+{
+    char *key, *value, *eol, *sol, *tmp, *fn;
+    size_t flen = 0;
+
+    if (filter) {
+        flen = strlen(filter);
+    }
+
     sol = data;
-    while((eol = strchr(sol, '\n'))) {
+    while ((eol = strchr(sol, '\n'))) {
         key = sol;
         *eol++ = 0;
         sol = eol;
 
-        value = strchr(key, '=');
-        if(value == 0) continue;
-        *value++ = 0;
+        while (isspace(*key)) key++;
+        if (*key == '#') continue;
 
-        while(isspace(*key)) key++;
-        if(*key == '#') continue;
-        tmp = value - 2;
-        while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
-
-        if (prefix && strncmp(key, prefix, plen))
-            continue;
-
-        while(isspace(*value)) value++;
         tmp = eol - 2;
-        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
+        while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
 
-        property_set(key, value);
+        if (!strncmp(key, "import ", 7) && flen == 0) {
+            fn = key + 7;
+            while (isspace(*fn)) fn++;
+
+            key = strchr(fn, ' ');
+            if (key) {
+                *key++ = 0;
+                while (isspace(*key)) key++;
+            }
+
+            load_properties_from_file(fn, key);
+
+        } else {
+            value = strchr(key, '=');
+            if (!value) continue;
+            *value++ = 0;
+
+            tmp = value - 2;
+            while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+
+            while (isspace(*value)) value++;
+
+            if (flen > 0) {
+                if (filter[flen - 1] == '*') {
+                    if (strncmp(key, filter, flen - 1)) continue;
+                } else {
+                    if (strcmp(key, filter)) continue;
+                }
+            }
+
+            property_set(key, value);
+        }
     }
 }
 
-static void load_properties_from_file(const char *fn, char *prefix)
+/*
+ * Filter is used to decide which properties to load: NULL loads all keys,
+ * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
+ */
+static void load_properties_from_file(const char *fn, const char *filter)
 {
     char *data;
     unsigned sz;
@@ -480,7 +513,7 @@
     data = read_file(fn, &sz);
 
     if(data != 0) {
-        load_properties(data, prefix);
+        load_properties(data, filter);
         free(data);
     }
 }
@@ -592,8 +625,10 @@
 
     load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
     load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
-    load_properties_from_file(PROP_PATH_FACTORY, "ro.");
+    load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
+
     load_override_properties();
+
     /* Read persistent properties after all default values have been loaded. */
     load_persistent_properties();
 
diff --git a/init/ueventd.c b/init/ueventd.c
index 5517448..662196d 100644
--- a/init/ueventd.c
+++ b/init/ueventd.c
@@ -71,8 +71,8 @@
     klog_init();
 #if LOG_UEVENTS
     /* Ensure we're at a logging level that will show the events */
-    if (klog_get_level() < KLOG_LEVEL_INFO) {
-        klog_set_level(KLOG_LEVEL_INFO);
+    if (klog_get_level() < KLOG_INFO_LEVEL) {
+        klog_set_level(KLOG_INFO_LEVEL);
     }
 #endif
 
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index c3efc33..9c73dad 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -77,32 +77,78 @@
     return (g_log_status == kLogAvailable);
 }
 
+/* give up, resources too limited */
 static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED,
                                size_t nr UNUSED)
 {
     return -1;
 }
 
+/* log_init_lock assumed */
+static int __write_to_log_initialize()
+{
+    int i, ret = 0;
+
+#if FAKE_LOG_DEVICE
+    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
+    if (logd_fd >= 0) {
+        i = logd_fd;
+        logd_fd = -1;
+        close(i);
+    }
+
+    i = socket(PF_UNIX, SOCK_DGRAM, 0);
+    if (i < 0) {
+        ret = -errno;
+        write_to_log = __write_to_log_null;
+    } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) {
+        ret = -errno;
+        close(i);
+        i = -1;
+        write_to_log = __write_to_log_null;
+    } else {
+        struct sockaddr_un un;
+        memset(&un, 0, sizeof(struct sockaddr_un));
+        un.sun_family = AF_UNIX;
+        strcpy(un.sun_path, "/dev/socket/logdw");
+
+        if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) {
+            ret = -errno;
+            close(i);
+            i = -1;
+        }
+    }
+    logd_fd = i;
+#endif
+
+    return ret;
+}
+
 static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
 {
-#if FAKE_LOG_DEVICE
     ssize_t ret;
+#if FAKE_LOG_DEVICE
     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;
+        return -EBADF;
     }
     do {
         ret = fakeLogWritev(log_fd, vec, nr);
-    } while (ret < 0 && errno == EINTR);
+        if (ret < 0) {
+            ret = -errno;
+        }
+    } while (ret == -EINTR);
 
     return ret;
 #else
-    if (logd_fd == -1) {
-        return -1;
-    }
     if (getuid() == AID_LOGD) {
         /*
          * ignore log messages we send to ourself.
@@ -111,11 +157,39 @@
          */
         return 0;
     }
-    struct iovec newVec[nr + 2];
+
+    if (logd_fd < 0) {
+        return -EBADF;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide
+     *      typeof_log_id_t  log_id;
+     *      u16              tid;
+     *      log_time         realtime;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+    static const unsigned header_length = 3;
+    struct iovec newVec[nr + header_length];
     typeof_log_id_t log_id_buf = log_id;
+    uint16_t tid = gettid();
 
     newVec[0].iov_base   = (unsigned char *) &log_id_buf;
     newVec[0].iov_len    = sizeof_log_id_t;
+    newVec[1].iov_base   = (unsigned char *) &tid;
+    newVec[1].iov_len    = sizeof(tid);
 
     struct timespec ts;
     clock_gettime(CLOCK_REALTIME, &ts);
@@ -123,17 +197,44 @@
     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);
+    newVec[2].iov_base   = (unsigned char *) &realtime_ts;
+    newVec[2].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;
+    for (i = header_length; i < nr + header_length; i++) {
+        newVec[i].iov_base = vec[i-header_length].iov_base;
+        newVec[i].iov_len  = vec[i-header_length].iov_len;
     }
 
-    /* The write below could be lost, but will never block. */
-    return writev(logd_fd, newVec, nr + 2);
+    /*
+     * The write below could be lost, but will never block.
+     *
+     * ENOTCONN occurs if logd dies.
+     * EAGAIN occurs if logd is overloaded.
+     */
+    ret = writev(logd_fd, newVec, nr + header_length);
+    if (ret < 0) {
+        ret = -errno;
+        if (ret == -ENOTCONN) {
+#ifdef HAVE_PTHREADS
+            pthread_mutex_lock(&log_init_lock);
+#endif
+            ret = __write_to_log_initialize();
+#ifdef HAVE_PTHREADS
+            pthread_mutex_unlock(&log_init_lock);
+#endif
+
+            if (ret < 0) {
+                return ret;
+            }
+
+            ret = writev(logd_fd, newVec, nr + header_length);
+            if (ret < 0) {
+                ret = -errno;
+            }
+        }
+    }
+    return ret;
 #endif
 }
 
@@ -161,35 +262,17 @@
 #endif
 
     if (write_to_log == __write_to_log_init) {
-        write_to_log = __write_to_log_kernel;
+        int ret;
 
-#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;
-        }
-        logd_fd = sock;
+        ret = __write_to_log_initialize();
+        if (ret < 0) {
+#ifdef HAVE_PTHREADS
+            pthread_mutex_unlock(&log_init_lock);
 #endif
+            return ret;
+        }
+
+        write_to_log = __write_to_log_kernel;
     }
 
 #ifdef HAVE_PTHREADS
@@ -219,7 +302,7 @@
         !strcmp(tag, "PHONE") ||
         !strcmp(tag, "SMS")) {
             log_id = LOG_ID_RADIO;
-            // Inform third party apps/ril/radio.. to use Rlog or RLOG
+            /* 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;
     }
@@ -254,7 +337,7 @@
         !strcmp(tag, "PHONE") ||
         !strcmp(tag, "SMS"))) {
             bufID = LOG_ID_RADIO;
-            // Inform third party apps/ril/radio.. to use Rlog or RLOG
+            /* 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;
     }
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
index 32a202b..5ef349b 100644
--- a/liblog/logd_write_kern.c
+++ b/liblog/logd_write_kern.c
@@ -95,12 +95,15 @@
     if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
         log_fd = log_fds[(int)log_id];
     } else {
-        return EBADF;
+        return -EBADF;
     }
 
     do {
         ret = log_writev(log_fd, vec, nr);
-    } while (ret < 0 && errno == EINTR);
+        if (ret < 0) {
+            ret = -errno;
+        }
+    } while (ret == -EINTR);
 
     return ret;
 }
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index ffb7fd1..d726f2d 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -38,30 +38,30 @@
     _rc; })
 
 TEST(liblog, __android_log_buf_print) {
-    ASSERT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_print",
                                          "radio"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_print",
                                          "system"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_print",
                                          "main"));
     usleep(1000);
 }
 
 TEST(liblog, __android_log_buf_write) {
-    ASSERT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_write",
                                          "radio"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_write",
                                          "system"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_write",
                                          "main"));
     usleep(1000);
@@ -69,16 +69,16 @@
 
 TEST(liblog, __android_log_btwrite) {
     int intBuf = 0xDEADBEEF;
-    ASSERT_LT(0, __android_log_btwrite(0,
+    EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_INT,
                                       &intBuf, sizeof(intBuf)));
     long long longBuf = 0xDEADBEEFA55A5AA5;
-    ASSERT_LT(0, __android_log_btwrite(0,
+    EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_LONG,
                                       &longBuf, sizeof(longBuf)));
     usleep(1000);
     char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5";
-    ASSERT_LT(0, __android_log_btwrite(0,
+    EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_STRING,
                                       Buf, sizeof(Buf) - 1));
     usleep(1000);
@@ -120,7 +120,7 @@
 
     pid_t pid = getpid();
 
-    ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open(
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
 
     log_time ts(CLOCK_MONOTONIC);
@@ -155,7 +155,7 @@
         }
     }
 
-    ASSERT_EQ(1, count);
+    EXPECT_EQ(1, count);
 
     android_logger_list_close(logger_list);
 }
@@ -221,7 +221,7 @@
 
     v += pid & 0xFFFF;
 
-    ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open(
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, O_RDONLY, 1000, pid)));
 
     int count = 0;
@@ -277,13 +277,13 @@
             ++signals;
             break;
         }
-    } while (!signaled || ({log_time t(CLOCK_MONOTONIC); t < signal_time;}));
+    } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
     alarm(0);
     signal(SIGALRM, SIG_DFL);
 
-    ASSERT_LT(1, count);
+    EXPECT_LT(1, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 
     android_logger_list_close(logger_list);
 
@@ -295,9 +295,245 @@
     const unsigned long long one_percent_ticks = alarm_time;
     unsigned long long user_ticks = uticks_end - uticks_start;
     unsigned long long system_ticks = sticks_end - sticks_start;
-    ASSERT_GT(one_percent_ticks, user_ticks);
-    ASSERT_GT(one_percent_ticks, system_ticks);
-    ASSERT_GT(one_percent_ticks, user_ticks + system_ticks);
+    EXPECT_GT(one_percent_ticks, user_ticks);
+    EXPECT_GT(one_percent_ticks, system_ticks);
+    EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+}
+
+static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_buf[LOGGER_ENTRY_MAX_PAYLOAD
+    - sizeof(max_payload_tag) - 1] = "LEONATO\n\
+I learn in this letter that Don Peter of Arragon\n\
+comes this night to Messina\n\
+MESSENGER\n\
+He is very near by this: he was not three leagues off\n\
+when I left him\n\
+LEONATO\n\
+How many gentlemen have you lost in this action?\n\
+MESSENGER\n\
+But few of any sort, and none of name\n\
+LEONATO\n\
+A victory is twice itself when the achiever brings\n\
+home full numbers. I find here that Don Peter hath\n\
+bestowed much honour on a young Florentine called Claudio\n\
+MESSENGER\n\
+Much deserved on his part and equally remembered by\n\
+Don Pedro: he hath borne himself beyond the\n\
+promise of his age, doing, in the figure of a lamb,\n\
+the feats of a lion: he hath indeed better\n\
+bettered expectation than you must expect of me to\n\
+tell you how\n\
+LEONATO\n\
+He hath an uncle here in Messina will be very much\n\
+glad of it.\n\
+MESSENGER\n\
+I have already delivered him letters, and there\n\
+appears much joy in him; even so much that joy could\n\
+not show itself modest enough without a badge of\n\
+bitterness.\n\
+LEONATO\n\
+Did he break out into tears?\n\
+MESSENGER\n\
+In great measure.\n\
+LEONATO\n\
+A kind overflow of kindness: there are no faces\n\
+truer than those that are so washed. How much\n\
+better is it to weep at joy than to joy at weeping!\n\
+BEATRICE\n\
+I pray you, is Signior Mountanto returned from the\n\
+wars or no?\n\
+MESSENGER\n\
+I know none of that name, lady: there was none such\n\
+in the army of any sort.\n\
+LEONATO\n\
+What is he that you ask for, niece?\n\
+HERO\n\
+My cousin means Signior Benedick of Padua.\n\
+MESSENGER\n\
+O, he's returned; and as pleasant as ever he was.\n\
+BEATRICE\n\
+He set up his bills here in Messina and challenged\n\
+Cupid at the flight; and my uncle's fool, reading\n\
+the challenge, subscribed for Cupid, and challenged\n\
+him at the bird-bolt. I pray you, how many hath he\n\
+killed and eaten in these wars? But how many hath\n\
+he killed? for indeed I promised to eat all of his killing.\n\
+LEONATO\n\
+Faith, niece, you tax Signior Benedick too much;\n\
+but he'll be meet with you, I doubt it not.\n\
+MESSENGER\n\
+He hath done good service, lady, in these wars.\n\
+BEATRICE\n\
+You had musty victual, and he hath holp to eat it:\n\
+he is a very valiant trencherman; he hath an\n\
+excellent stomach.\n\
+MESSENGER\n\
+And a good soldier too, lady.\n\
+BEATRICE\n\
+And a good soldier to a lady: but what is he to a lord?\n\
+MESSENGER\n\
+A lord to a lord, a man to a man; stuffed with all\n\
+honourable virtues.\n\
+BEATRICE\n\
+It is so, indeed; he is no less than a stuffed man:\n\
+but for the stuffing,--well, we are all mortal.\n\
+LEONATO\n\
+You must not, sir, mistake my niece. There is a\n\
+kind of merry war betwixt Signior Benedick and her:\n\
+they never meet but there's a skirmish of wit\n\
+between them.\n\
+BEATRICE\n\
+Alas! he gets nothing by that. In our last\n\
+conflict four of his five wits went halting off, and\n\
+now is the whole man governed with one: so that if\n\
+he have wit enough to keep himself warm, let him\n\
+bear it for a difference between himself and his\n\
+horse; for it is all the wealth that he hath left,\n\
+to be known a reasonable creature. Who is his\n\
+companion now? He hath every month a new sworn brother.\n\
+MESSENGER\n\
+Is't possible?\n\
+BEATRICE\n\
+Very easily possible: he wears his faith but as\n\
+the fashion of his hat; it ever changes with the\n\
+next block.\n\
+MESSENGER\n\
+I see, lady, the gentleman is not in your books.\n\
+BEATRICE\n\
+No; an he were, I would burn my study. But, I pray\n\
+you, who is his companion? Is there no young\n\
+squarer now that will make a voyage with him to the devil?\n\
+MESSENGER\n\
+He is most in the company of the right noble Claudio.\n\
+BEATRICE\n\
+O Lord, he will hang upon him like a disease: he\n\
+is sooner caught than the pestilence, and the taker\n\
+runs presently mad. God help the noble Claudio! if\n\
+he have caught the Benedick, it will cost him a\n\
+thousand pound ere a' be cured.\n\
+MESSENGER\n\
+I will hold friends with you, lady.\n\
+BEATRICE\n\
+Do, good friend.\n\
+LEONATO\n\
+You will never run mad, niece.\n\
+BEATRICE\n\
+No, not till a hot January.\n\
+MESSENGER\n\
+Don Pedro is approached.\n\
+Enter DON PEDRO, DON JOHN, CLAUDIO, BENEDICK, and BALTHASAR\n\
+\n\
+DON PEDRO\n\
+Good Signior Leonato, you are come to meet your\n\
+trouble: the fashion of the world is to avoid\n\
+cost, and you encounter it\n\
+LEONATO\n\
+Never came trouble to my house in the likeness";
+
+TEST(liblog, max_payload) {
+    pid_t pid = getpid();
+    char tag[sizeof(max_payload_tag)];
+    memcpy(tag, max_payload_tag, sizeof(tag));
+    snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+    LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+                                              tag, max_payload_buf));
+
+    struct logger_list *logger_list;
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_SYSTEM, O_RDONLY, 100, 0)));
+
+    bool matches = false;
+    ssize_t max_len = 0;
+
+    for(;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+            continue;
+        }
+
+        char *data = log_msg.msg() + 1;
+
+        if (strcmp(data, tag)) {
+            continue;
+        }
+
+        data += strlen(data) + 1;
+
+        const char *left = data;
+        const char *right = max_payload_buf;
+        while (*left && *right && (*left == *right)) {
+            ++left;
+            ++right;
+        }
+
+        if (max_len <= (left - data)) {
+            max_len = left - data + 1;
+        }
+
+        if (max_len > 512) {
+            matches = true;
+            break;
+        }
+    }
+
+    EXPECT_EQ(true, matches);
+
+    EXPECT_LE(sizeof(max_payload_buf), max_len);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, dual_reader) {
+    struct logger_list *logger_list1;
+
+    // >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
+    ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
+        LOG_ID_MAIN, O_RDONLY | O_NDELAY, 25, 0)));
+
+    struct logger_list *logger_list2;
+
+    if (NULL == (logger_list2 = android_logger_list_open(
+            LOG_ID_MAIN, O_RDONLY | O_NDELAY, 15, 0))) {
+        android_logger_list_close(logger_list1);
+        ASSERT_TRUE(NULL != logger_list2);
+    }
+
+    int count1 = 0;
+    bool done1 = false;
+    int count2 = 0;
+    bool done2 = false;
+
+    do {
+        log_msg log_msg;
+
+        if (!done1) {
+            if (android_logger_list_read(logger_list1, &log_msg) <= 0) {
+                done1 = true;
+            } else {
+                ++count1;
+            }
+        }
+
+        if (!done2) {
+            if (android_logger_list_read(logger_list2, &log_msg) <= 0) {
+                done2 = true;
+            } else {
+                ++count2;
+            }
+        }
+    } while ((!done1) || (!done2));
+
+    android_logger_list_close(logger_list1);
+    android_logger_list_close(logger_list2);
+
+    EXPECT_EQ(25, count1);
+    EXPECT_EQ(15, count2);
 }
 
 TEST(liblog, android_logger_get_) {
@@ -310,11 +546,11 @@
             continue;
         }
         struct logger * logger;
-        ASSERT_EQ(0, NULL == (logger = android_logger_open(logger_list, id)));
-        ASSERT_EQ(id, android_logger_get_id(logger));
-        ASSERT_LT(0, android_logger_get_log_size(logger));
-        ASSERT_LT(0, android_logger_get_log_readable_size(logger));
-        ASSERT_LT(0, android_logger_get_log_version(logger));
+        EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
+        EXPECT_EQ(id, android_logger_get_id(logger));
+        EXPECT_LT(0, android_logger_get_log_size(logger));
+        EXPECT_LT(0, android_logger_get_log_readable_size(logger));
+        EXPECT_LT(0, android_logger_get_log_version(logger));
     }
 
     android_logger_list_close(logger_list);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7bf91a9..5a80efe 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -699,7 +699,7 @@
             int ret;
             ret = android_logger_clear(dev->logger);
             if (ret) {
-                perror("clearLog");
+                perror("failed to clear the log");
                 exit(EXIT_FAILURE);
             }
         }
@@ -707,7 +707,7 @@
 #ifdef USERDEBUG_BUILD
 
         if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
-            perror("setLogSize");
+            perror("failed to set the log size");
             exit(EXIT_FAILURE);
         }
 
@@ -718,13 +718,13 @@
 
             size = android_logger_get_log_size(dev->logger);
             if (size < 0) {
-                perror("getLogSize");
+                perror("failed to get the log size");
                 exit(EXIT_FAILURE);
             }
 
             readable = android_logger_get_log_readable_size(dev->logger);
             if (readable < 0) {
-                perror("getLogReadableSize");
+                perror("failed to get the readable log size");
                 exit(EXIT_FAILURE);
             }
 
@@ -748,7 +748,7 @@
         free(buf);
 
         if (ret) {
-            perror("setPruneList");
+            perror("failed to set the prune list");
             exit(EXIT_FAILURE);
         }
     }
@@ -792,7 +792,7 @@
         }
 
         if (!buf) {
-            perror("response read");
+            perror("failed to read data");
             exit(EXIT_FAILURE);
         }
 
@@ -807,7 +807,10 @@
         // squash out the byte count
         cp = buf;
         if (!truncated) {
-            while (isdigit(*cp) || (*cp == '\n')) {
+            while (isdigit(*cp)) {
+                ++cp;
+            }
+            if (*cp == '\n') {
                 ++cp;
             }
         }
@@ -859,7 +862,7 @@
                 fprintf(stderr, "read: unexpected length.\n");
                 exit(EXIT_FAILURE);
             }
-            perror("logcat read");
+            perror("logcat read failure");
             exit(EXIT_FAILURE);
         }
 
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 818a978..0165073 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <signal.h>
 #include <stdio.h>
+#include <string.h>
+
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/logger.h>
@@ -41,7 +44,7 @@
 TEST(logcat, sorted_order) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
       "r")));
 
@@ -92,34 +95,49 @@
         }
     } last(NULL);
 
+    char *last_buffer = NULL;
     char buffer[5120];
 
     int count = 0;
+    int next_lt_last = 0;
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
             continue;
         }
         if (!last.valid()) {
+            free(last_buffer);
+            last_buffer = strdup(buffer);
             last.init(buffer);
         }
         timestamp next(buffer);
-        ASSERT_EQ(0, next < last);
+        if (next < last) {
+            if (last_buffer) {
+                fprintf(stderr, "<%s", last_buffer);
+            }
+            fprintf(stderr, ">%s", buffer);
+            ++next_lt_last;
+        }
         if (next.valid()) {
+            free(last_buffer);
+            last_buffer = strdup(buffer);
             last.init(buffer);
         }
         ++count;
     }
+    free(last_buffer);
 
     pclose(fp);
 
-    ASSERT_LT(100, count);
+    EXPECT_EQ(0, next_lt_last);
+
+    EXPECT_LT(100, count);
 }
 
 TEST(logcat, buckets) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b radio -b events -b system -b main -d 2>/dev/null",
       "r")));
 
@@ -141,15 +159,15 @@
 
     pclose(fp);
 
-    ASSERT_EQ(15, ids);
+    EXPECT_EQ(15, ids);
 
-    ASSERT_EQ(4, count);
+    EXPECT_EQ(4, count);
 }
 
 TEST(logcat, tail_3) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null",
       "r")));
 
@@ -173,7 +191,7 @@
 TEST(logcat, tail_10) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null",
       "r")));
 
@@ -197,7 +215,7 @@
 TEST(logcat, tail_100) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null",
       "r")));
 
@@ -221,7 +239,7 @@
 TEST(logcat, tail_1000) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null",
       "r")));
 
@@ -242,6 +260,72 @@
     ASSERT_EQ(1000, count);
 }
 
+TEST(logcat, tail_time) {
+    FILE *fp;
+
+    ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
+
+    char buffer[5120];
+    char *last_timestamp = NULL;
+    char *first_timestamp = NULL;
+    int count = 0;
+    const unsigned int time_length = 18;
+    const unsigned int time_offset = 2;
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if ((buffer[0] == '[') && (buffer[1] == ' ')
+         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
+         && (buffer[time_offset + 2] == '-')) {
+            ++count;
+            buffer[time_length + time_offset] = '\0';
+            if (!first_timestamp) {
+                first_timestamp = strdup(buffer + time_offset);
+            }
+            free(last_timestamp);
+            last_timestamp = strdup(buffer + time_offset);
+        }
+    }
+    pclose(fp);
+
+    EXPECT_EQ(10, count);
+    EXPECT_TRUE(last_timestamp != NULL);
+    EXPECT_TRUE(first_timestamp != NULL);
+
+    snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1",
+             first_timestamp);
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+    int second_count = 0;
+    int last_timestamp_count = -1;
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if ((buffer[0] == '[') && (buffer[1] == ' ')
+         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
+         && (buffer[time_offset + 2] == '-')) {
+            ++second_count;
+            buffer[time_length + time_offset] = '\0';
+            if (first_timestamp) {
+                // we can get a transitory *extremely* rare failure if hidden
+                // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
+                EXPECT_STREQ(buffer + time_offset, first_timestamp);
+                free(first_timestamp);
+                first_timestamp = NULL;
+            }
+            if (!strcmp(buffer + time_offset, last_timestamp)) {
+                last_timestamp_count = second_count;
+            }
+        }
+    }
+    pclose(fp);
+
+    free(last_timestamp);
+    last_timestamp = NULL;
+
+    EXPECT_TRUE(first_timestamp == NULL);
+    EXPECT_LE(count, second_count);
+    EXPECT_LE(count, last_timestamp_count);
+}
+
 TEST(logcat, End_to_End) {
     pid_t pid = getpid();
 
@@ -250,7 +334,7 @@
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
     FILE *fp;
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b events -t 100 2>/dev/null",
       "r")));
 
@@ -281,7 +365,7 @@
 TEST(logcat, get_) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b radio -b events -b system -b main -g 2>/dev/null",
       "r")));
 
@@ -329,7 +413,7 @@
 
     v &= 0xFFFFFFFFFFFAFFFFULL;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events 2>&1",
       "r")));
@@ -343,7 +427,6 @@
     signal(SIGALRM, caught_blocking);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-        alarm(2);
 
         if (!strncmp(buffer, "DONE", 4)) {
             break;
@@ -373,9 +456,9 @@
 
     pclose(fp);
 
-    ASSERT_LE(2, count);
+    EXPECT_LE(2, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 }
 
 static void caught_blocking_tail(int signum)
@@ -399,7 +482,7 @@
 
     v &= 0xFFFAFFFFFFFFFFFFULL;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events -T 5 2>&1",
       "r")));
@@ -413,7 +496,6 @@
     signal(SIGALRM, caught_blocking_tail);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-        alarm(2);
 
         if (!strncmp(buffer, "DONE", 4)) {
             break;
@@ -445,9 +527,9 @@
 
     pclose(fp);
 
-    ASSERT_LE(2, count);
+    EXPECT_LE(2, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 }
 
 static void caught_blocking_clear(int signum)
@@ -469,7 +551,7 @@
 
     // 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(
+    ASSERT_TRUE(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",
@@ -484,7 +566,6 @@
     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");
@@ -523,9 +604,9 @@
 
     pclose(fp);
 
-    ASSERT_LE(1, count);
+    EXPECT_LE(1, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 }
 
 #ifdef USERDEBUG_BUILD
@@ -601,22 +682,23 @@
 
     ASSERT_EQ(true, get_white_black(&list));
 
-    static const char adjustment[] = "~! ~1000";
+    static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30";
     ASSERT_EQ(true, set_white_black(adjustment));
     ASSERT_EQ(true, get_white_black(&adjust));
-    if (strcmp(adjustment, adjust)) {
-        fprintf(stderr, "ERROR: '%s' != '%s'\n", adjustment, adjust);
-    }
-    ASSERT_STREQ(adjustment, adjust);
+    EXPECT_STREQ(adjustment, adjust);
+    free(adjust);
+    adjust = NULL;
+
+    static const char adjustment2[] = "300/20 300/21 2000 ~1000";
+    ASSERT_EQ(true, set_white_black(adjustment2));
+    ASSERT_EQ(true, get_white_black(&adjust));
+    EXPECT_STREQ(adjustment2, adjust);
     free(adjust);
     adjust = NULL;
 
     ASSERT_EQ(true, set_white_black(list));
     ASSERT_EQ(true, get_white_black(&adjust));
-    if (strcmp(list, adjust)) {
-        fprintf(stderr, "ERROR: '%s' != '%s'\n", list, adjust);
-    }
-    ASSERT_STREQ(list, adjust);
+    EXPECT_STREQ(list, adjust);
     free(adjust);
     adjust = NULL;
 
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 197b7e8..1c5cef0 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -45,13 +45,13 @@
 }
 
 void LogBuffer::log(log_id_t log_id, log_time realtime,
-                    uid_t uid, pid_t pid, const char *msg,
-                    unsigned short len) {
+                    uid_t uid, pid_t pid, pid_t tid,
+                    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);
+                                                  uid, pid, tid, msg, len);
 
     pthread_mutex_lock(&mLogElementsLock);
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 0745e56..cbbb2ce 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -48,7 +48,8 @@
     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);
+             uid_t uid, pid_t pid, pid_t tid,
+             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,
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 8d45f34..d959ceb 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -27,11 +27,12 @@
 const log_time LogBufferElement::FLUSH_ERROR((uint32_t)0, (uint32_t)0);
 
 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
-                                   uid_t uid, pid_t pid, const char *msg,
-                                   unsigned short len)
+                                   uid_t uid, pid_t pid, pid_t tid,
+                                   const char *msg, unsigned short len)
         : mLogId(log_id)
         , mUid(uid)
         , mPid(pid)
+        , mTid(tid)
         , mMsgLen(len)
         , mMonotonicTime(CLOCK_MONOTONIC)
         , mRealTime(realtime) {
@@ -50,6 +51,7 @@
     entry.len = mMsgLen;
     entry.lid = mLogId;
     entry.pid = mPid;
+    entry.tid = mTid;
     entry.sec = mRealTime.tv_sec;
     entry.nsec = mRealTime.tv_nsec;
 
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 1da09ae..fdca973 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -26,6 +26,7 @@
     const log_id_t mLogId;
     const uid_t mUid;
     const pid_t mPid;
+    const pid_t mTid;
     char *mMsg;
     const unsigned short mMsgLen;
     const log_time mMonotonicTime;
@@ -33,12 +34,14 @@
 
 public:
     LogBufferElement(log_id_t log_id, log_time realtime,
-                     uid_t uid, pid_t pid, const char *msg, unsigned short len);
+                     uid_t uid, pid_t pid, pid_t tid,
+                     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; }
+    pid_t getTid(void) const { return mTid; }
     unsigned short getMsgLen() const { return mMsgLen; }
     log_time getMonotonicTime(void) const { return mMonotonicTime; }
     log_time getRealTime(void) const { return mRealTime; }
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index c6b248b..b835b4f 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -31,7 +31,8 @@
 {  }
 
 bool LogListener::onDataAvailable(SocketClient *cli) {
-    char buffer[1024];
+    char buffer[sizeof_log_id_t + sizeof(log_time) + sizeof(char)
+        + LOGGER_ENTRY_MAX_PAYLOAD];
     struct iovec iov = { buffer, sizeof(buffer) };
     memset(buffer, 0, sizeof(buffer));
 
@@ -49,7 +50,7 @@
     int socket = cli->getSocket();
 
     ssize_t n = recvmsg(socket, &hdr, 0);
-    if (n <= (ssize_t) sizeof_log_id_t) {
+    if (n <= (ssize_t)(sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time))) {
         return false;
     }
 
@@ -81,17 +82,24 @@
     if (log_id < 0 || log_id >= LOG_ID_MAX) {
         return false;
     }
-
     char *msg = ((char *)buffer) + sizeof_log_id_t;
     n -= sizeof_log_id_t;
 
+    // second element is the thread id of the caller
+    pid_t tid = (pid_t) *((uint16_t *) msg);
+    msg += sizeof(uint16_t);
+    n -= sizeof(uint16_t);
+
+    // third element is the realtime at point of caller
     log_time realtime(msg);
     msg += sizeof(log_time);
     n -= sizeof(log_time);
 
-    unsigned short len = n;
+    // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
+    // truncated message to the logs.
+    unsigned short len = n; // cap to internal maximum
     if (len == n) {
-        logbuf->log(log_id, realtime, cred->uid, cred->pid, msg, len);
+        logbuf->log(log_id, realtime, cred->uid, cred->pid, tid, msg, len);
         reader->notifyNewLog();
     }
 
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index d0ceb9f..5f8173f 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -154,8 +154,8 @@
                 }
                 m = pid - p->mPid;
             }
-            if (m >= 0) {
-                if (m > 0) {
+            if (m <= 0) {
+                if (m < 0) {
                     list->insert(it, new Prune(uid,pid));
                 }
                 break;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ffbc9fa..23a517a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -420,6 +420,13 @@
     critical
     seclabel u:r:ueventd:s0
 
+service logd /system/bin/logd
+    class core
+    socket logd stream 0666 logd logd
+    socket logdr seqpacket 0666 logd logd
+    socket logdw dgram 0222 logd logd
+    seclabel u:r:logd:s0
+
 service healthd /sbin/healthd
     class core
     critical
@@ -452,12 +459,6 @@
 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