Merge "Adding documentation on the sync part of the adb protocol previously missing."
diff --git a/adb/transport_local.c b/adb/transport_local.c
index d2dbca6..948cc15 100644
--- a/adb/transport_local.c
+++ b/adb/transport_local.c
@@ -48,7 +48,7 @@
  * local transport it is connected. The list is used to detect when we're
  * trying to connect twice to a given local transport.
  */
-#define  ADB_LOCAL_TRANSPORT_MAX  16
+#define  ADB_LOCAL_TRANSPORT_MAX  64
 
 ADB_MUTEX_DEFINE( local_transports_lock );
 
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/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/include/log/log_read.h b/include/log/log_read.h
index 7edfe3c..bd9de12 100644
--- a/include/log/log_read.h
+++ b/include/log/log_read.h
@@ -17,11 +17,17 @@
 #ifndef _LIBS_LOG_LOG_READ_H
 #define _LIBS_LOG_LOG_READ_H
 
+#include <stdint.h>
 #include <time.h>
 
 /* struct log_time is a wire-format variant of struct timespec */
 #define NS_PER_SEC 1000000000ULL
+
 #ifdef __cplusplus
+
+// NB: do NOT define a copy constructor. This will result in structure
+// no longer being compatible with pass-by-value which is desired
+// efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
 struct log_time {
 public:
     uint32_t tv_sec; // good to Feb 5 2106
@@ -32,16 +38,12 @@
         tv_sec = T.tv_sec;
         tv_nsec = T.tv_nsec;
     }
-    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;
     }
+    static const timespec EPOCH;
     log_time()
     {
     }
@@ -86,6 +88,12 @@
     {
         return !(*this > T);
     }
+    log_time operator-= (const timespec &T);
+    log_time operator- (const timespec &T) const
+    {
+        log_time local(*this);
+        return local -= T;
+    }
 
     // log_time
     bool operator== (const log_time &T) const
@@ -114,17 +122,31 @@
     {
         return !(*this > T);
     }
+    log_time operator-= (const log_time &T);
+    log_time operator- (const log_time &T) const
+    {
+        log_time local(*this);
+        return local -= T;
+    }
 
     uint64_t nsec() const
     {
         return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
     }
+
+    static const char default_format[];
+
+    // Add %#q for the fraction of a second to the standard library functions
+    char *strptime(const char *s, const char *format = default_format);
 } __attribute__((__packed__));
+
 #else
+
 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 8dab234..3c6ea30 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -12,6 +12,7 @@
 
 #include <stdint.h>
 #include <log/log.h>
+#include <log/log_read.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -161,6 +162,9 @@
 struct logger_list *android_logger_list_alloc(int mode,
                                               unsigned int tail,
                                               pid_t pid);
+struct logger_list *android_logger_list_alloc_time(int mode,
+                                                   log_time start,
+                                                   pid_t pid);
 void android_logger_list_free(struct logger_list *logger_list);
 /* In the purest sense, the following two are orthogonal interfaces */
 int android_logger_list_read(struct logger_list *logger_list,
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/Android.mk b/liblog/Android.mk
index 4fe20db..a23de2d 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -50,10 +50,11 @@
 endif
 
 liblog_host_sources := $(liblog_sources) fake_log_device.c
+liblog_target_sources := $(liblog_sources) log_time.cpp
 ifneq ($(TARGET_USES_LOGD),false)
-liblog_target_sources = $(liblog_sources) log_read.c
+liblog_target_sources += log_read.c
 else
-liblog_target_sources = $(liblog_sources) log_read_kern.c
+liblog_target_sources += log_read_kern.c
 endif
 
 # Shared and static library for host
@@ -61,12 +62,18 @@
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_host_sources)
 LOCAL_LDLIBS := -lpthread
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS += -lrt
+endif
 LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS := -lrt
+endif
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 
@@ -76,6 +83,9 @@
 LOCAL_MODULE := lib64log
 LOCAL_SRC_FILES := $(liblog_host_sources)
 LOCAL_LDLIBS := -lpthread
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS += -lrt
+endif
 LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64
 include $(BUILD_HOST_STATIC_LIBRARY)
 
diff --git a/liblog/log_read.c b/liblog/log_read.c
index e4acac2..2dd07e6 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <signal.h>
 #include <stddef.h>
 #define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
@@ -234,6 +235,7 @@
     struct listnode node;
     int mode;
     unsigned int tail;
