Merge "Adding a handy predicate function (str_parms_has_key()"
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/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/liblog/logd_write.c b/liblog/logd_write.c
index aa0ad39..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,6 +157,11 @@
          */
         return 0;
     }
+
+    if (logd_fd < 0) {
+        return -EBADF;
+    }
+
     /*
      *  struct {
      *      // what we provide
@@ -155,8 +206,35 @@
         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 + header_length);
+    /*
+     * 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
 }
 
@@ -184,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
@@ -242,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;
     }
@@ -277,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/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 958948b..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")));
@@ -372,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)
@@ -398,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")));
@@ -443,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)
@@ -467,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",
@@ -520,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,29 +685,20 @@
     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));
-    if (strcmp(adjustment2, adjust)) {
-        fprintf(stderr, "ERROR: '%s' != '%s'\n", adjustment2, adjust);
-    }
-    ASSERT_STREQ(adjustment2, 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/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 9bf17e2..b8fe716 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -35,7 +35,7 @@
 /dev/uhid                 0660   system     net_bt_stack
 /dev/uinput               0660   system     net_bt_stack
 /dev/alarm                0664   system     radio
-/dev/rtc0                 0664   system     radio
+/dev/rtc0                 0640   system     system
 /dev/tty0                 0660   root       system
 /dev/graphics/*           0660   root       graphics
 /dev/msm_hw3dm            0660   system     graphics