+    log_time start;
     pid_t pid;
     int sock;
 };
@@ -270,11 +272,11 @@
                             const char *msg, char *buf, size_t buf_size)
 {
     ssize_t ret;
+    int errno_save = 0;
     int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
                                    SOCK_STREAM);
     if (sock < 0) {
-        ret = sock;
-        goto done;
+        return sock;
     }
 
     if (msg) {
@@ -290,9 +292,12 @@
 
 done:
     if ((ret == -1) && errno) {
-        ret = -errno;
+        errno_save = errno;
     }
     close(sock);
+    if (errno_save) {
+        errno = errno_save;
+    }
     return ret;
 }
 
@@ -303,6 +308,7 @@
     }
 
     if (strncmp(buf, "success", 7)) {
+        errno = EINVAL;
         return -1;
     }
 
@@ -441,6 +447,8 @@
 
     list_init(&logger_list->node);
     logger_list->mode = mode;
+    logger_list->start.tv_sec = 0;
+    logger_list->start.tv_nsec = 0;
     logger_list->tail = tail;
     logger_list->pid = pid;
     logger_list->sock = -1;
@@ -448,6 +456,27 @@
     return logger_list;
 }
 
+struct logger_list *android_logger_list_alloc_time(int mode,
+                                                   log_time start,
+                                                   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->start = start;
+    logger_list->tail = 0;
+    logger_list->pid = pid;
+    logger_list->sock = -1;
+
+    return logger_list;
+}
+
 /* android_logger_list_register unimplemented, no use case */
 /* android_logger_list_unregister unimplemented, no use case */
 
@@ -564,6 +593,15 @@
             cp += ret;
         }
 
+        if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+            ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
+                           logger_list->start.tv_sec,
+                           logger_list->start.tv_nsec);
+            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);
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
index 483b6b6..9cccb1d 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -308,6 +308,13 @@
     return logger_list;
 }
 
+struct logger_list *android_logger_list_alloc_time(int mode,
+                                                   log_time start UNUSED,
+                                                   pid_t pid)
+{
+    return android_logger_list_alloc(mode, 0, pid);
+}
+
 /* android_logger_list_register unimplemented, no use case */
 /* android_logger_list_unregister unimplemented, no use case */
 
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
new file mode 100644
index 0000000..755c2d9
--- /dev/null
+++ b/liblog/log_time.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <log/log_read.h>
+
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%3q";
+const timespec log_time::EPOCH = { 0, 0 };
+
+// Add %#q for fractional seconds to standard strptime function
+
+char *log_time::strptime(const char *s, const char *format) {
+    time_t now;
+#ifdef __linux__
+    *this = log_time(CLOCK_REALTIME);
+    now = tv_sec;
+#else
+    time(&now);
+    tv_sec = now;
+    tv_nsec = 0;
+#endif
+
+    struct tm *ptm;
+#if (defined(HAVE_LOCALTIME_R))
+    struct tm tmBuf;
+    ptm = localtime_r(&now, &tmBuf);
+#else
+    ptm = localtime(&now);
+#endif
+
+    char fmt[strlen(format) + 1];
+    strcpy(fmt, format);
+
+    char *ret = const_cast<char *> (s);
+    char *cp;
+    for (char *f = cp = fmt; ; ++cp) {
+        if (!*cp) {
+            if (f != cp) {
+                ret = ::strptime(ret, f, ptm);
+            }
+            break;
+        }
+        if (*cp != '%') {
+            continue;
+        }
+        char *e = cp;
+        ++e;
+#if (defined(__BIONIC__))
+        if (*e == 's') {
+            *cp = '\0';
+            if (*f) {
+                ret = ::strptime(ret, f, ptm);
+                if (!ret) {
+                    break;
+                }
+            }
+            tv_sec = 0;
+            while (isdigit(*ret)) {
+                tv_sec = tv_sec * 10 + *ret - '0';
+                ++ret;
+            }
+            now = tv_sec;
+#if (defined(HAVE_LOCALTIME_R))
+            ptm = localtime_r(&now, &tmBuf);
+#else
+            ptm = localtime(&now);
+#endif
+        } else
+#endif
+        {
+            unsigned num = 0;
+            while (isdigit(*e)) {
+                num = num * 10 + *e - '0';
+                ++e;
+            }
+            if (*e != 'q') {
+                continue;
+            }
+            *cp = '\0';
+            if (*f) {
+                ret = ::strptime(ret, f, ptm);
+                if (!ret) {
+                    break;
+                }
+            }
+            unsigned long mul = NS_PER_SEC;
+            if (num == 0) {
+                num = INT_MAX;
+            }
+            tv_nsec = 0;
+            while (isdigit(*ret) && num && (mul > 1)) {
+                --num;
+                mul /= 10;
+                tv_nsec = tv_nsec + (*ret - '0') * mul;
+                ++ret;
+            }
+        }
+        f = cp = e;
+        ++f;
+    }
+
+    if (ret) {
+        tv_sec = mktime(ptm);
+        return ret;
+    }
+
+    // Upon error, place a known value into the class, the current time.
+#ifdef __linux__
+    *this = log_time(CLOCK_REALTIME);
+#else
+    time(&now);
+    tv_sec = now;
+    tv_nsec = 0;
+#endif
+    return ret;
+}
+
+log_time log_time::operator-= (const timespec &T) {
+    // No concept of negative time, clamp to EPOCH
+    if (*this <= T) {
+        return *this = EPOCH;
+    }
+
+    if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
+        --this->tv_sec;
+        this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+    } else {
+        this->tv_nsec -= T.tv_nsec;
+    }
+    this->tv_sec -= T.tv_sec;
+
+    return *this;
+}
+
+log_time log_time::operator-= (const log_time &T) {
+    // No concept of negative time, clamp to EPOCH
+    if (*this <= T) {
+        return *this = EPOCH;
+    }
+
+    if (this->tv_nsec < T.tv_nsec) {
+        --this->tv_sec;
+        this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+    } else {
+        this->tv_nsec -= T.tv_nsec;
+    }
+    this->tv_sec -= T.tv_sec;
+
+    return *this;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 01f9249..aebddc8 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -17,22 +17,23 @@
 /*
  * Read-only access to Zip archives, with minimal heap allocation.
  */
-#include "ziparchive/zip_archive.h"
-
-#include <zlib.h>
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <log/log.h>
-#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <utils/FileMap.h>
+#include <zlib.h>
 
 #include <JNIHelp.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
 
+#include "ziparchive/zip_archive.h"
+
 // This is for windows. If we don't open a file in binary mode, weirds
 // things will happen.
 #ifndef O_BINARY
@@ -217,8 +218,7 @@
     ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
 
     if (actual != get_size) {
-      ALOGW("CopyFileToFile: copy read failed (%d vs %zd)",
-          (int) actual, get_size);
+      ALOGW("CopyFileToFile: copy read failed (%zd vs %zd)", actual, get_size);
       return kIoError;
     }
 
@@ -337,12 +337,12 @@
   const off64_t search_start = file_length - read_amount;
 
   if (lseek64(fd, search_start, SEEK_SET) != search_start) {
-    ALOGW("Zip: seek %lld failed: %s", search_start, strerror(errno));
+    ALOGW("Zip: seek %" PRId64 " failed: %s", search_start, strerror(errno));
     return kIoError;
   }
   ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scan_buffer, read_amount));
   if (actual != (ssize_t) read_amount) {
-    ALOGW("Zip: read %zd failed: %s", read_amount, strerror(errno));
+    ALOGW("Zip: read %u failed: %s", read_amount, strerror(errno));
     return kIoError;
   }
 
@@ -379,7 +379,7 @@
   const off64_t dir_offset = get4LE(eocd_ptr + kEOCDFileOffset);
 
   if (dir_offset + dir_size > eocd_offset) {
-    ALOGW("Zip: bad offsets (dir %lld, size %lld, eocd %lld)",
+    ALOGW("Zip: bad offsets (dir %" PRId64 ", size %" PRId64 ", eocd %" PRId64 ")",
         dir_offset, dir_size, eocd_offset);
     return kInvalidOffset;
   }
@@ -388,8 +388,8 @@
     return kEmptyArchive;
   }
 
-  ALOGV("+++ num_entries=%d dir_size=%d dir_offset=%d", num_entries, dir_size,
-      dir_offset);
+  ALOGV("+++ num_entries=%d dir_size=%" PRId64 " dir_offset=%" PRId64,
+        num_entries, dir_size, dir_offset);
 
   /*
    * It all looks good.  Create a mapping for the CD, and set the fields
@@ -430,12 +430,12 @@
   }
 
   if (file_length > (off64_t) 0xffffffff) {
-    ALOGV("Zip: zip file too long %d", file_length);
+    ALOGV("Zip: zip file too long %" PRId64, file_length);
     return kInvalidFile;
   }
 
   if (file_length < (int64_t) kEOCDLen) {
-    ALOGV("Zip: length %ld is too small to be zip", file_length);
+    ALOGV("Zip: length %" PRId64 " is too small to be zip", file_length);
     return kInvalidFile;
   }
 
@@ -503,7 +503,7 @@
 
     const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset);
     if (local_header_offset >= archive->directory_offset) {
-      ALOGW("Zip: bad LFH offset %lld at entry %d", local_header_offset, i);
+      ALOGW("Zip: bad LFH offset %" PRId64 " at entry %d", local_header_offset, i);
       goto bail;
     }
 
@@ -522,8 +522,8 @@
 
     ptr += kCDELen + file_name_length + extra_length + comment_length;
     if ((size_t)(ptr - cd_ptr) > cd_length) {
-      ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d",
-        (int) (ptr - cd_ptr), cd_length, i);
+      ALOGW("Zip: bad CD advance (%zu vs %zu) at entry %d",
+        (size_t) (ptr - cd_ptr), cd_length, i);
       goto bail;
     }
   }
@@ -630,7 +630,7 @@
   // is Windows. Only recent versions of windows support unix like forks,
   // and even there the semantics are quite different.
   if (lseek64(fd, off, SEEK_SET) != off) {
-    ALOGW("Zip: failed seek to offset %lld", off);
+    ALOGW("Zip: failed seek to offset %" PRId64, off);
     return kIoError;
   }
 
@@ -686,12 +686,12 @@
   ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
                                  local_header_offset);
   if (actual != sizeof(lfh_buf)) {
-    ALOGW("Zip: failed reading lfh name from offset %lld", local_header_offset);
+    ALOGW("Zip: failed reading lfh name from offset %" PRId64, local_header_offset);
     return kIoError;
   }
 
   if (get4LE(lfh_buf) != kLFHSignature) {
-    ALOGW("Zip: didn't find signature at start of lfh, offset=%lld",
+    ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
         local_header_offset);
     return kInvalidOffset;
   }
@@ -733,7 +733,7 @@
                                   name_offset);
 
     if (actual != nameLen) {
-      ALOGW("Zip: failed reading lfh name from offset %lld", name_offset);
+      ALOGW("Zip: failed reading lfh name from offset %" PRId64, name_offset);
       free(name_buf);
       return kIoError;
     }
@@ -751,19 +751,19 @@
 
   const off64_t data_offset = local_header_offset + kLFHLen + lfhNameLen + lfhExtraLen;
   if (data_offset > cd_offset) {
-    ALOGW("Zip: bad data offset %lld in zip", (off64_t) data_offset);
+    ALOGW("Zip: bad data offset %" PRId64 " in zip", data_offset);
     return kInvalidOffset;
   }
 
   if ((off64_t)(data_offset + data->compressed_length) > cd_offset) {
-    ALOGW("Zip: bad compressed length in zip (%lld + %zd > %lld)",
+    ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %zd > %" PRId64 ")",
       data_offset, data->compressed_length, cd_offset);
     return kInvalidOffset;
   }
 
   if (data->method == kCompressStored &&
     (off64_t)(data_offset + data->uncompressed_length) > cd_offset) {
-     ALOGW("Zip: bad uncompressed length in zip (%lld + %zd > %lld)",
+     ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %d > %" PRId64 ")",
        data_offset, data->uncompressed_length, cd_offset);
      return kInvalidOffset;
   }
@@ -903,7 +903,7 @@
       const ssize_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
       const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, read_buf, getSize));
       if (actual != getSize) {
-        ALOGW("Zip: inflate read failed (%d vs %zd)", actual, getSize);
+        ALOGW("Zip: inflate read failed (%zd vs %zd)", actual, getSize);
         result = kIoError;
         goto z_bail;
       }
@@ -946,7 +946,7 @@
   *crc_out = zstream.adler;
 
   if (zstream.total_out != uncompressed_length || compressed_length != 0) {
-    ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)",
+    ALOGW("Zip: size mismatch on inflated file (%ld vs %u)",
         zstream.total_out, uncompressed_length);
     result = kInconsistentInformation;
     goto z_bail;
@@ -967,7 +967,7 @@
   off64_t data_offset = entry->offset;
 
   if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
-    ALOGW("Zip: lseek to data at %lld failed", (off64_t) data_offset);
+    ALOGW("Zip: lseek to data at %" PRId64 " failed", data_offset);
     return kIoError;
   }
 
@@ -990,7 +990,7 @@
   // TODO: Fix this check by passing the right flags to inflate2 so that
   // it calculates the CRC for us.
   if (entry->crc32 != crc && false) {
-    ALOGW("Zip: crc mismatch: expected %u, was %llu", entry->crc32, crc);
+    ALOGW("Zip: crc mismatch: expected %u, was %" PRIu64, entry->crc32, crc);
     return kInconsistentInformation;
   }
 
@@ -1010,8 +1010,8 @@
 
   int result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
   if (result == -1) {
-    ALOGW("Zip: unable to truncate file to %lld: %s", declared_length + current_offset,
-          strerror(errno));
+    ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+          declared_length + current_offset, strerror(errno));
     return kIoError;
   }
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 00b5ba9..5960609 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -17,6 +17,7 @@
 
 #include <cutils/sockets.h>
 #include <log/log.h>
+#include <log/log_read.h>
 #include <log/logger.h>
 #include <log/logd.h>
 #include <log/logprint.h>
@@ -302,7 +303,8 @@
     log_device_t* dev;
     bool needBinary = false;
     struct logger_list *logger_list;
-    int tail_lines = 0;
+    unsigned int tail_lines = 0;
+    log_time tail_time(log_time::EPOCH);
 
     signal(SIGPIPE, exit);
 
@@ -352,7 +354,32 @@
                 mode = O_RDONLY | O_NDELAY;
                 /* FALLTHRU */
             case 'T':
-                tail_lines = atoi(optarg);
+                if (strspn(optarg, "0123456789") != strlen(optarg)) {
+                    char *cp = tail_time.strptime(optarg,
+                                                  log_time::default_format);
+                    if (!cp) {
+                        fprintf(stderr,
+                                "ERROR: -%c \"%s\" not in \"%s\" time format\n",
+                                ret, optarg, log_time::default_format);
+                        exit(1);
+                    }
+                    if (*cp) {
+                        char c = *cp;
+                        *cp = '\0';
+                        fprintf(stderr,
+                                "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+                                ret, optarg, c, cp + 1);
+                        *cp = c;
+                    }
+                } else {
+                    tail_lines = atoi(optarg);
+                    if (!tail_lines) {
+                        fprintf(stderr,
+                                "WARNING: -%c %s invalid, setting to 1\n",
+                                ret, optarg);
+                        tail_lines = 1;
+                    }
+                }
             break;
 
             case 'g':
@@ -654,7 +681,11 @@
     }
 
     dev = devices;
-    logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+    if (tail_time != log_time::EPOCH) {
+        logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
+    } else {
+        logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+    }
     while (dev) {
         dev->logger_list = logger_list;
         dev->logger = android_logger_open(logger_list,
@@ -668,7 +699,7 @@
             int ret;
             ret = android_logger_clear(dev->logger);
             if (ret) {
-                perror("clearLog");
+                perror("failed to clear the log");
                 exit(EXIT_FAILURE);
             }
         }
@@ -676,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);
         }
 
@@ -687,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);
             }
 
@@ -717,7 +748,7 @@
         free(buf);
 
         if (ret) {
-            perror("setPruneList");
+            perror("failed to set the prune list");
             exit(EXIT_FAILURE);
         }
     }
@@ -761,7 +792,7 @@
         }
 
         if (!buf) {
-            perror("response read");
+            perror("failed to read data");
             exit(EXIT_FAILURE);
         }
 
@@ -828,7 +859,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/Android.mk b/logcat/tests/Android.mk
index bdaec14..733af31 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -28,7 +28,11 @@
     -g \
     -Wall -Wextra \
     -Werror \
-    -fno-builtin \
+    -fno-builtin
+
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+test_c_flags += -DUSERDEBUG_BUILD=1
+endif
 
 test_src_files := \
     logcat_test.cpp \
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index fc696bb..818a978 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -527,3 +527,100 @@
 
     ASSERT_EQ(1, signals);
 }
+
+#ifdef USERDEBUG_BUILD
+static bool get_white_black(char **list) {
+    FILE *fp;
+
+    fp = popen("logcat -p 2>/dev/null", "r");
+    if (fp == NULL) {
+        fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
+        return false;
+    }
+
+    char buffer[5120];
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        char *hold = *list;
+        char *buf = buffer;
+	while (isspace(*buf)) {
+            ++buf;
+        }
+        char *end = buf + strlen(buf);
+        while (isspace(*--end) && (end >= buf)) {
+            *end = '\0';
+        }
+        if (end < buf) {
+            continue;
+        }
+        if (hold) {
+            asprintf(list, "%s %s", hold, buf);
+            free(hold);
+        } else {
+            asprintf(list, "%s", buf);
+        }
+    }
+    pclose(fp);
+    return *list != NULL;
+}
+
+static bool set_white_black(const char *list) {
+    FILE *fp;
+
+    char buffer[5120];
+
+    snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list);
+    fp = popen(buffer, "r");
+    if (fp == NULL) {
+        fprintf(stderr, "ERROR: %s\n", buffer);
+        return false;
+    }
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        char *buf = buffer;
+	while (isspace(*buf)) {
+            ++buf;
+        }
+        char *end = buf + strlen(buf);
+        while (isspace(*--end) && (end >= buf)) {
+            *end = '\0';
+        }
+        if (end < buf) {
+            continue;
+        }
+        fprintf(stderr, "%s\n", buf);
+        pclose(fp);
+        return false;
+    }
+    return pclose(fp) == 0;
+}
+
+TEST(logcat, white_black_adjust) {
+    char *list = NULL;
+    char *adjust = NULL;
+
+    ASSERT_EQ(true, get_white_black(&list));
+
+    static const char adjustment[] = "~! ~1000";
+    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);
+    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);
+    free(adjust);
+    adjust = NULL;
+
+    free(list);
+    list = NULL;
+}
+#endif // USERDEBUG_BUILD
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index 0b8c31b..3be07c0 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -26,12 +26,14 @@
                            bool nonBlock,
                            unsigned long tail,
                            unsigned int logMask,
-                           pid_t pid)
+                           pid_t pid,
+                           log_time start)
         : mReader(reader)
         , mNonBlock(nonBlock)
         , mTail(tail)
         , mLogMask(logMask)
         , mPid(pid)
+        , mStart(start)
 { }
 
 // runSocketCommand is called once for every open client on the
@@ -69,7 +71,7 @@
             LogTimeEntry::unlock();
             return;
         }
-        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid);
+        entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
         times.push_back(entry);
     }
 
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 715daac..f34c06a 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -16,8 +16,13 @@
 #ifndef _FLUSH_COMMAND_H
 #define _FLUSH_COMMAND_H
 
+#include <log/log_read.h>
 #include <sysutils/SocketClientCommand.h>
 
+class LogBufferElement;
+
+#include "LogTimes.h"
+
 class LogReader;
 
 class FlushCommand : public SocketClientCommand {
@@ -26,13 +31,15 @@
     unsigned long mTail;
     unsigned int mLogMask;
     pid_t mPid;
+    log_time mStart;
 
 public:
     FlushCommand(LogReader &mReader,
                  bool nonBlock = false,
                  unsigned long tail = -1,
                  unsigned int logMask = -1,
-                 pid_t pid = 0);
+                 pid_t pid = 0,
+                 log_time start = LogTimeEntry::EPOCH);
     virtual void runSocketCommand(SocketClient *client);
 
     static bool hasReadLogs(SocketClient *client);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 01cc9de..8d45f34 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -24,7 +24,7 @@
 #include "LogBufferElement.h"
 #include "LogReader.h"
 
-const log_time LogBufferElement::FLUSH_ERROR(0, 0);
+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,
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index c6b248b..2aa2ebb 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));
 
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 5b540bf..60a3507 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <poll.h>
 #include <sys/socket.h>
 #include <cutils/sockets.h>
@@ -50,6 +51,14 @@
         tail = atol(cp + sizeof(_tail) - 1);
     }
 
+    log_time start(log_time::EPOCH);
+    static const char _start[] = " start=";
+    cp = strstr(buffer, _start);
+    if (cp) {
+        // Parse errors will result in current time
+        start.strptime(cp + sizeof(_start) - 1, "%s.%q");
+    }
+
     unsigned int logMask = -1;
     static const char _logIds[] = " lids=";
     cp = strstr(buffer, _logIds);
@@ -58,9 +67,8 @@
         cp += sizeof(_logIds) - 1;
         while (*cp && *cp != '\0') {
             int val = 0;
-            while (('0' <= *cp) && (*cp <= '9')) {
-                val *= 10;
-                val += *cp - '0';
+            while (isdigit(*cp)) {
+                val = val * 10 + *cp - '0';
                 ++cp;
             }
             logMask |= 1 << val;
@@ -83,7 +91,63 @@
         nonBlock = true;
     }
 
-    FlushCommand command(*this, nonBlock, tail, logMask, pid);
+    // Convert realtime to monotonic time
+    if (start == log_time::EPOCH) {
+        start = LogTimeEntry::EPOCH;
+    } else {
+        class LogFindStart {
+            const pid_t mPid;
+            const unsigned mLogMask;
+            bool startTimeSet;
+            log_time &start;
+            log_time last;
+
+        public:
+            LogFindStart(unsigned logMask, pid_t pid, log_time &start)
+                    : mPid(pid)
+                    , mLogMask(logMask)
+                    , startTimeSet(false)
+                    , start(start)
+                    , last(LogTimeEntry::EPOCH)
+            { }
+
+            static bool callback(const LogBufferElement *element, void *obj) {
+                LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
+                if (!me->startTimeSet
+                        && (!me->mPid || (me->mPid == element->getPid()))
+                        && (me->mLogMask & (1 << element->getLogId()))) {
+                    if (me->start == element->getRealTime()) {
+                        me->start = element->getMonotonicTime();
+                        me->startTimeSet = true;
+                    } else {
+                        if (me->start < element->getRealTime()) {
+                            me->start = me->last;
+                            me->startTimeSet = true;
+                        }
+                        me->last = element->getMonotonicTime();
+                    }
+                }
+                return false;
+            }
+
+            bool found() { return startTimeSet; }
+        } logFindStart(logMask, pid, start);
+
+        logbuf().flushTo(cli, LogTimeEntry::EPOCH,
+                         FlushCommand::hasReadLogs(cli),
+                         logFindStart.callback, &logFindStart);
+
+        if (!logFindStart.found()) {
+            if (nonBlock) {
+                doSocketDelete(cli);
+                return false;
+            }
+            log_time now(CLOCK_MONOTONIC);
+            start = now;
+        }
+    }
+
+    FlushCommand command(*this, nonBlock, tail, logMask, pid, start);
     command.runSocketCommand(cli);
     return true;
 }
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 67cc65e..8cb015c 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -25,7 +25,8 @@
 
 LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
                            bool nonBlock, unsigned long tail,
-                           unsigned int logMask, pid_t pid)
+                           unsigned int logMask, pid_t pid,
+                           log_time start)
         : mRefCount(1)
         , mRelease(false)
         , mError(false)
@@ -39,7 +40,7 @@
         , mTail(tail)
         , mIndex(0)
         , mClient(client)
-        , mStart(EPOCH)
+        , mStart(start)
         , mNonBlock(nonBlock)
         , mEnd(CLOCK_MONOTONIC)
 { }
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index cb6f566..beaf646 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -45,7 +45,8 @@
 
 public:
     LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
-                 unsigned long tail, unsigned int logMask, pid_t pid);
+                 unsigned long tail, unsigned int logMask, pid_t pid,
+                 log_time start);
 
     SocketClient *mClient;
     static const struct timespec EPOCH;
diff --git a/toolbox/ls.c b/toolbox/ls.c
index 3cc5bb2..06910ee 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -137,7 +137,7 @@
 
     /* blocks are 512 bytes, we want output to be KB */
     if ((flags & LIST_SIZE) != 0) {
-        printf("%lld ", s->st_blocks / 2);
+        printf("%lld ", (long long)s->st_blocks / 2);
     }
 
     if ((flags & LIST_CLASSIFY) != 0) {
@@ -205,7 +205,7 @@
         break;
     case S_IFREG:
         printf("%s %-8s %-8s %8lld %s %s\n",
-               mode, user, group, s->st_size, date, name);
+               mode, user, group, (long long)s->st_size, date, name);
         break;
     case S_IFLNK: {
         char linkto[256];
@@ -321,7 +321,7 @@
     }
 
     if(flags & LIST_INODE) {
-        printf("%8llu ", s.st_ino);
+        printf("%8llu ", (unsigned long long)s.st_ino);
     }
 
     if ((flags & LIST_MACLABEL) != 0) {