Merge "Add C++ thread annotations support to core."
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index e5babed..41fe4b3 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -100,9 +100,9 @@
     errno = saved_errno_;
   }
 
-  // Allow this object to evaluate to false which is useful in macros.
+  // Allow this object to be used as part of && operation.
   operator bool() const {
-    return false;
+    return true;
   }
 
  private:
@@ -123,13 +123,11 @@
 // else statement after LOG() macro, it won't bind to the if statement in the macro.
 // do-while(0) statement doesn't work here. Because we need to support << operator
 // following the macro, like "LOG(DEBUG) << xxx;".
-#define LOG_TO(dest, severity)                                                      \
-  if (LIKELY(::android::base::severity < ::android::base::GetMinimumLogSeverity())) \
-    ;                                                                               \
-  else                                                                              \
-    ::android::base::ErrnoRestorer() ? *(std::ostream*)nullptr :                    \
-      ::android::base::LogMessage(__FILE__, __LINE__,                               \
-          ::android::base::dest,                                                    \
+#define LOG_TO(dest, severity)                                                        \
+  UNLIKELY(::android::base::severity >= ::android::base::GetMinimumLogSeverity()) &&  \
+    ::android::base::ErrnoRestorer() &&                                               \
+      ::android::base::LogMessage(__FILE__, __LINE__,                                 \
+          ::android::base::dest,                                                      \
           ::android::base::severity, -1).stream()
 
 // A variant of LOG that also logs the current errno value. To be used when
@@ -137,13 +135,11 @@
 #define PLOG(severity) PLOG_TO(DEFAULT, severity)
 
 // Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                                     \
-  if (LIKELY(::android::base::severity < ::android::base::GetMinimumLogSeverity())) \
-    ;                                                                               \
-  else                                                                              \
-    ::android::base::ErrnoRestorer() ? *(std::ostream*)nullptr :                    \
-      ::android::base::LogMessage(__FILE__, __LINE__,                               \
-          ::android::base::dest,                                                    \
+#define PLOG_TO(dest, severity)                                                      \
+  UNLIKELY(::android::base::severity >= ::android::base::GetMinimumLogSeverity()) && \
+    ::android::base::ErrnoRestorer() &&                                              \
+      ::android::base::LogMessage(__FILE__, __LINE__,                                \
+          ::android::base::dest,                                                     \
           ::android::base::severity, errno).stream()
 
 // Marker that code is yet to be implemented.
@@ -165,9 +161,7 @@
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
 #define CHECK(x)                                                              \
-  if (LIKELY((x)))                                                            \
-    ;                                                                         \
-  else                                                                        \
+  LIKELY((x)) ||                                                              \
     ABORT_AFTER_LOG_FATAL                                                     \
     ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                                 ::android::base::FATAL, -1).stream()          \
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
index c6349c1..6300941 100644
--- a/bootstat/Android.mk
+++ b/bootstat/Android.mk
@@ -20,37 +20,35 @@
 
 bootstat_lib_src_files := \
         boot_event_record_store.cpp \
-        event_log_list_builder.cpp
+        event_log_list_builder.cpp \
+        histogram_logger.cpp \
+        uptime_parser.cpp \
 
 bootstat_src_files := \
-        bootstat.cpp
+        bootstat.cpp \
 
 bootstat_test_src_files := \
         boot_event_record_store_test.cpp \
         event_log_list_builder_test.cpp \
-        testrunner.cpp
+        testrunner.cpp \
 
 bootstat_shared_libs := \
         libbase \
         libcutils \
-        liblog
+        liblog \
 
 bootstat_cflags := \
         -Wall \
         -Wextra \
-        -Werror
-
-bootstat_cppflags := \
-        -Wno-non-virtual-dtor
-
-bootstat_debug_cflags := \
-        $(bootstat_cflags) \
-        -UNDEBUG
+        -Werror \
 
 # 524291 corresponds to sysui_histogram, from
 # frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
 bootstat_cflags += -DHISTOGRAM_LOG_TAG=524291
 
+bootstat_debug_cflags := \
+        $(bootstat_cflags) \
+        -UNDEBUG \
 
 # bootstat static library
 # -----------------------------------------------------------------------------
@@ -59,7 +57,6 @@
 
 LOCAL_MODULE := libbootstat
 LOCAL_CFLAGS := $(bootstat_cflags)
-LOCAL_CPPFLAGS := $(bootstat_cppflags)
 LOCAL_C_INCLUDES := $(bootstat_c_includes)
 LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
 LOCAL_SRC_FILES := $(bootstat_lib_src_files)
@@ -75,7 +72,6 @@
 
 LOCAL_MODULE := libbootstat_debug
 LOCAL_CFLAGS := $(bootstat_cflags)
-LOCAL_CPPFLAGS := $(bootstat_debug_cppflags)
 LOCAL_C_INCLUDES := $(bootstat_c_includes)
 LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
 LOCAL_SRC_FILES := $(bootstat_lib_src_files)
@@ -91,7 +87,6 @@
 
 LOCAL_MODULE := libbootstat_host_debug
 LOCAL_CFLAGS := $(bootstat_debug_cflags)
-LOCAL_CPPFLAGS := $(bootstat_cppflags)
 LOCAL_C_INCLUDES := $(bootstat_c_includes)
 LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
 LOCAL_SRC_FILES := $(bootstat_lib_src_files)
@@ -107,7 +102,6 @@
 
 LOCAL_MODULE := bootstat
 LOCAL_CFLAGS := $(bootstat_cflags)
-LOCAL_CPPFLAGS := $(bootstat_cppflags)
 LOCAL_C_INCLUDES := $(bootstat_c_includes)
 LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
 LOCAL_STATIC_LIBRARIES := libbootstat
@@ -125,7 +119,6 @@
 
 LOCAL_MODULE := bootstat_tests
 LOCAL_CFLAGS := $(bootstat_tests_cflags)
-LOCAL_CPPFLAGS := $(bootstat_cppflags)
 LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
 LOCAL_STATIC_LIBRARIES := libbootstat_debug libgmock
 LOCAL_SRC_FILES := $(bootstat_test_src_files)
@@ -141,7 +134,6 @@
 
 LOCAL_MODULE := bootstat_tests
 LOCAL_CFLAGS := $(bootstat_tests_cflags)
-LOCAL_CPPFLAGS := $(bootstat_cppflags)
 LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
 LOCAL_STATIC_LIBRARIES := libbootstat_host_debug libgmock_host
 LOCAL_SRC_FILES := $(bootstat_test_src_files)
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 8282b40..5d1fae9 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -21,9 +21,12 @@
 #include <sys/stat.h>
 #include <utime.h>
 #include <cstdlib>
+#include <string>
 #include <utility>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include "histogram_logger.h"
+#include "uptime_parser.h"
 
 namespace {
 
@@ -41,6 +44,20 @@
   }
 
   *uptime = file_stat.st_mtime;
+
+  // The following code (till function exit) is a debug test to ensure the
+  // validity of the file mtime value, i.e., to check that the record file
+  // mtime values are not changed once set.
+  // TODO(jhawkins): Remove this code.
+  std::string content;
+  if (!android::base::ReadFileToString(path, &content)) {
+    PLOG(ERROR) << "Failed to read " << path;
+    return false;
+  }
+
+  int32_t value = std::stoi(content);
+  bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+
   return true;
 }
 
@@ -51,14 +68,7 @@
 }
 
 void BootEventRecordStore::AddBootEvent(const std::string& event) {
-  std::string uptime_str;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
-    LOG(ERROR) << "Failed to read /proc/uptime";
-  }
-
-  // Cast intentionally rounds down.
-  int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-  AddBootEventWithValue(event, uptime);
+  AddBootEventWithValue(event, bootstat::ParseUptime());
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
@@ -67,8 +77,20 @@
 void BootEventRecordStore::AddBootEventWithValue(
     const std::string& event, int32_t value) {
   std::string record_path = GetBootEventPath(event);
-  if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
+  int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR);
+  if (record_fd == -1) {
     PLOG(ERROR) << "Failed to create " << record_path;
+    return;
+  }
+
+  // Writing the value as content in the record file is a debug measure to
+  // ensure the validity of the file mtime value, i.e., to check that the record
+  // file mtime values are not changed once set.
+  // TODO(jhawkins): Remove this block.
+  if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) {
+    PLOG(ERROR) << "Failed to write value to " << record_path;
+    close(record_fd);
+    return;
   }
 
   // Fill out the stat structure for |record_path| in order to get the atime to
@@ -76,6 +98,8 @@
   struct stat file_stat;
   if (stat(record_path.c_str(), &file_stat) == -1) {
     PLOG(ERROR) << "Failed to read " << record_path;
+    close(record_fd);
+    return;
   }
 
   // Set the |modtime| of the file to store the value of the boot event while
@@ -83,7 +107,11 @@
   struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value};
   if (utime(record_path.c_str(), &times) == -1) {
     PLOG(ERROR) << "Failed to set mtime for " << record_path;
+    close(record_fd);
+    return;
   }
+
+  close(record_fd);
 }
 
 bool BootEventRecordStore::GetBootEvent(
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 0d7bbb0..343f9d0 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -25,6 +25,7 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
+#include "uptime_parser.h"
 
 using testing::UnorderedElementsAreArray;
 
@@ -38,17 +39,6 @@
   return (abs(a - b) <= FUZZ_SECONDS);
 }
 
-// Returns the uptime as read from /proc/uptime, rounded down to an integer.
-int32_t ReadUptime() {
-  std::string uptime_str;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
-    return -1;
-  }
-
-  // Cast to int to round down.
-  return static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-}
-
 // Recursively deletes the directory at |path|.
 void DeleteDirectory(const std::string& path) {
   typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;
@@ -110,7 +100,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  int32_t uptime = ReadUptime();
+  time_t uptime = bootstat::ParseUptime();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cenozoic");
@@ -125,7 +115,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  int32_t uptime = ReadUptime();
+  time_t uptime = bootstat::ParseUptime();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cretaceous");
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c59a191..2639d05 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -32,25 +32,11 @@
 #include <log/log.h>
 #include "boot_event_record_store.h"
 #include "event_log_list_builder.h"
+#include "histogram_logger.h"
+#include "uptime_parser.h"
 
 namespace {
 
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
-void LogBootEvent(const std::string& event, int32_t data) {
-  LOG(INFO) << "Logging boot metric: " << event << " " << data;
-
-  EventLogListBuilder log_builder;
-  log_builder.Append(event);
-  log_builder.Append(data);
-
-  std::unique_ptr<uint8_t[]> log;
-  size_t size;
-  log_builder.Release(&log, &size);
-
-  android_bWriteLog(HISTOGRAM_LOG_TAG, log.get(), size);
-}
-
 // Scans the boot event record store for record files and logs each boot event
 // via EventLog.
 void LogBootEvents() {
@@ -58,7 +44,22 @@
 
   auto events = boot_event_store.GetAllBootEvents();
   for (auto i = events.cbegin(); i != events.cend(); ++i) {
-    LogBootEvent(i->first, i->second);
+    bootstat::LogHistogram(i->first, i->second);
+  }
+}
+
+// Records the named boot |event| to the record store. If |value| is non-empty
+// and is a proper string representation of an integer value, the converted
+// integer value is associated with the boot event.
+void RecordBootEventFromCommandLine(
+    const std::string& event, const std::string& value_str) {
+  BootEventRecordStore boot_event_store;
+  if (!value_str.empty()) {
+    int32_t value = 0;
+    value = std::stoi(value_str);
+    boot_event_store.AddBootEventWithValue(event, value);
+  } else {
+    boot_event_store.AddBootEvent(event);
   }
 }
 
@@ -81,6 +82,7 @@
           "  -l, --log             Log all metrics to logstorage\n"
           "  -p, --print           Dump the boot event records to the console\n"
           "  -r, --record          Record the timestamp of a named boot event\n"
+          "  --value               Optional value to associate with the boot event\n"
           "  --record_boot_reason  Record the reason why the device booted\n"
           "  --record_time_since_factory_reset Record the time since the device was reset\n");
 }
@@ -174,6 +176,37 @@
   return kUnknownBootReason;
 }
 
+// Records several metrics related to the time it takes to boot the device,
+// including disambiguating boot time on encrypted or non-encrypted devices.
+void RecordBootComplete() {
+  BootEventRecordStore boot_event_store;
+  time_t uptime = bootstat::ParseUptime();
+
+  BootEventRecordStore::BootEventRecord record;
+
+  // post_decrypt_time_elapsed is only logged on encrypted devices.
+  if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
+    // Log the amount of time elapsed until the device is decrypted, which
+    // includes the variable amount of time the user takes to enter the
+    // decryption password.
+    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+
+    // Subtract the decryption time to normalize the boot cycle timing.
+    time_t boot_complete = uptime - record.second;
+    boot_event_store.AddBootEventWithValue("boot_complete_post_decrypt",
+                                           boot_complete);
+
+
+  } else {
+    boot_event_store.AddBootEventWithValue("boot_complete_no_encryption",
+                                           uptime);
+  }
+
+  // Record the total time from device startup to boot complete, regardless of
+  // encryption state.
+  boot_event_store.AddBootEventWithValue("boot_complete", uptime);
+}
+
 // Records the boot_reason metric by querying the ro.boot.bootreason system
 // property.
 void RecordBootReason() {
@@ -194,10 +227,10 @@
   static const char* factory_reset_current_time = "factory_reset_current_time";
   if (current_time_utc < 0) {
     // UMA does not display negative values in buckets, so convert to positive.
-    LogBootEvent(factory_reset_current_time, std::abs(current_time_utc));
+    bootstat::LogHistogram(factory_reset_current_time, std::abs(current_time_utc));
     return;
   } else {
-    LogBootEvent(factory_reset_current_time, current_time_utc);
+    bootstat::LogHistogram(factory_reset_current_time, current_time_utc);
   }
 
   // The factory_reset boot event does not exist after the device is reset, so
@@ -213,7 +246,7 @@
   // Calculate and record the difference in time between now and the
   // factory_reset time.
   time_t factory_reset_utc = record.second;
-  LogBootEvent("factory_reset_record_value", factory_reset_utc);
+  bootstat::LogHistogram("factory_reset_record_value", factory_reset_utc);
   time_t time_since_factory_reset = difftime(current_time_utc,
                                              factory_reset_utc);
   boot_event_store.AddBootEventWithValue("time_since_factory_reset",
@@ -229,6 +262,8 @@
   LOG(INFO) << "Service started: " << cmd_line;
 
   int option_index = 0;
+  static const char value_str[] = "value";
+  static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
@@ -236,18 +271,28 @@
     { "log",             no_argument,       NULL,   'l' },
     { "print",           no_argument,       NULL,   'p' },
     { "record",          required_argument, NULL,   'r' },
+    { value_str,         required_argument, NULL,   0 },
+    { boot_complete_str, no_argument,       NULL,   0 },
     { boot_reason_str,   no_argument,       NULL,   0 },
     { factory_reset_str, no_argument,       NULL,   0 },
     { NULL,              0,                 NULL,   0 }
   };
 
+  std::string boot_event;
+  std::string value;
   int opt = 0;
   while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) {
     switch (opt) {
       // This case handles long options which have no single-character mapping.
       case 0: {
         const std::string option_name = long_options[option_index].name;
-        if (option_name == boot_reason_str) {
+        if (option_name == value_str) {
+          // |optarg| is an external variable set by getopt representing
+          // the option argument.
+          value = optarg;
+        } else if (option_name == boot_complete_str) {
+          RecordBootComplete();
+        } else if (option_name == boot_reason_str) {
           RecordBootReason();
         } else if (option_name == factory_reset_str) {
           RecordFactoryReset();
@@ -275,10 +320,7 @@
       case 'r': {
         // |optarg| is an external variable set by getopt representing
         // the option argument.
-        const char* event = optarg;
-
-        BootEventRecordStore boot_event_store;
-        boot_event_store.AddBootEvent(event);
+        boot_event = optarg;
         break;
       }
 
@@ -294,5 +336,9 @@
     }
   }
 
+  if (!boot_event.empty()) {
+    RecordBootEventFromCommandLine(boot_event, value);
+  }
+
   return 0;
 }
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 3c20fc8..ba8f81c 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -3,6 +3,14 @@
 on post-fs-data
     mkdir /data/misc/bootstat 0700 root root
 
+# Record the time at which the user has successfully entered the pin to decrypt
+# the device, /data is decrypted, and the system is entering the main boot phase.
+#
+# post-fs-data: /data is writable
+# property:init.svc.bootanim=running: The boot animation is running
+on post-fs-data && property:init.svc.bootanim=running
+    exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+
 # The first marker, boot animation stopped, is considered the point at which
 # the user may interact with the device, so it is a good proxy for the boot
 # complete signal.
@@ -10,8 +18,8 @@
 # The second marker ensures an encrypted device is decrypted before logging
 # boot time data.
 on property:init.svc.bootanim=stopped && property:vold.decrypt=trigger_restart_framework
-    # Record boot_complete timing event.
-    exec - root root -- /system/bin/bootstat -r boot_complete
+    # Record boot_complete and related stats (decryption, etc).
+    exec - root root -- /system/bin/bootstat --record_boot_complete
 
     # Record the boot reason.
     exec - root root -- /system/bin/bootstat --record_boot_reason
diff --git a/bootstat/histogram_logger.cpp b/bootstat/histogram_logger.cpp
new file mode 100644
index 0000000..e3aad28
--- /dev/null
+++ b/bootstat/histogram_logger.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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 "histogram_logger.h"
+
+#include <cstdlib>
+#include <memory>
+#include <android-base/logging.h>
+#include <log/log.h>
+#include "event_log_list_builder.h"
+
+namespace bootstat {
+
+void LogHistogram(const std::string& event, int32_t data) {
+  LOG(INFO) << "Logging histogram: " << event << " " << data;
+
+  EventLogListBuilder log_builder;
+  log_builder.Append(event);
+  log_builder.Append(data);
+
+  std::unique_ptr<uint8_t[]> log;
+  size_t size;
+  log_builder.Release(&log, &size);
+
+  android_bWriteLog(HISTOGRAM_LOG_TAG, log.get(), size);
+}
+
+}  // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/histogram_logger.h b/bootstat/histogram_logger.h
new file mode 100644
index 0000000..60c7776
--- /dev/null
+++ b/bootstat/histogram_logger.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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 <cstdint>
+#include <string>
+
+namespace bootstat {
+
+// Builds an EventLog buffer named |event| containing |data| and writes
+// the log into the Tron histogram logs.
+void LogHistogram(const std::string& event, int32_t data);
+
+}  // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
new file mode 100644
index 0000000..7c2034c
--- /dev/null
+++ b/bootstat/uptime_parser.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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 "uptime_parser.h"
+
+#include <time.h>
+#include <cstdlib>
+#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace bootstat {
+
+time_t ParseUptime() {
+  std::string uptime_str;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+    PLOG(ERROR) << "Failed to read /proc/uptime";
+    return -1;
+  }
+
+  // Cast intentionally rounds down.
+  return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+}  // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.h b/bootstat/uptime_parser.h
new file mode 100644
index 0000000..756ae9b
--- /dev/null
+++ b/bootstat/uptime_parser.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UPTIME_PARSER_H_
+#define UPTIME_PARSER_H_
+
+#include <time.h>
+
+namespace bootstat {
+
+// Returns the number of seconds the system has been on since reboot.
+time_t ParseUptime();
+
+}  // namespace bootstat
+
+#endif  // UPTIME_PARSER_H_
\ No newline at end of file
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 9e4f1f7..6469db4 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -15,6 +15,7 @@
     debuggerd.cpp \
     elf_utils.cpp \
     getevent.cpp \
+    signal_sender.cpp \
     tombstone.cpp \
     utility.cpp \
 
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b191954..97f4096 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -15,21 +15,20 @@
  */
 
 #include <dirent.h>
+#include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <elf.h>
 #include <sys/poll.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/wait.h>
+#include <time.h>
 
 #include <set>
 
@@ -48,6 +47,7 @@
 
 #include "backtrace.h"
 #include "getevent.h"
+#include "signal_sender.h"
 #include "tombstone.h"
 #include "utility.h"
 
@@ -248,8 +248,8 @@
   return 0;
 }
 
-static bool should_attach_gdb(debugger_request_t* request) {
-  if (request->action == DEBUGGER_ACTION_CRASH) {
+static bool should_attach_gdb(const debugger_request_t& request) {
+  if (request.action == DEBUGGER_ACTION_CRASH) {
     return property_get_bool("debug.debuggerd.wait_for_gdb", false);
   }
   return false;
@@ -374,7 +374,8 @@
 }
 
 static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
-                         BacktraceMap* backtrace_map, const std::set<pid_t>& siblings) {
+                         BacktraceMap* backtrace_map, const std::set<pid_t>& siblings,
+                         int* crash_signal) {
   if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
     ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
     return false;
@@ -416,11 +417,7 @@
 #endif
       case SIGTRAP:
         ALOGV("stopped -- fatal signal\n");
-        // Send a SIGSTOP to the process to make all of
-        // the non-signaled threads stop moving.  Without
-        // this we get a lot of "ptrace detach failed:
-        // No such process".
-        kill(request.pid, SIGSTOP);
+        *crash_signal = signal;
         engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
                           request.original_si_code, request.abort_msg_address);
         break;
@@ -449,99 +446,7 @@
   return true;
 }
 
-// Fork a process that listens for signals to send, or 0, to exit.
-static bool fork_signal_sender(int* out_fd, pid_t* sender_pid, pid_t target_pid) {
-  int sfd[2];
-  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sfd) != 0) {
-    ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
-    return false;
-  } else if (fork_pid == 0) {
-    close(sfd[1]);
-
-    while (true) {
-      int signal;
-      int rc = TEMP_FAILURE_RETRY(read(sfd[0], &signal, sizeof(signal)));
-      if (rc < 0) {
-        ALOGE("debuggerd: signal sender failed to read from socket");
-        kill(target_pid, SIGKILL);
-        exit(1);
-      } else if (rc != sizeof(signal)) {
-        ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
-        kill(target_pid, SIGKILL);
-        exit(1);
-      }
-
-      // Report success after sending a signal, or before exiting.
-      int err = 0;
-      if (signal != 0) {
-        if (kill(target_pid, signal) != 0) {
-          err = errno;
-        }
-      }
-
-      if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
-        ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
-        kill(target_pid, SIGKILL);
-        exit(1);
-      }
-
-      if (signal == 0) {
-        exit(0);
-      }
-    }
-  } else {
-    close(sfd[0]);
-    *out_fd = sfd[1];
-    *sender_pid = fork_pid;
-    return true;
-  }
-}
-
-static void handle_request(int fd) {
-  ALOGV("handle_request(%d)\n", fd);
-
-  ScopedFd closer(fd);
-  debugger_request_t request;
-  memset(&request, 0, sizeof(request));
-  int status = read_request(fd, &request);
-  if (status != 0) {
-    return;
-  }
-
-  ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid);
-
-#if defined(__LP64__)
-  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
-  // to the 64 bit debuggerd. If the process is a 32 bit executable,
-  // redirect the request to the 32 bit debuggerd.
-  if (is32bit(request.tid)) {
-    // Only dump backtrace and dump tombstone requests can be redirected.
-    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
-        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-      redirect_to_32(fd, &request);
-    } else {
-      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
-    }
-    return;
-  }
-#endif
-
-  // Fork a child to handle the rest of the request.
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
-    return;
-  } else if (fork_pid != 0) {
-    waitpid(fork_pid, nullptr, 0);
-    return;
-  }
-
+static void worker_process(int fd, debugger_request_t& request) {
   // Open the tombstone file if we need it.
   std::string tombstone_path;
   int tombstone_fd = -1;
@@ -582,16 +487,7 @@
 
   // Don't attach to the sibling threads if we want to attach gdb.
   // Supposedly, it makes the process less reliable.
-  bool attach_gdb = should_attach_gdb(&request);
-  int signal_fd = -1;
-  pid_t signal_pid = 0;
-
-  // Fork a process that stays root, and listens on a pipe to pause and resume the target.
-  if (!fork_signal_sender(&signal_fd, &signal_pid, request.pid)) {
-    ALOGE("debuggerd: failed to fork signal sender");
-    exit(1);
-  }
-
+  bool attach_gdb = should_attach_gdb(request);
   if (attach_gdb) {
     // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
     if (init_getevent() != 0) {
@@ -601,21 +497,6 @@
 
   }
 
-  auto send_signal = [=](int signal) {
-    int error;
-    if (TEMP_FAILURE_RETRY(write(signal_fd, &signal, sizeof(signal))) < 0) {
-      ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno));
-      return false;
-    } else if (TEMP_FAILURE_RETRY(read(signal_fd, &error, sizeof(error))) < 0) {
-      ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno));
-      return false;
-    } else if (error != 0) {
-      errno = error;
-      return false;
-    }
-    return true;
-  };
-
   std::set<pid_t> siblings;
   if (!attach_gdb) {
     ptrace_siblings(request.pid, request.tid, siblings);
@@ -632,7 +513,8 @@
     _exit(1);
   }
 
-  succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings);
+  int crash_signal = SIGKILL;
+  succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings, &crash_signal);
   if (succeeded) {
     if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
       if (!tombstone_path.empty()) {
@@ -643,7 +525,7 @@
 
   if (attach_gdb) {
     // Tell the signal process to send SIGSTOP to the target.
-    if (!send_signal(SIGSTOP)) {
+    if (!send_signal(request.pid, 0, SIGSTOP)) {
       ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
       attach_gdb = false;
     }
@@ -659,9 +541,7 @@
 
   // Send the signal back to the process if it crashed and we're not waiting for gdb.
   if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
-    // TODO: Send the same signal that triggered the dump, so that shell says "Segmentation fault"
-    //       instead of "Killed"?
-    if (!send_signal(SIGKILL)) {
+    if (!send_signal(request.pid, request.tid, crash_signal)) {
       ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
     }
   }
@@ -671,21 +551,125 @@
     wait_for_user_action(request);
 
     // Tell the signal process to send SIGCONT to the target.
-    if (!send_signal(SIGCONT)) {
+    if (!send_signal(request.pid, 0, SIGCONT)) {
       ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
     }
 
     uninit_getevent();
   }
 
-  if (!send_signal(0)) {
-    ALOGE("debuggerd: failed to notify signal sender to finish");
-    kill(signal_pid, SIGKILL);
-  }
-  waitpid(signal_pid, nullptr, 0);
   exit(!succeeded);
 }
 
+static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
+  struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
+  if (should_attach_gdb(request)) {
+    // If wait_for_gdb is enabled, set the timeout to something large.
+    timeout.tv_sec = INT_MAX;
+  }
+
+  sigset_t signal_set;
+  sigemptyset(&signal_set);
+  sigaddset(&signal_set, SIGCHLD);
+
+  bool kill_worker = false;
+  bool kill_target = false;
+  bool kill_self = false;
+
+  int status;
+  siginfo_t siginfo;
+  int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
+  if (signal == SIGCHLD) {
+    pid_t rc = waitpid(0, &status, WNOHANG | WUNTRACED);
+    if (rc != child_pid) {
+      ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
+      kill_worker = true;
+      kill_target = true;
+      kill_self = true;
+    }
+
+    if (WIFSIGNALED(status)) {
+      ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
+      kill_worker = false;
+      kill_target = true;
+    } else if (WIFSTOPPED(status)) {
+      ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
+      kill_worker = true;
+      kill_target = true;
+    }
+  } else {
+    ALOGE("debuggerd: worker process %d timed out", child_pid);
+    kill_worker = true;
+    kill_target = true;
+  }
+
+  if (kill_worker) {
+    // Something bad happened, kill the worker.
+    if (kill(child_pid, SIGKILL) != 0) {
+      ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
+    } else {
+      waitpid(child_pid, &status, 0);
+    }
+  }
+
+  if (kill_target) {
+    // Resume or kill the target, depending on what the initial request was.
+    if (request.action == DEBUGGER_ACTION_CRASH) {
+      ALOGE("debuggerd: killing target %d", request.pid);
+      kill(request.pid, SIGKILL);
+    } else {
+      ALOGE("debuggerd: resuming target %d", request.pid);
+      kill(request.pid, SIGCONT);
+    }
+  }
+
+  if (kill_self) {
+    stop_signal_sender();
+    _exit(1);
+  }
+}
+
+static void handle_request(int fd) {
+  ALOGV("handle_request(%d)\n", fd);
+
+  ScopedFd closer(fd);
+  debugger_request_t request;
+  memset(&request, 0, sizeof(request));
+  int status = read_request(fd, &request);
+  if (status != 0) {
+    return;
+  }
+
+  ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
+        request.gid, request.tid);
+
+#if defined(__LP64__)
+  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
+  // to the 64 bit debuggerd. If the process is a 32 bit executable,
+  // redirect the request to the 32 bit debuggerd.
+  if (is32bit(request.tid)) {
+    // Only dump backtrace and dump tombstone requests can be redirected.
+    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
+        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+      redirect_to_32(fd, &request);
+    } else {
+      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
+    }
+    return;
+  }
+#endif
+
+  // Fork a child to handle the rest of the request.
+  pid_t fork_pid = fork();
+  if (fork_pid == -1) {
+    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
+  } else if (fork_pid == 0) {
+    worker_process(fd, request);
+  } else {
+    monitor_worker_process(fork_pid, request);
+  }
+}
+
 static int do_server() {
   // debuggerd crashes can't be reported to debuggerd.
   // Reset all of the crash handlers.
@@ -702,17 +686,22 @@
   // Ignore failed writes to closed sockets
   signal(SIGPIPE, SIG_IGN);
 
-  struct sigaction act;
-  act.sa_handler = SIG_DFL;
-  sigemptyset(&act.sa_mask);
-  sigaddset(&act.sa_mask,SIGCHLD);
-  act.sa_flags = SA_NOCLDWAIT;
-  sigaction(SIGCHLD, &act, 0);
+  // Block SIGCHLD so we can sigtimedwait for it.
+  sigset_t sigchld;
+  sigemptyset(&sigchld);
+  sigaddset(&sigchld, SIGCHLD);
+  sigprocmask(SIG_SETMASK, &sigchld, nullptr);
 
   int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
                               SOCK_STREAM | SOCK_CLOEXEC);
   if (s == -1) return 1;
 
+  // Fork a process that stays root, and listens on a pipe to pause and resume the target.
+  if (!start_signal_sender()) {
+    ALOGE("debuggerd: failed to fork signal sender");
+    return 1;
+  }
+
   ALOGI("debuggerd: starting\n");
 
   for (;;) {
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
new file mode 100644
index 0000000..1cfb704
--- /dev/null
+++ b/debuggerd/signal_sender.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+
+#include "signal_sender.h"
+
+static int signal_fd = -1;
+static pid_t signal_pid;
+struct signal_message {
+  pid_t pid;
+  pid_t tid;
+  int signal;
+};
+
+// Fork a process to send signals for the worker processes to use after they've dropped privileges.
+bool start_signal_sender() {
+  if (signal_pid != 0) {
+    ALOGE("debuggerd: attempted to start signal sender multiple times");
+    return false;
+  }
+
+  int sfd[2];
+  if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
+    ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
+    return false;
+  }
+
+  pid_t parent = getpid();
+  pid_t fork_pid = fork();
+  if (fork_pid == -1) {
+    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
+    return false;
+  } else if (fork_pid == 0) {
+    close(sfd[1]);
+
+    while (true) {
+      signal_message msg;
+      int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
+      if (rc < 0) {
+        ALOGE("debuggerd: signal sender failed to read from socket");
+        break;
+      } else if (rc != sizeof(msg)) {
+        ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
+        break;
+      }
+
+      // Report success after sending a signal
+      int err = 0;
+      if (msg.tid > 0) {
+        if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
+          err = errno;
+        }
+      } else {
+        if (kill(msg.pid, msg.signal) != 0) {
+          err = errno;
+        }
+      }
+
+      if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
+        ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
+      }
+    }
+
+    // Our parent proably died, but if not, kill them.
+    if (getppid() == parent) {
+      kill(parent, SIGKILL);
+    }
+    _exit(1);
+  } else {
+    close(sfd[0]);
+    signal_fd = sfd[1];
+    signal_pid = fork_pid;
+    return true;
+  }
+}
+
+bool stop_signal_sender() {
+  if (signal_pid <= 0) {
+    return false;
+  }
+
+  if (kill(signal_pid, SIGKILL) != 0) {
+    ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
+    return false;
+  }
+
+  close(signal_fd);
+  signal_fd = -1;
+
+  int status;
+  waitpid(signal_pid, &status, 0);
+  signal_pid = 0;
+
+  return true;
+}
+
+bool send_signal(pid_t pid, pid_t tid, int signal) {
+  if (signal_fd == -1) {
+    ALOGE("debuggerd: attempted to send signal before signal sender was started");
+    errno = EHOSTUNREACH;
+    return false;
+  }
+
+  signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
+  if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
+    ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
+    errno = EHOSTUNREACH;
+    return false;
+  }
+
+  int response;
+  ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
+  if (rc == 0) {
+    ALOGE("debuggerd: received EOF from signal sender");
+    errno = EHOSTUNREACH;
+    return false;
+  } else if (rc < 0) {
+    ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
+    errno = EHOSTUNREACH;
+    return false;
+  }
+
+  if (response == 0) {
+    return true;
+  }
+
+  errno = response;
+  return false;
+}
diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h
new file mode 100644
index 0000000..0443272
--- /dev/null
+++ b/debuggerd/signal_sender.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_SIGNAL_SENDER_H
+#define _DEBUGGERD_SIGNAL_SENDER_H
+
+#include <sys/types.h>
+
+bool start_signal_sender();
+bool stop_signal_sender();
+
+// Sends a signal to a target process or thread.
+// If tid is greater than zero, this performs tgkill(pid, tid, signal).
+// Otherwise, it performs kill(pid, signal).
+bool send_signal(pid_t pid, pid_t tid, int signal);
+
+#endif
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
index 04238a6..c3ea1ed 100644
--- a/include/private/android_logger.h
+++ b/include/private/android_logger.h
@@ -19,7 +19,10 @@
 #ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
 #define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
 
+/* Android private interfaces */
+
 #include <stdint.h>
+#include <sys/types.h>
 
 #include <log/log.h>
 #include <log/log_read.h>
@@ -95,4 +98,35 @@
     char data[];
 } android_log_event_string_t;
 
+#if defined(__cplusplus)
+extern "C" {
 #endif
+
+#define ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE 256 /* 1MB file */
+#define ANDROID_LOG_PMSG_FILE_SEQUENCE     1000
+
+ssize_t __android_log_pmsg_file_write(
+        log_id_t logId,
+        char prio,
+        const char *filename,
+        const char *buf, size_t len);
+
+#define LOG_ID_ANY      ((log_id_t)-1)
+#define ANDROID_LOG_ANY ANDROID_LOG_UNKNOWN
+
+/* first 5 arguments match __android_log_msg_file_write, a cast is safe */
+typedef ssize_t (*__android_log_pmsg_file_read_fn)(
+        log_id_t logId,
+        char prio,
+        const char *filename,
+        const char *buf, size_t len, void *arg);
+
+ssize_t __android_log_pmsg_file_read(
+        log_id_t logId, char prio, const char *prefix,
+        __android_log_pmsg_file_read_fn fn, void *arg);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */
diff --git a/init/init.cpp b/init/init.cpp
index bac27df..69f0595 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,6 +18,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fstream>
 #include <libgen.h>
 #include <paths.h>
 #include <signal.h>
@@ -74,8 +75,7 @@
 
 static char qemu[32];
 
-int have_console;
-std::string console_name = "/dev/console";
+std::string default_console = "/dev/console";
 static time_t process_needs_restart;
 
 const char *ENV[32];
@@ -290,6 +290,100 @@
     return result;
 }
 
+static void security_failure() {
+    ERROR("Security failure; rebooting into recovery mode...\n");
+    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+    while (true) { pause(); }  // never reached
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+/* __attribute__((unused)) due to lack of mips support: see mips block
+ * in set_mmap_rnd_bits_action */
+static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
+    std::string path;
+    if (compat) {
+        path = MMAP_RND_COMPAT_PATH;
+    } else {
+        path = MMAP_RND_PATH;
+    }
+    std::ifstream inf(path, std::fstream::in);
+    if (!inf) {
+        return false;
+    }
+    while (start >= min) {
+        // try to write out new value
+        std::string str_val = std::to_string(start);
+        std::ofstream of(path, std::fstream::out);
+        if (!of) {
+            return false;
+        }
+        of << str_val << std::endl;
+        of.close();
+
+        // check to make sure it was recorded
+        inf.seekg(0);
+        std::string str_rec;
+        inf >> str_rec;
+        if (str_val.compare(str_rec) == 0) {
+            break;
+        }
+        start--;
+    }
+    inf.close();
+    return (start >= min);
+}
+
+/*
+ * Set /proc/sys/vm/mmap_rnd_bits and potentially
+ * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+ * Returns -1 if unable to set these to an acceptable value.  Apply
+ * upstream patch-sets https://lkml.org/lkml/2015/12/21/337 and
+ * https://lkml.org/lkml/2016/2/4/831 to enable this.
+ */
+static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
+{
+    int ret = -1;
+
+    /* values are arch-dependent */
+#if defined(__aarch64__)
+    /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
+    if (set_mmap_rnd_bits_min(33, 24, false)
+            && set_mmap_rnd_bits_min(16, 16, true)) {
+        ret = 0;
+    }
+#elif defined(__x86_64__)
+    /* x86_64 supports 28 - 32 bits */
+    if (set_mmap_rnd_bits_min(32, 32, false)
+            && set_mmap_rnd_bits_min(16, 16, true)) {
+        ret = 0;
+    }
+#elif defined(__arm__) || defined(__i386__)
+    /* check to see if we're running on 64-bit kernel */
+    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+    /* supported 32-bit architecture must have 16 bits set */
+    if (set_mmap_rnd_bits_min(16, 16, h64)) {
+        ret = 0;
+    }
+#elif defined(__mips__) || defined(__mips64__)
+    // TODO: add mips support b/27788820
+    ret = 0;
+#else
+    ERROR("Unknown architecture\n");
+#endif
+
+#ifdef __BRILLO__
+    // TODO: b/27794137
+    ret = 0;
+#endif
+    if (ret == -1) {
+        ERROR("Unable to set adequate mmap entropy value!\n");
+        security_failure();
+    }
+    return ret;
+}
+
 static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
@@ -300,36 +394,8 @@
 {
     std::string console = property_get("ro.boot.console");
     if (!console.empty()) {
-        console_name = "/dev/" + console;
+        default_console = "/dev/" + console;
     }
-
-    int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
-    if (fd >= 0)
-        have_console = 1;
-    close(fd);
-
-    fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        const char *msg;
-            msg = "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"  // console is 40 cols x 30 lines
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "             A N D R O I D ";
-        write(fd, msg, strlen(msg));
-        close(fd);
-    }
-
     return 0;
 }
 
@@ -474,12 +540,6 @@
     return 0;
 }
 
-static void security_failure() {
-    ERROR("Security failure; rebooting into recovery mode...\n");
-    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-    while (true) { pause(); }  // never reached
-}
-
 static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
@@ -629,6 +689,7 @@
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
diff --git a/init/init.h b/init/init.h
index b6a095b..0019337 100644
--- a/init/init.h
+++ b/init/init.h
@@ -26,8 +26,7 @@
 
 extern const char *ENV[32];
 extern bool waiting_for_exec;
-extern int have_console;
-extern std::string console_name;
+extern std::string default_console;
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 
diff --git a/init/readme.txt b/init/readme.txt
index ef85ccf..aa372eb 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -115,6 +115,13 @@
 Options are modifiers to services.  They affect how and when init
 runs the service.
 
+console [<console>]
+  This service needs a console. The optional second parameter chooses a
+  specific console instead of the default. The default "/dev/console" can
+  be changed by setting the "androidboot.console" kernel parameter. In
+  all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
+  specified as just "console tty0".
+
 critical
   This is a device-critical service. If it exits more than four times in
   four minutes, the device will reboot into recovery mode.
diff --git a/init/service.cpp b/init/service.cpp
index bdecc32..e509f46 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -172,6 +172,7 @@
 
 bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
     flags_ |= SVC_CONSOLE;
+    console_ = args.size() > 1 ? "/dev/" + args[1] : "";
     return true;
 }
 
@@ -282,7 +283,7 @@
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     static const Map option_handlers = {
         {"class",       {1,     1,    &Service::HandleClass}},
-        {"console",     {0,     0,    &Service::HandleConsole}},
+        {"console",     {0,     1,    &Service::HandleConsole}},
         {"critical",    {0,     0,    &Service::HandleCritical}},
         {"disabled",    {0,     0,    &Service::HandleDisabled}},
         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
@@ -329,10 +330,18 @@
     }
 
     bool needs_console = (flags_ & SVC_CONSOLE);
-    if (needs_console && !have_console) {
-        ERROR("service '%s' requires console\n", name_.c_str());
-        flags_ |= SVC_DISABLED;
-        return false;
+    if (needs_console) {
+        if (console_.empty()) {
+            console_ = default_console;
+        }
+
+        bool have_console = (open(console_.c_str(), O_RDWR | O_CLOEXEC) != -1);
+        if (!have_console) {
+            ERROR("service '%s' couldn't open console '%s': %s\n",
+                  name_.c_str(), console_.c_str(), strerror(errno));
+            flags_ |= SVC_DISABLED;
+            return false;
+        }
     }
 
     struct stat sb;
@@ -606,10 +615,8 @@
 }
 
 void Service::OpenConsole() const {
-    int fd;
-    if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
-        fd = open("/dev/null", O_RDWR);
-    }
+    int fd = open(console_.c_str(), O_RDWR);
+    if (fd == -1) fd = open("/dev/null", O_RDWR);
     ioctl(fd, TIOCSCTTY, 0);
     dup2(fd, 0);
     dup2(fd, 1);
diff --git a/init/service.h b/init/service.h
index 35abde9..b003ca0 100644
--- a/init/service.h
+++ b/init/service.h
@@ -129,6 +129,7 @@
 
     std::string name_;
     std::string classname_;
+    std::string console_;
 
     unsigned flags_;
     pid_t pid_;
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index ac14046..dc3ed67 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -564,7 +564,7 @@
   uint64_t eh_frame_vaddr = 0;
   std::vector<uint8_t> eh_frame_data;
 
-  for (auto it = elf->begin_sections(); it != elf->end_sections(); ++it) {
+  for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
     llvm::ErrorOr<llvm::StringRef> name = elf->getSectionName(&*it);
     if (name) {
       if (name.get() == ".debug_frame") {
@@ -603,7 +603,7 @@
   }
 
   std::vector<ProgramHeader> program_headers;
-  for (auto it = elf->begin_program_headers(); it != elf->end_program_headers(); ++it) {
+  for (auto it = elf->program_header_begin(); it != elf->program_header_end(); ++it) {
     ProgramHeader header;
     header.vaddr = it->p_vaddr;
     header.file_offset = it->p_offset;
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 607d667..59c696b 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,20 +15,29 @@
 //
 
 liblog_sources = [
-    "logd_write.c",
     "log_event_list.c",
     "log_event_write.c",
+    "logger_write.c",
+    "config_write.c",
+    "logger_name.c",
+    "logger_lock.c",
 ]
 liblog_host_sources = [
     "fake_log_device.c",
     //"event.logtags",
+    "fake_writer.c",
 ]
 liblog_target_sources = [
     "event_tag_map.c",
+    "config_read.c",
     "log_time.cpp",
     "log_is_loggable.c",
     "logprint.c",
-    "log_read.c",
+    "pmsg_reader.c",
+    "pmsg_writer.c",
+    "logd_reader.c",
+    "logd_writer.c",
+    "logger_read.c",
 ]
 
 // Shared and static library for host and device
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 6d53a4a..01c8e77 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,10 +24,14 @@
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-liblog_sources := logd_write.c log_event_list.c log_event_write.c
+liblog_sources := log_event_list.c log_event_write.c logger_write.c
+liblog_sources += config_write.c logger_name.c logger_lock.c
 liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
+liblog_host_sources += fake_writer.c
 liblog_target_sources := $(liblog_sources) event_tag_map.c
-liblog_target_sources += log_time.cpp log_is_loggable.c logprint.c log_read.c
+liblog_target_sources += config_read.c log_time.cpp log_is_loggable.c logprint.c
+liblog_target_sources += pmsg_reader.c pmsg_writer.c
+liblog_target_sources += logd_reader.c logd_writer.c logger_read.c
 
 # Shared and static library for host
 # ========================================================
diff --git a/liblog/config_read.c b/liblog/config_read.c
new file mode 100644
index 0000000..1f54152
--- /dev/null
+++ b/liblog/config_read.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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 "config_read.h"
+#include "logger.h"
+
+LIBLOG_HIDDEN struct listnode __android_log_transport_read =
+    { &__android_log_transport_read, &__android_log_transport_read };
+LIBLOG_HIDDEN struct listnode __android_log_persist_read =
+    { &__android_log_persist_read, &__android_log_persist_read };
+
+static void __android_log_add_transport(
+        struct listnode *list, struct android_log_transport_read *transport) {
+    size_t i;
+
+    /* Try to keep one functioning transport for each log buffer id */
+    for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
+        struct android_log_transport_read *transp;
+
+        if (list_empty(list)) {
+            if (!transport->available || ((*transport->available)(i) >= 0)) {
+                list_add_tail(list, &transport->node);
+                return;
+            }
+        } else {
+            read_transport_for_each(transp, list) {
+                if (!transp->available) {
+                    return;
+                }
+                if (((*transp->available)(i) < 0) &&
+                        (!transport->available ||
+                            ((*transport->available)(i) >= 0))) {
+                    list_add_tail(list, &transport->node);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+LIBLOG_HIDDEN void __android_log_config_read() {
+#if (FAKE_LOG_DEVICE == 0)
+    extern struct android_log_transport_read logdLoggerRead;
+    extern struct android_log_transport_read pmsgLoggerRead;
+
+    __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
+    __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
+#endif
+}
diff --git a/liblog/config_read.h b/liblog/config_read.h
new file mode 100644
index 0000000..67f4c20
--- /dev/null
+++ b/liblog/config_read.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_CONFIG_READ_H__
+#define _LIBLOG_CONFIG_READ_H__
+
+#include <cutils/list.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+extern LIBLOG_HIDDEN struct listnode __android_log_transport_read;
+extern LIBLOG_HIDDEN struct listnode __android_log_persist_read;
+
+#define read_transport_for_each(transp, transports)                         \
+    for (transp = node_to_item((transports)->next,                          \
+                               struct android_log_transport_read, node);    \
+         (transp != node_to_item(transports,                                \
+                                 struct android_log_transport_read, node)); \
+         transp = node_to_item(transp->node.next,                           \
+                               struct android_log_transport_read, node))    \
+
+#define read_transport_for_each_safe(transp, n, transports)                 \
+    for (transp = node_to_item((transports)->next,                          \
+                               struct android_log_transport_read, node),    \
+         n = transp->node.next;                                             \
+         (transp != node_to_item(transports,                                \
+                                 struct android_log_transport_read, node)); \
+         transp = node_to_item(n, struct android_log_transport_read, node), \
+         n = transp->node.next)
+
+LIBLOG_HIDDEN void __android_log_config_read();
+
+__END_DECLS
+
+#endif /* _LIBLOG_CONFIG_READ_H__ */
diff --git a/liblog/config_write.c b/liblog/config_write.c
new file mode 100644
index 0000000..d689f63
--- /dev/null
+++ b/liblog/config_write.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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 "config_write.h"
+#include "logger.h"
+
+LIBLOG_HIDDEN struct listnode __android_log_transport_write =
+    { &__android_log_transport_write, &__android_log_transport_write };
+LIBLOG_HIDDEN struct listnode __android_log_persist_write =
+    { &__android_log_persist_write, &__android_log_persist_write};
+
+static void __android_log_add_transport(
+        struct listnode *list, struct android_log_transport_write *transport) {
+    size_t i;
+
+    /* Try to keep one functioning transport for each log buffer id */
+    for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
+        struct android_log_transport_write *transp;
+
+        if (list_empty(list)) {
+            if (!transport->available || ((*transport->available)(i) >= 0)) {
+                list_add_tail(list, &transport->node);
+                return;
+            }
+        } else {
+            write_transport_for_each(transp, list) {
+                if (!transp->available) {
+                    return;
+                }
+                if (((*transp->available)(i) < 0) &&
+                        (!transport->available ||
+                            ((*transport->available)(i) >= 0))) {
+                    list_add_tail(list, &transport->node);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+LIBLOG_HIDDEN void __android_log_config_write() {
+#if (FAKE_LOG_DEVICE == 0)
+    extern struct android_log_transport_write logdLoggerWrite;
+    extern struct android_log_transport_write pmsgLoggerWrite;
+
+    __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
+    __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
+#else
+    extern struct android_log_transport_write fakeLoggerWrite;
+
+    __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
+#endif
+}
diff --git a/liblog/config_write.h b/liblog/config_write.h
new file mode 100644
index 0000000..3a02a4e
--- /dev/null
+++ b/liblog/config_write.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_CONFIG_WRITE_H__
+#define _LIBLOG_CONFIG_WRITE_H__
+
+#include <cutils/list.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+extern LIBLOG_HIDDEN struct listnode __android_log_transport_write;
+extern LIBLOG_HIDDEN struct listnode __android_log_persist_write;
+
+#define write_transport_for_each(transp, transports)                         \
+    for (transp = node_to_item((transports)->next,                           \
+                               struct android_log_transport_write, node);    \
+         (transp != node_to_item(transports,                                 \
+                                 struct android_log_transport_write, node)); \
+         transp = node_to_item(transp->node.next,                            \
+                               struct android_log_transport_write, node))    \
+
+#define write_transport_for_each_safe(transp, n, transports)                 \
+    for (transp = node_to_item((transports)->next,                           \
+                               struct android_log_transport_write, node),    \
+         n = transp->node.next;                                              \
+         (transp != node_to_item(transports,                                 \
+                                 struct android_log_transport_write, node)); \
+         transp = node_to_item(n, struct android_log_transport_write, node), \
+         n = transp->node.next)
+
+LIBLOG_HIDDEN void __android_log_config_write();
+
+__END_DECLS
+
+#endif /* _LIBLOG_CONFIG_WRITE_H__ */
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index 870c69a..64d872a 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -24,7 +24,7 @@
 #include <log/event_tag_map.h>
 #include <log/log.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 #define OUT_TAG "EventTagMap"
 
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index c73e03e..cc67f3e 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -31,7 +31,7 @@
 #include <log/logd.h>
 
 #include "fake_log_device.h"
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 #define kMaxTagLen  16      /* from the long-dead utils/Log.cpp */
 
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 672b446..4529b5d 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -19,7 +19,7 @@
 
 #include <sys/types.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 struct iovec;
 
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
new file mode 100644
index 0000000..dab8bc5
--- /dev/null
+++ b/liblog/fake_writer.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "config_write.h"
+#include "fake_log_device.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static int fakeOpen();
+static void fakeClose();
+static int fakeWrite(log_id_t log_id, struct timespec *ts,
+                     struct iovec *vec, size_t nr);
+
+static int logFds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
+
+LIBLOG_HIDDEN struct android_log_transport_write fakeLoggerWrite = {
+    .node = { &fakeLoggerWrite.node, &fakeLoggerWrite.node },
+    .context.private = &logFds,
+    .name = "fake",
+    .available = NULL,
+    .open = fakeOpen,
+    .close = fakeClose,
+    .write = fakeWrite,
+};
+
+static int fakeOpen() {
+    int i;
+
+    for (i = 0; i < LOG_ID_MAX; i++) {
+        char buf[sizeof("/dev/log_security")];
+        snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+        logFds[i] = fakeLogOpen(buf, O_WRONLY);
+    }
+    return 0;
+}
+
+static void fakeClose() {
+    int i;
+
+    for (i = 0; i < LOG_ID_MAX; i++) {
+        fakeLogClose(logFds[i]);
+        logFds[i] = -1;
+    }
+}
+
+static int fakeWrite(log_id_t log_id, struct timespec *ts __unused,
+                      struct iovec *vec, size_t nr)
+{
+    ssize_t ret;
+    int logFd;
+
+    if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
+        return -EBADF;
+    }
+
+    logFd = logFds[(int)log_id];
+    ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
+    if (ret < 0) {
+        ret = -errno;
+    }
+
+    return ret;
+}
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index a77c56e..64d9024 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -25,7 +25,7 @@
 #include <log/log.h>
 #include <log/logger.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
 
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
index 3535b94..b9827a1 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.c
@@ -18,7 +18,7 @@
 
 #include <log/log.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 #define MAX_SUBTAG_LEN 32
 
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 47fde20..551fa76 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,7 +23,7 @@
 
 #include <android/log.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
 
diff --git a/liblog/log_cdefs.h b/liblog/log_portability.h
similarity index 65%
rename from liblog/log_cdefs.h
rename to liblog/log_portability.h
index 3a52625..3ad2060 100644
--- a/liblog/log_cdefs.h
+++ b/liblog/log_portability.h
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LIBLOG_CDEFS_H__
-#define _LIBLOG_CDEFS_H__
+#ifndef _LIBLOG_PORTABILITY_H__
+#define _LIBLOG_PORTABILITY_H__
 
 #include <sys/cdefs.h>
+#include <unistd.h>
+
+/* Helpful private sys/cdefs.h like definitions */
 
 /* Declare this library function hidden and internal */
 #if defined(_WIN32)
@@ -46,9 +49,34 @@
 #define LIBLOG_WEAK __attribute__((weak,visibility("default")))
 #endif
 
+/* possible missing definitions in sys/cdefs.h */
+
+/* DECLS */
+#ifndef __BEGIN_DECLS
+#if defined(__cplusplus)
+#define __BEGIN_DECLS           extern "C" {
+#define __END_DECLS             }
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+#endif
+
 /* Unused argument. For C code only, remove symbol name for C++ */
 #ifndef __unused
 #define __unused        __attribute__((__unused__))
 #endif
 
-#endif /* _LIBLOG_CDEFS_H__ */
+/* possible missing definitions in unistd.h */
+
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    __typeof__(exp) _rc;                   \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+#endif
+
+#endif /* _LIBLOG_PORTABILITY_H__ */
diff --git a/liblog/log_read.c b/liblog/log_read.c
deleted file mode 100644
index 4b83944..0000000
--- a/liblog/log_read.c
+++ /dev/null
@@ -1,917 +0,0 @@
-/*
-** Copyright 2013-2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cutils/list.h>
-#include <cutils/sockets.h>
-#include <log/log.h>
-#include <log/logger.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "log_cdefs.h"
-
-/* branchless on many architectures. */
-#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
-/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
-
-#if defined(_WIN32)
-
-LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
-{
-    errno = ENOSYS;
-    return -ENOSYS;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <sys/types.h>
-
-/* Private copy of ../libcutils/socket_local.h prevent library loops */
-#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
-#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
-/* End of ../libcutils/socket_local.h */
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-LIBLOG_WEAK int socket_make_sockaddr_un(const char *name, int namespaceId,
-                                        struct sockaddr_un *p_addr,
-                                        socklen_t *alen)
-{
-    memset (p_addr, 0, sizeof (*p_addr));
-    size_t namelen;
-
-    switch (namespaceId) {
-    case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
-        namelen  = strlen(name);
-
-        /* Test with length +1 for the *initial* '\0'. */
-        if ((namelen + 1) > sizeof(p_addr->sun_path)) {
-            goto error;
-        }
-
-        /*
-         * Note: The path in this case is *not* supposed to be
-         * '\0'-terminated. ("man 7 unix" for the gory details.)
-         */
-
-        p_addr->sun_path[0] = 0;
-        memcpy(p_addr->sun_path + 1, name, namelen);
-#else
-        /* this OS doesn't have the Linux abstract namespace */
-
-        namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
-        /* unix_path_max appears to be missing on linux */
-        if (namelen > sizeof(*p_addr)
-                - offsetof(struct sockaddr_un, sun_path) - 1) {
-            goto error;
-        }
-
-        strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
-        strcat(p_addr->sun_path, name);
-#endif
-        break;
-
-    case ANDROID_SOCKET_NAMESPACE_RESERVED:
-        namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
-        /* unix_path_max appears to be missing on linux */
-        if (namelen > sizeof(*p_addr)
-                - offsetof(struct sockaddr_un, sun_path) - 1) {
-            goto error;
-        }
-
-        strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
-        strcat(p_addr->sun_path, name);
-        break;
-
-    case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
-        namelen = strlen(name);
-        /* unix_path_max appears to be missing on linux */
-        if (namelen > sizeof(*p_addr)
-                - offsetof(struct sockaddr_un, sun_path) - 1) {
-            goto error;
-        }
-
-        strcpy(p_addr->sun_path, name);
-        break;
-
-    default:
-        /* invalid namespace id */
-        return -1;
-    }
-
-    p_addr->sun_family = AF_LOCAL;
-    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
-    return 0;
-error:
-    return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- *
- * Used by AndroidSocketImpl
- */
-LIBLOG_WEAK int socket_local_client_connect(int fd, const char *name,
-                                            int namespaceId, int type __unused)
-{
-    struct sockaddr_un addr;
-    socklen_t alen;
-    int err;
-
-    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
-    if (err < 0) {
-        goto error;
-    }
-
-    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
-        goto error;
-    }
-
-    return fd;
-
-error:
-    return -1;
-}
-
-/**
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
-{
-    int s;
-
-    s = socket(AF_LOCAL, type, 0);
-    if(s < 0) return -1;
-
-    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
-        close(s);
-        return -1;
-    }
-
-    return s;
-}
-
-#endif /* !_WIN32 */
-/* End of ../libcutils/socket_local_client.c */
-
-#define logger_for_each(logger, logger_list) \
-    for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
-         logger != node_to_item(&(logger_list)->node, struct logger, node); \
-         logger = node_to_item((logger)->node.next, struct logger, node))
-
-/* In the future, we would like to make this list extensible */
-static const char *LOG_NAME[LOG_ID_MAX] = {
-    [LOG_ID_MAIN] = "main",
-    [LOG_ID_RADIO] = "radio",
-    [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system",
-    [LOG_ID_CRASH] = "crash",
-    [LOG_ID_SECURITY] = "security",
-    [LOG_ID_KERNEL] = "kernel",
-};
-
-LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
-{
-    if (log_id >= LOG_ID_MAX) {
-        log_id = LOG_ID_MAIN;
-    }
-    return LOG_NAME[log_id];
-}
-
-LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName)
-{
-    const char *b;
-    int ret;
-
-    if (!logName) {
-        return -1; /* NB: log_id_t is unsigned */
-    }
-    b = strrchr(logName, '/');
-    if (!b) {
-        b = logName;
-    } else {
-        ++b;
-    }
-
-    for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
-        const char *l = LOG_NAME[ret];
-        if (l && !strcmp(b, l)) {
-            return ret;
-        }
-    }
-    return -1;   /* should never happen */
-}
-
-struct logger_list {
-    struct listnode node;
-    int mode;
-    unsigned int tail;
-    log_time start;
-    pid_t pid;
-    int sock;
-};
-
-struct logger {
-    struct listnode node;
-    struct logger_list *top;
-    log_id_t id;
-};
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger *logger)
-{
-    if (!logger) {
-        return;
-    }
-
-    list_remove(&logger->node);
-
-    free(logger);
-}
-
-/* android_logger_alloc unimplemented, no use case */
-
-/* method for getting the associated sublog id */
-LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger)
-{
-    return logger->id;
-}
-
-/* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct logger *logger,
-                            const char *msg, char *buf, size_t buf_size)
-{
-    ssize_t ret;
-    size_t len;
-    char *cp;
-    int errno_save = 0;
-    int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                   SOCK_STREAM);
-    if (sock < 0) {
-        return sock;
-    }
-
-    if (msg) {
-        snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1);
-    }
-
-    len = strlen(buf) + 1;
-    ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
-    if (ret <= 0) {
-        goto done;
-    }
-
-    len = buf_size;
-    cp = buf;
-    while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
-        struct pollfd p;
-
-        if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
-            break;
-        }
-
-        len -= ret;
-        cp += ret;
-
-        memset(&p, 0, sizeof(p));
-        p.fd = sock;
-        p.events = POLLIN;
-
-        /* Give other side 20ms to refill pipe */
-        ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
-
-        if (ret <= 0) {
-            break;
-        }
-
-        if (!(p.revents & POLLIN)) {
-            ret = 0;
-            break;
-        }
-    }
-
-    if (ret >= 0) {
-        ret += buf_size - len;
-    }
-
-done:
-    if ((ret == -1) && errno) {
-        errno_save = errno;
-    }
-    close(sock);
-    if (errno_save) {
-        errno = errno_save;
-    }
-    return ret;
-}
-
-static int check_log_success(char *buf, ssize_t ret)
-{
-    if (ret < 0) {
-        return ret;
-    }
-
-    if (strncmp(buf, "success", 7)) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
-}
-
-/* Determine the credentials of the caller */
-static bool uid_has_log_permission(uid_t uid)
-{
-    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
-}
-
-static uid_t get_best_effective_uid()
-{
-    uid_t euid;
-    uid_t uid;
-    gid_t gid;
-    ssize_t i;
-    static uid_t last_uid = (uid_t) -1;
-
-    if (last_uid != (uid_t) -1) {
-        return last_uid;
-    }
-    uid = getuid();
-    if (uid_has_log_permission(uid)) {
-        return last_uid = uid;
-    }
-    euid = geteuid();
-    if (uid_has_log_permission(euid)) {
-        return last_uid = euid;
-    }
-    gid = getgid();
-    if (uid_has_log_permission(gid)) {
-        return last_uid = gid;
-    }
-    gid = getegid();
-    if (uid_has_log_permission(gid)) {
-        return last_uid = gid;
-    }
-    i = getgroups((size_t) 0, NULL);
-    if (i > 0) {
-        gid_t list[i];
-
-        getgroups(i, list);
-        while (--i >= 0) {
-            if (uid_has_log_permission(list[i])) {
-                return last_uid = list[i];
-            }
-        }
-    }
-    return last_uid = uid;
-}
-
-LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger)
-{
-    char buf[512];
-
-    if (logger->top->mode & ANDROID_LOG_PSTORE) {
-        if (uid_has_log_permission(get_best_effective_uid())) {
-            return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-        }
-        errno = EPERM;
-        return -1;
-    }
-    return check_log_success(buf,
-        send_log_msg(logger, "clear %d", buf, sizeof(buf)));
-}
-
-/* returns the total size of the log's ring buffer */
-LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger *logger)
-{
-    char buf[512];
-
-    ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
-    if (ret < 0) {
-        return ret;
-    }
-
-    if ((buf[0] < '0') || ('9' < buf[0])) {
-        return -1;
-    }
-
-    return atol(buf);
-}
-
-LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger,
-                                                  unsigned long size)
-{
-    char buf[512];
-
-    snprintf(buf, sizeof(buf), "setLogSize %d %lu",
-        logger ? logger->id : (unsigned) -1, size);
-
-    return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
-        struct logger *logger)
-{
-    char buf[512];
-
-    ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
-    if (ret < 0) {
-        return ret;
-    }
-
-    if ((buf[0] < '0') || ('9' < buf[0])) {
-        return -1;
-    }
-
-    return atol(buf);
-}
-
-/*
- * returns the logger version
- */
-LIBLOG_ABI_PUBLIC int android_logger_get_log_version(
-        struct logger *logger __unused)
-{
-    return 4;
-}
-
-/*
- * returns statistics
- */
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
-        struct logger_list *logger_list,
-        char *buf, size_t len)
-{
-    struct logger *logger;
-    char *cp = buf;
-    size_t remaining = len;
-    size_t n;
-
-    n = snprintf(cp, remaining, "getStatistics");
-    n = min(n, remaining);
-    remaining -= n;
-    cp += n;
-
-    logger_for_each(logger, logger_list) {
-        n = snprintf(cp, remaining, " %d", logger->id);
-        n = min(n, remaining);
-        remaining -= n;
-        cp += n;
-    }
-
-    if (logger_list->pid) {
-        snprintf(cp, remaining, " pid=%u", logger_list->pid);
-    }
-
-    return send_log_msg(NULL, NULL, buf, len);
-}
-
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
-        struct logger_list *logger_list __unused,
-        char *buf, size_t len)
-{
-    return send_log_msg(NULL, "getPruneList", buf, len);
-}
-
-LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
-        struct logger_list *logger_list __unused,
-        char *buf, size_t len)
-{
-    const char cmd[] = "setPruneList ";
-    const size_t cmdlen = sizeof(cmd) - 1;
-
-    if (strlen(buf) > (len - cmdlen)) {
-        return -ENOMEM; /* KISS */
-    }
-    memmove(buf + cmdlen, buf, len - cmdlen);
-    buf[len - 1] = '\0';
-    memcpy(buf, cmd, cmdlen);
-
-    return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
-}
-
-LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
-        int mode,
-        unsigned int tail,
-        pid_t pid)
-{
-    struct logger_list *logger_list;
-
-    logger_list = calloc(1, sizeof(*logger_list));
-    if (!logger_list) {
-        return NULL;
-    }
-
-    list_init(&logger_list->node);
-    logger_list->mode = mode;
-    logger_list->start.tv_sec = 0;
-    logger_list->start.tv_nsec = 0;
-    logger_list->tail = tail;
-    logger_list->pid = pid;
-    logger_list->sock = -1;
-
-    return logger_list;
-}
-
-LIBLOG_ABI_PUBLIC 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 */
-
-/* Open the named log and add it to the logger list */
-LIBLOG_ABI_PUBLIC struct logger *android_logger_open(
-        struct logger_list *logger_list,
-        log_id_t id)
-{
-    struct logger *logger;
-
-    if (!logger_list || (id >= LOG_ID_MAX)) {
-        goto err;
-    }
-
-    logger_for_each(logger, logger_list) {
-        if (logger->id == id) {
-            goto ok;
-        }
-    }
-
-    logger = calloc(1, sizeof(*logger));
-    if (!logger) {
-        goto err;
-    }
-
-    logger->id = id;
-    list_add_tail(&logger_list->node, &logger->node);
-    logger->top = logger_list;
-    goto ok;
-
-err:
-    logger = NULL;
-ok:
-    return logger;
-}
-
-/* Open the single named log and make it part of a new logger list */
-LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open(
-        log_id_t id,
-        int mode,
-        unsigned int tail,
-        pid_t pid)
-{
-    struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
-    if (!logger_list) {
-        return NULL;
-    }
-
-    if (!android_logger_open(logger_list, id)) {
-        android_logger_list_free(logger_list);
-        return NULL;
-    }
-
-    return logger_list;
-}
-
-static int android_logger_list_read_pstore(struct logger_list *logger_list,
-                                           struct log_msg *log_msg)
-{
-    ssize_t ret;
-    off_t current, next;
-    uid_t uid;
-    struct logger *logger;
-    struct __attribute__((__packed__)) {
-        android_pmsg_log_header_t p;
-        android_log_header_t l;
-    } buf;
-    static uint8_t preread_count;
-    bool is_system;
-
-    memset(log_msg, 0, sizeof(*log_msg));
-
-    if (logger_list->sock < 0) {
-        int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
-
-        if (fd < 0) {
-            return -errno;
-        }
-        logger_list->sock = fd;
-        preread_count = 0;
-    }
-
-    while(1) {
-        if (preread_count < sizeof(buf)) {
-            ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
-                                          &buf.p.magic + preread_count,
-                                          sizeof(buf) - preread_count));
-            if (ret < 0) {
-                return -errno;
-            }
-            preread_count += ret;
-        }
-        if (preread_count != sizeof(buf)) {
-            return preread_count ? -EIO : -EAGAIN;
-        }
-        if ((buf.p.magic != LOGGER_MAGIC)
-         || (buf.p.len <= sizeof(buf))
-         || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD))
-         || (buf.l.id >= LOG_ID_MAX)
-         || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) {
-            do {
-                memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
-            } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
-            continue;
-        }
-        preread_count = 0;
-
-        logger_for_each(logger, logger_list) {
-            if (buf.l.id != logger->id) {
-                continue;
-            }
-
-            if ((logger_list->start.tv_sec || logger_list->start.tv_nsec)
-             && ((logger_list->start.tv_sec > buf.l.realtime.tv_sec)
-              || ((logger_list->start.tv_sec == buf.l.realtime.tv_sec)
-               && (logger_list->start.tv_nsec > buf.l.realtime.tv_nsec)))) {
-                break;
-            }
-
-            if (logger_list->pid && (logger_list->pid != buf.p.pid)) {
-                break;
-            }
-
-            uid = get_best_effective_uid();
-            is_system = uid_has_log_permission(uid);
-            if (!is_system && (uid != buf.p.uid)) {
-                break;
-            }
-
-            ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
-                                          is_system ?
-                                              log_msg->entry_v4.msg :
-                                              log_msg->entry_v3.msg,
-                                          buf.p.len - sizeof(buf)));
-            if (ret < 0) {
-                return -errno;
-            }
-            if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
-                return -EIO;
-            }
-
-            log_msg->entry_v4.len = buf.p.len - sizeof(buf);
-            log_msg->entry_v4.hdr_size = is_system ?
-                sizeof(log_msg->entry_v4) :
-                sizeof(log_msg->entry_v3);
-            log_msg->entry_v4.pid = buf.p.pid;
-            log_msg->entry_v4.tid = buf.l.tid;
-            log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
-            log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
-            log_msg->entry_v4.lid = buf.l.id;
-            if (is_system) {
-                log_msg->entry_v4.uid = buf.p.uid;
-            }
-
-            return ret;
-        }
-
-        current = TEMP_FAILURE_RETRY(lseek(logger_list->sock,
-                                           (off_t)0, SEEK_CUR));
-        if (current < 0) {
-            return -errno;
-        }
-        next = TEMP_FAILURE_RETRY(lseek(logger_list->sock,
-                                        (off_t)(buf.p.len - sizeof(buf)),
-                                        SEEK_CUR));
-        if (next < 0) {
-            return -errno;
-        }
-        if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
-            return -EIO;
-        }
-    }
-}
-
-static void caught_signal(int signum __unused)
-{
-}
-
-/* Read from the selected logs */
-LIBLOG_ABI_PUBLIC int android_logger_list_read(
-        struct logger_list *logger_list,
-        struct log_msg *log_msg)
-{
-    int ret, e;
-    struct logger *logger;
-    struct sigaction ignore;
-    struct sigaction old_sigaction;
-    unsigned int old_alarm = 0;
-
-    if (!logger_list) {
-        return -EINVAL;
-    }
-
-    if (logger_list->mode & ANDROID_LOG_PSTORE) {
-        return android_logger_list_read_pstore(logger_list, log_msg);
-    }
-
-    if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-        memset(&ignore, 0, sizeof(ignore));
-        ignore.sa_handler = caught_signal;
-        sigemptyset(&ignore.sa_mask);
-    }
-
-    if (logger_list->sock < 0) {
-        char buffer[256], *cp, c;
-
-        int sock = socket_local_client("logdr",
-                                       ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                       SOCK_SEQPACKET);
-        if (sock < 0) {
-            if ((sock == -1) && errno) {
-                return -errno;
-            }
-            return sock;
-        }
-
-        strcpy(buffer,
-               (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
-        cp = buffer + strlen(buffer);
-
-        strcpy(cp, " lids");
-        cp += 5;
-        c = '=';
-        int remaining = sizeof(buffer) - (cp - buffer);
-        logger_for_each(logger, logger_list) {
-            ret = snprintf(cp, remaining, "%c%u", c, logger->id);
-            ret = min(ret, remaining);
-            remaining -= ret;
-            cp += ret;
-            c = ',';
-        }
-
-        if (logger_list->tail) {
-            ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
-            ret = min(ret, remaining);
-            remaining -= ret;
-            cp += ret;
-        }
-
-        if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
-            if (logger_list->mode & ANDROID_LOG_WRAP) {
-                // ToDo: alternate API to allow timeout to be adjusted.
-                ret = snprintf(cp, remaining, " timeout=%u",
-                               ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
-                ret = min(ret, remaining);
-                remaining -= ret;
-                cp += ret;
-            }
-            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);
-            cp += ret;
-        }
-
-        if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-            /* Deal with an unresponsive logd */
-            sigaction(SIGALRM, &ignore, &old_sigaction);
-            old_alarm = alarm(30);
-        }
-        ret = write(sock, buffer, cp - buffer);
-        e = errno;
-        if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-            if (e == EINTR) {
-                e = ETIMEDOUT;
-            }
-            alarm(old_alarm);
-            sigaction(SIGALRM, &old_sigaction, NULL);
-        }
-
-        if (ret <= 0) {
-            close(sock);
-            if ((ret == -1) && e) {
-                return -e;
-            }
-            if (ret == 0) {
-                return -EIO;
-            }
-            return ret;
-        }
-
-        logger_list->sock = sock;
-    }
-
-    while(1) {
-        memset(log_msg, 0, sizeof(*log_msg));
-
-        if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-            /* particularily useful if tombstone is reporting for logd */
-            sigaction(SIGALRM, &ignore, &old_sigaction);
-            old_alarm = alarm(30);
-        }
-        /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
-        ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
-        e = errno;
-        if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-            if ((ret == 0) || (e == EINTR)) {
-                e = EAGAIN;
-                ret = -1;
-            }
-            alarm(old_alarm);
-            sigaction(SIGALRM, &old_sigaction, NULL);
-        }
-
-        if ((ret == -1) && e) {
-            return -e;
-        }
-        return ret;
-    }
-    /* NOTREACH */
-    return ret;
-}
-
-/* Close all the logs */
-LIBLOG_ABI_PUBLIC void android_logger_list_free(
-        struct logger_list *logger_list)
-{
-    if (logger_list == NULL) {
-        return;
-    }
-
-    while (!list_empty(&logger_list->node)) {
-        struct listnode *node = list_head(&logger_list->node);
-        struct logger *logger = node_to_item(node, struct logger, node);
-        android_logger_free(logger);
-    }
-
-    if (logger_list->sock >= 0) {
-        close (logger_list->sock);
-    }
-
-    free(logger_list);
-}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index b6af222..d2bf181 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -21,7 +21,7 @@
 
 #include <log/log_read.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
 LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 };
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
new file mode 100644
index 0000000..d844104
--- /dev/null
+++ b/liblog/logd_reader.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2007-2016 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 <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <log/logd.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h"
+#include "log_portability.h"
+#include "logger.h"
+
+/* branchless on many architectures. */
+#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static int logdAvailable(log_id_t LogId);
+static int logdVersion(struct android_log_logger *logger,
+                       struct android_log_transport_context *transp);
+static int logdRead(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp,
+                    struct log_msg *log_msg);
+static int logdPoll(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp);
+static void logdClose(struct android_log_logger_list *logger_list,
+                      struct android_log_transport_context *transp);
+static int logdClear(struct android_log_logger *logger,
+                     struct android_log_transport_context *transp);
+static ssize_t logdSetSize(struct android_log_logger *logger,
+                           struct android_log_transport_context *transp,
+                           size_t size);
+static ssize_t logdGetSize(struct android_log_logger *logger,
+                           struct android_log_transport_context *transp);
+static ssize_t logdGetReadableSize(struct android_log_logger *logger,
+                                   struct android_log_transport_context *transp);
+static ssize_t logdGetPrune(struct android_log_logger_list *logger,
+                            struct android_log_transport_context *transp,
+                            char *buf, size_t len);
+static ssize_t logdSetPrune(struct android_log_logger_list *logger,
+                            struct android_log_transport_context *transp,
+                            char *buf, size_t len);
+static ssize_t logdGetStats(struct android_log_logger_list *logger,
+                            struct android_log_transport_context *transp,
+                            char *buf, size_t len);
+
+LIBLOG_HIDDEN struct android_log_transport_read logdLoggerRead = {
+    .node = { &logdLoggerRead.node, &logdLoggerRead.node },
+    .name = "logd",
+    .available = logdAvailable,
+    .version = logdVersion,
+    .read = logdRead,
+    .poll = logdPoll,
+    .close = logdClose,
+    .clear = logdClear,
+    .getSize = logdGetSize,
+    .setSize = logdSetSize,
+    .getReadableSize = logdGetSize,
+    .getPrune = logdGetPrune,
+    .setPrune = logdSetPrune,
+    .getStats = logdGetStats,
+};
+
+static int logdAvailable(log_id_t logId)
+{
+    if (logId > LOG_ID_KERNEL) {
+        return -EINVAL;
+    }
+    if (logId == LOG_ID_SECURITY) {
+        uid_t uid = __android_log_uid();
+        if (uid != AID_SYSTEM) {
+            return -EPERM;
+        }
+    }
+    if (access("/dev/socket/logdw", W_OK) == 0) {
+        return 0;
+    }
+    return -EBADF;
+}
+
+/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
+
+#if defined(_WIN32)
+
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
+{
+    errno = ENOSYS;
+    return -ENOSYS;
+}
+
+#else /* !_WIN32 */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+/* Private copy of ../libcutils/socket_local.h prevent library loops */
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+/* End of ../libcutils/socket_local.h */
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+LIBLOG_WEAK int socket_make_sockaddr_un(const char *name, int namespaceId,
+                                        struct sockaddr_un *p_addr,
+                                        socklen_t *alen)
+{
+    memset (p_addr, 0, sizeof (*p_addr));
+    size_t namelen;
+
+    switch (namespaceId) {
+    case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#if defined(__linux__)
+        namelen  = strlen(name);
+
+        /* Test with length +1 for the *initial* '\0'. */
+        if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+            goto error;
+        }
+
+        /*
+         * Note: The path in this case is *not* supposed to be
+         * '\0'-terminated. ("man 7 unix" for the gory details.)
+         */
+
+        p_addr->sun_path[0] = 0;
+        memcpy(p_addr->sun_path + 1, name, namelen);
+#else
+        /* this OS doesn't have the Linux abstract namespace */
+
+        namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+        /* unix_path_max appears to be missing on linux */
+        if (namelen > sizeof(*p_addr)
+                - offsetof(struct sockaddr_un, sun_path) - 1) {
+            goto error;
+        }
+
+        strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+        strcat(p_addr->sun_path, name);
+#endif
+        break;
+
+    case ANDROID_SOCKET_NAMESPACE_RESERVED:
+        namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+        /* unix_path_max appears to be missing on linux */
+        if (namelen > sizeof(*p_addr)
+                - offsetof(struct sockaddr_un, sun_path) - 1) {
+            goto error;
+        }
+
+        strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+        strcat(p_addr->sun_path, name);
+        break;
+
+    case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+        namelen = strlen(name);
+        /* unix_path_max appears to be missing on linux */
+        if (namelen > sizeof(*p_addr)
+                - offsetof(struct sockaddr_un, sun_path) - 1) {
+            goto error;
+        }
+
+        strcpy(p_addr->sun_path, name);
+        break;
+
+    default:
+        /* invalid namespace id */
+        return -1;
+    }
+
+    p_addr->sun_family = AF_LOCAL;
+    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+    return 0;
+error:
+    return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+LIBLOG_WEAK int socket_local_client_connect(int fd, const char *name,
+                                            int namespaceId, int type __unused)
+{
+    struct sockaddr_un addr;
+    socklen_t alen;
+    int err;
+
+    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+    if (err < 0) {
+        goto error;
+    }
+
+    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+        goto error;
+    }
+
+    return fd;
+
+error:
+    return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
+{
+    int s;
+
+    s = socket(AF_LOCAL, type, 0);
+    if(s < 0) return -1;
+
+    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+#endif /* !_WIN32 */
+/* End of ../libcutils/socket_local_client.c */
+
+/* worker for sending the command to the logger */
+static ssize_t send_log_msg(struct android_log_logger *logger,
+                            const char *msg, char *buf, size_t buf_size)
+{
+    ssize_t ret;
+    size_t len;
+    char *cp;
+    int errno_save = 0;
+    int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    if (sock < 0) {
+        return sock;
+    }
+
+    if (msg) {
+        snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned) -1);
+    }
+
+    len = strlen(buf) + 1;
+    ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
+    if (ret <= 0) {
+        goto done;
+    }
+
+    len = buf_size;
+    cp = buf;
+    while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+        struct pollfd p;
+
+        if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+            break;
+        }
+
+        len -= ret;
+        cp += ret;
+
+        memset(&p, 0, sizeof(p));
+        p.fd = sock;
+        p.events = POLLIN;
+
+        /* Give other side 20ms to refill pipe */
+        ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+        if (ret <= 0) {
+            break;
+        }
+
+        if (!(p.revents & POLLIN)) {
+            ret = 0;
+            break;
+        }
+    }
+
+    if (ret >= 0) {
+        ret += buf_size - len;
+    }
+
+done:
+    if ((ret == -1) && errno) {
+        errno_save = errno;
+    }
+    close(sock);
+    if (errno_save) {
+        errno = errno_save;
+    }
+    return ret;
+}
+
+static int check_log_success(char *buf, ssize_t ret)
+{
+    if (ret < 0) {
+        return ret;
+    }
+
+    if (strncmp(buf, "success", 7)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int logdClear(struct android_log_logger *logger,
+                     struct android_log_transport_context *transp __unused)
+{
+    char buf[512];
+
+    return check_log_success(buf,
+        send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+}
+
+/* returns the total size of the log's ring buffer */
+static ssize_t logdGetSize(struct android_log_logger *logger,
+                           struct android_log_transport_context *transp __unused)
+{
+    char buf[512];
+
+    ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+    if (ret < 0) {
+        return ret;
+    }
+
+    if ((buf[0] < '0') || ('9' < buf[0])) {
+        return -1;
+    }
+
+    return atol(buf);
+}
+
+static ssize_t logdSetSize(
+        struct android_log_logger *logger,
+        struct android_log_transport_context *transp __unused,
+        size_t size)
+{
+    char buf[512];
+
+    snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
+
+    return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+static ssize_t logdGetReadableSize(
+       struct android_log_logger *logger,
+       struct android_log_transport_context *transp __unused)
+{
+    char buf[512];
+
+    ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+    if (ret < 0) {
+        return ret;
+    }
+
+    if ((buf[0] < '0') || ('9' < buf[0])) {
+        return -1;
+    }
+
+    return atol(buf);
+}
+
+/*
+ * returns the logger version
+ */
+static int logdVersion(
+        struct android_log_logger *logger __unused,
+        struct android_log_transport_context *transp __unused)
+{
+    uid_t uid = __android_log_uid();
+    return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+}
+
+/*
+ * returns statistics
+ */
+static ssize_t logdGetStats(struct android_log_logger_list *logger_list,
+                            struct android_log_transport_context *transp __unused,
+                            char *buf, size_t len)
+{
+    struct android_log_logger *logger;
+    char *cp = buf;
+    size_t remaining = len;
+    size_t n;
+
+    n = snprintf(cp, remaining, "getStatistics");
+    n = min(n, remaining);
+    remaining -= n;
+    cp += n;
+
+    logger_for_each(logger, logger_list) {
+        n = snprintf(cp, remaining, " %d", logger->logId);
+        n = min(n, remaining);
+        remaining -= n;
+        cp += n;
+    }
+
+    if (logger_list->pid) {
+        snprintf(cp, remaining, " pid=%u", logger_list->pid);
+    }
+
+    return send_log_msg(NULL, NULL, buf, len);
+}
+
+static ssize_t logdGetPrune(
+        struct android_log_logger_list *logger_list __unused,
+        struct android_log_transport_context *transp __unused,
+        char *buf, size_t len)
+{
+    return send_log_msg(NULL, "getPruneList", buf, len);
+}
+
+static ssize_t logdSetPrune(
+        struct android_log_logger_list *logger_list __unused,
+        struct android_log_transport_context *transp __unused,
+        char *buf, size_t len)
+{
+    const char cmd[] = "setPruneList ";
+    const size_t cmdlen = sizeof(cmd) - 1;
+
+    if (strlen(buf) > (len - cmdlen)) {
+        return -ENOMEM; /* KISS */
+    }
+    memmove(buf + cmdlen, buf, len - cmdlen);
+    buf[len - 1] = '\0';
+    memcpy(buf, cmd, cmdlen);
+
+    return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+}
+
+
+static void caught_signal(int signum __unused)
+{
+}
+
+static int logdOpen(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp)
+{
+    struct android_log_logger *logger;
+    struct sigaction ignore;
+    struct sigaction old_sigaction;
+    unsigned int old_alarm = 0;
+    char buffer[256], *cp, c;
+    int e, ret, remaining;
+
+    int sock = transp->context.sock;
+    if (sock > 0) {
+        return sock;
+    }
+
+    if (!logger_list) {
+        return -EINVAL;
+    }
+
+    sock = socket_local_client("logdr",
+                               ANDROID_SOCKET_NAMESPACE_RESERVED,
+                               SOCK_SEQPACKET);
+    if (sock == 0) {
+        /* Guarantee not file descriptor zero */
+        int newsock = socket_local_client("logdr",
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_SEQPACKET);
+        close(sock);
+        sock = newsock;
+    }
+    if (sock <= 0) {
+        if ((sock == -1) && errno) {
+            return -errno;
+        }
+        return sock;
+    }
+
+    strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ?
+            "dumpAndClose" : "stream");
+    cp = buffer + strlen(buffer);
+
+    strcpy(cp, " lids");
+    cp += 5;
+    c = '=';
+    remaining = sizeof(buffer) - (cp - buffer);
+    logger_for_each(logger, logger_list) {
+        ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
+        ret = min(ret, remaining);
+        remaining -= ret;
+        cp += ret;
+        c = ',';
+    }
+
+    if (logger_list->tail) {
+        ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
+        ret = min(ret, remaining);
+        remaining -= ret;
+        cp += ret;
+    }
+
+    if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+        if (logger_list->mode & ANDROID_LOG_WRAP) {
+            // ToDo: alternate API to allow timeout to be adjusted.
+            ret = snprintf(cp, remaining, " timeout=%u",
+                           ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+            ret = min(ret, remaining);
+            remaining -= ret;
+            cp += ret;
+        }
+        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);
+        cp += ret;
+    }
+
+    if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+        /* Deal with an unresponsive logd */
+        memset(&ignore, 0, sizeof(ignore));
+        ignore.sa_handler = caught_signal;
+        sigemptyset(&ignore.sa_mask);
+        /* particularily useful if tombstone is reporting for logd */
+        sigaction(SIGALRM, &ignore, &old_sigaction);
+        old_alarm = alarm(30);
+    }
+    ret = write(sock, buffer, cp - buffer);
+    e = errno;
+    if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+        if (e == EINTR) {
+            e = ETIMEDOUT;
+        }
+        alarm(old_alarm);
+        sigaction(SIGALRM, &old_sigaction, NULL);
+    }
+
+    if (ret <= 0) {
+        close(sock);
+        if ((ret == -1) && e) {
+            return -e;
+        }
+        if (ret == 0) {
+            return -EIO;
+        }
+        return ret;
+    }
+
+    return transp->context.sock = sock;
+}
+
+/* Read from the selected logs */
+static int logdRead(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp,
+                    struct log_msg *log_msg)
+{
+    int ret, e;
+    struct sigaction ignore;
+    struct sigaction old_sigaction;
+    unsigned int old_alarm = 0;
+
+    ret = logdOpen(logger_list, transp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    memset(log_msg, 0, sizeof(*log_msg));
+
+    if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+        memset(&ignore, 0, sizeof(ignore));
+        ignore.sa_handler = caught_signal;
+        sigemptyset(&ignore.sa_mask);
+        /* particularily useful if tombstone is reporting for logd */
+        sigaction(SIGALRM, &ignore, &old_sigaction);
+        old_alarm = alarm(30);
+    }
+
+    /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
+    ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
+    e = errno;
+
+    if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
+        if ((ret == 0) || (e == EINTR)) {
+            e = EAGAIN;
+            ret = -1;
+        }
+        alarm(old_alarm);
+        sigaction(SIGALRM, &old_sigaction, NULL);
+    }
+
+    if ((ret == -1) && e) {
+        return -e;
+    }
+    return ret;
+}
+
+static int logdPoll(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp)
+{
+    struct pollfd p;
+
+    int ret = logdOpen(logger_list, transp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    memset(&p, 0, sizeof(p));
+    p.fd = ret;
+    p.events = POLLIN;
+    ret = poll(&p, 1, 20);
+    if ((ret > 0) && !(p.revents & POLLIN)) {
+        ret = 0;
+    }
+    if ((ret == -1) && errno) {
+        return -errno;
+    }
+    return ret;
+}
+
+/* Close all the logs */
+static void logdClose(struct android_log_logger_list *logger_list __unused,
+                      struct android_log_transport_context *transp)
+{
+    if (transp->context.sock > 0) {
+        close (transp->context.sock);
+        transp->context.sock = -1;
+    }
+}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
deleted file mode 100644
index 85a4aab..0000000
--- a/liblog/logd_write.c
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * Copyright (C) 2007-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#if (FAKE_LOG_DEVICE == 0)
-#include <endian.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-#include <stdarg.h>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#if (FAKE_LOG_DEVICE == 0)
-#include <sys/socket.h>
-#include <sys/un.h>
-#endif
-#include <time.h>
-#include <unistd.h>
-
-#ifdef __BIONIC__
-#include <android/set_abort_message.h>
-#endif
-
-#include <log/event_tag_map.h>
-#include <log/logd.h>
-#include <log/logger.h>
-#include <log/log_read.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include "log_cdefs.h"
-
-#define LOG_BUF_SIZE 1024
-
-#if FAKE_LOG_DEVICE
-/* This will be defined when building for the host. */
-#include "fake_log_device.h"
-#endif
-
-static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
-
-#if !defined(_WIN32)
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static void lock()
-{
-    /*
-     * If we trigger a signal handler in the middle of locked activity and the
-     * signal handler logs a message, we could get into a deadlock state.
-     */
-    pthread_mutex_lock(&log_init_lock);
-}
-
-static int trylock()
-{
-    return pthread_mutex_trylock(&log_init_lock);
-}
-
-static void unlock()
-{
-    pthread_mutex_unlock(&log_init_lock);
-}
-
-#else   /* !defined(_WIN32) */
-
-#define lock() ((void)0)
-#define trylock() (0) /* success */
-#define unlock() ((void)0)
-
-#endif  /* !defined(_WIN32) */
-
-#if FAKE_LOG_DEVICE
-static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
-#else
-static int logd_fd = -1;
-static int pstore_fd = -1;
-#endif
-
-/*
- * This is used by the C++ code to decide if it should write logs through
- * the C code.  Basically, if /dev/socket/logd is available, we're running in
- * the simulator rather than a desktop tool and want to use the device.
- */
-static enum {
-    kLogUninitialized, kLogNotAvailable, kLogAvailable
-} g_log_status = kLogUninitialized;
-
-LIBLOG_ABI_PUBLIC int __android_log_dev_available()
-{
-    if (g_log_status == kLogUninitialized) {
-        if (access("/dev/socket/logdw", W_OK) == 0)
-            g_log_status = kLogAvailable;
-        else
-            g_log_status = kLogNotAvailable;
-    }
-
-    return (g_log_status == kLogAvailable);
-}
-
-/* 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_security")];
-        snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
-        log_fds[i] = fakeLogOpen(buf, O_WRONLY);
-    }
-#else
-    if (pstore_fd < 0) {
-        pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
-    }
-
-    if (logd_fd < 0) {
-        i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
-        if (i < 0) {
-            ret = -errno;
-        } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
-            ret = -errno;
-            close(i);
-        } 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 (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
-                                           sizeof(struct sockaddr_un))) < 0) {
-                ret = -errno;
-                close(i);
-            } else {
-                logd_fd = i;
-            }
-        }
-    }
-#endif
-
-    return ret;
-}
-
-static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
-{
-    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;
-    }
-    do {
-        ret = fakeLogWritev(log_fd, vec, nr);
-        if (ret < 0) {
-            ret = -errno;
-        }
-    } while (ret == -EINTR);
-#else
-    static const unsigned header_length = 2;
-    struct iovec newVec[nr + header_length];
-    android_log_header_t header;
-    android_pmsg_log_header_t pmsg_header;
-    struct timespec ts;
-    size_t i, payload_size;
-    static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
-    static pid_t last_pid = (pid_t) -1;
-    static atomic_int_fast32_t dropped;
-    static atomic_int_fast32_t dropped_security;
-
-    if (!nr) {
-        return -EINVAL;
-    }
-
-    if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
-        last_uid = getuid();
-    }
-    if (last_pid == (pid_t) -1) {
-        last_pid = getpid();
-    }
-    if (log_id == LOG_ID_SECURITY) {
-        if (vec[0].iov_len < 4) {
-            return -EINVAL;
-        }
-        /* Matches clientHasLogCredentials() in logd */
-        if ((last_uid != AID_SYSTEM) && (last_uid != AID_ROOT) && (last_uid != AID_LOG)) {
-            uid_t uid = geteuid();
-            if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
-                gid_t gid = getgid();
-                if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
-                    gid = getegid();
-                    if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
-                        int num_groups;
-                        gid_t *groups;
-
-                        num_groups = getgroups(0, NULL);
-                        if (num_groups <= 0) {
-                            return -EPERM;
-                        }
-                        groups = calloc(num_groups, sizeof(gid_t));
-                        if (!groups) {
-                            return -ENOMEM;
-                        }
-                        num_groups = getgroups(num_groups, groups);
-                        while (num_groups > 0) {
-                            if (groups[num_groups - 1] == AID_LOG) {
-                                break;
-                            }
-                            --num_groups;
-                        }
-                        free(groups);
-                        if (num_groups <= 0) {
-                            return -EPERM;
-                        }
-                    }
-                }
-            }
-        }
-        if (!__android_log_security()) {
-            atomic_store(&dropped_security, 0);
-            return -EPERM;
-        }
-    } else if (log_id == LOG_ID_EVENTS) {
-        static atomic_uintptr_t map;
-        int ret;
-        const char *tag;
-        EventTagMap *m, *f;
-
-        if (vec[0].iov_len < 4) {
-            return -EINVAL;
-        }
-
-        tag = NULL;
-        f = NULL;
-        m = (EventTagMap *)atomic_load(&map);
-
-        if (!m) {
-            ret = trylock();
-            m = (EventTagMap *)atomic_load(&map); /* trylock flush cache */
-            if (!m) {
-                m = android_openEventTagMap(EVENT_TAG_MAP_FILE);
-                if (ret) { /* trylock failed, use local copy, mark for close */
-                    f = m;
-                } else {
-                    if (!m) { /* One chance to open map file */
-                        m = (EventTagMap *)(uintptr_t)-1LL;
-                    }
-                    atomic_store(&map, (uintptr_t)m);
-                }
-            }
-            if (!ret) { /* trylock succeeded, unlock */
-                unlock();
-            }
-        }
-        if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) {
-            tag = android_lookupEventTag(
-                                    m,
-                                    htole32(((uint32_t *)vec[0].iov_base)[0]));
-        }
-        ret = __android_log_is_loggable(ANDROID_LOG_INFO,
-                                        tag,
-                                        ANDROID_LOG_VERBOSE);
-        if (f) { /* local copy marked for close */
-            android_closeEventTagMap(f);
-        }
-        if (!ret) {
-            return -EPERM;
-        }
-    } else {
-        /* Validate the incoming tag, tag content can not split across iovec */
-        char prio = ANDROID_LOG_VERBOSE;
-        const char *tag = vec[0].iov_base;
-        size_t len = vec[0].iov_len;
-        if (!tag) {
-            len = 0;
-        }
-        if (len > 0) {
-            prio = *tag;
-            if (len > 1) {
-                --len;
-                ++tag;
-            } else {
-                len = vec[1].iov_len;
-                tag = ((const char *)vec[1].iov_base);
-                if (!tag) {
-                    len = 0;
-                }
-            }
-        }
-        /* tag must be nul terminated */
-        if (strnlen(tag, len) >= len) {
-            tag = NULL;
-        }
-
-        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
-            return -EPERM;
-        }
-    }
-
-    /*
-     *  struct {
-     *      // what we provide to pstore
-     *      android_pmsg_log_header_t pmsg_header;
-     *      // what we provide to socket
-     *      android_log_header_t header;
-     *      // caller provides
-     *      union {
-     *          struct {
-     *              char     prio;
-     *              char     payload[];
-     *          } string;
-     *          struct {
-     *              uint32_t tag
-     *              char     payload[];
-     *          } binary;
-     *      };
-     *  };
-     */
-
-    clock_gettime(android_log_clockid(), &ts);
-
-    pmsg_header.magic = LOGGER_MAGIC;
-    pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
-    pmsg_header.uid = last_uid;
-    pmsg_header.pid = last_pid;
-
-    header.tid = gettid();
-    header.realtime.tv_sec = ts.tv_sec;
-    header.realtime.tv_nsec = ts.tv_nsec;
-
-    newVec[0].iov_base   = (unsigned char *) &pmsg_header;
-    newVec[0].iov_len    = sizeof(pmsg_header);
-    newVec[1].iov_base   = (unsigned char *) &header;
-    newVec[1].iov_len    = sizeof(header);
-
-    if (logd_fd > 0) {
-        int32_t snapshot = atomic_exchange_explicit(&dropped_security, 0,
-                                                    memory_order_relaxed);
-        if (snapshot) {
-            android_log_event_int_t buffer;
-
-            header.id = LOG_ID_SECURITY;
-            buffer.header.tag = htole32(LIBLOG_LOG_TAG);
-            buffer.payload.type = EVENT_TYPE_INT;
-            buffer.payload.data = htole32(snapshot);
-
-            newVec[2].iov_base = &buffer;
-            newVec[2].iov_len  = sizeof(buffer);
-
-            ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
-            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
-                atomic_fetch_add_explicit(&dropped_security, snapshot,
-                                          memory_order_relaxed);
-            }
-        }
-        snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
-        if (snapshot && __android_log_is_loggable(ANDROID_LOG_INFO,
-                                                  "liblog",
-                                                  ANDROID_LOG_VERBOSE)) {
-            android_log_event_int_t buffer;
-
-            header.id = LOG_ID_EVENTS;
-            buffer.header.tag = htole32(LIBLOG_LOG_TAG);
-            buffer.payload.type = EVENT_TYPE_INT;
-            buffer.payload.data = htole32(snapshot);
-
-            newVec[2].iov_base = &buffer;
-            newVec[2].iov_len  = sizeof(buffer);
-
-            ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
-            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
-                atomic_fetch_add_explicit(&dropped, snapshot,
-                                          memory_order_relaxed);
-            }
-        }
-    }
-
-    header.id = log_id;
-
-    for (payload_size = 0, i = header_length; i < nr + header_length; i++) {
-        newVec[i].iov_base = vec[i - header_length].iov_base;
-        payload_size += newVec[i].iov_len = vec[i - header_length].iov_len;
-
-        if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) {
-            newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD;
-            if (newVec[i].iov_len) {
-                ++i;
-            }
-            payload_size = LOGGER_ENTRY_MAX_PAYLOAD;
-            break;
-        }
-    }
-    pmsg_header.len += payload_size;
-
-    if (pstore_fd >= 0) {
-        TEMP_FAILURE_RETRY(writev(pstore_fd, newVec, i));
-    }
-
-    if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */
-        /*
-         * ignore log messages we send to ourself (logd).
-         * Such log messages are often generated by libraries we depend on
-         * which use standard Android logging.
-         */
-        return 0;
-    }
-
-    if (logd_fd < 0) {
-        return -EBADF;
-    }
-
-    /*
-     * The write below could be lost, but will never block.
-     *
-     * To logd, we drop the pmsg_header
-     *
-     * ENOTCONN occurs if logd dies.
-     * EAGAIN occurs if logd is overloaded.
-     */
-    ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
-    if (ret < 0) {
-        ret = -errno;
-        if (ret == -ENOTCONN) {
-            lock();
-            close(logd_fd);
-            logd_fd = -1;
-            ret = __write_to_log_initialize();
-            unlock();
-
-            if (ret < 0) {
-                return ret;
-            }
-
-            ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
-            if (ret < 0) {
-                ret = -errno;
-            }
-        }
-    }
-
-    if (ret > (ssize_t)sizeof(header)) {
-        ret -= sizeof(header);
-    } else if (ret == -EAGAIN) {
-        atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
-        if (log_id == LOG_ID_SECURITY) {
-            atomic_fetch_add_explicit(&dropped_security, 1,
-                                      memory_order_relaxed);
-        }
-    }
-#endif
-
-    return ret;
-}
-
-#if FAKE_LOG_DEVICE
-static const char *LOG_NAME[LOG_ID_MAX] = {
-    [LOG_ID_MAIN] = "main",
-    [LOG_ID_RADIO] = "radio",
-    [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system",
-    [LOG_ID_CRASH] = "crash",
-    [LOG_ID_SECURITY] = "security",
-    [LOG_ID_KERNEL] = "kernel",
-};
-
-LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
-{
-    if (log_id >= LOG_ID_MAX) {
-        log_id = LOG_ID_MAIN;
-    }
-    return LOG_NAME[log_id];
-}
-#endif
-
-static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
-{
-    lock();
-
-    if (write_to_log == __write_to_log_init) {
-        int ret;
-
-        ret = __write_to_log_initialize();
-        if (ret < 0) {
-            unlock();
-#if (FAKE_LOG_DEVICE == 0)
-            if (pstore_fd >= 0) {
-                __write_to_log_daemon(log_id, vec, nr);
-            }
-#endif
-            return ret;
-        }
-
-        write_to_log = __write_to_log_daemon;
-    }
-
-    unlock();
-
-    return write_to_log(log_id, vec, nr);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char *tag,
-                                          const char *msg)
-{
-    return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
-                                              const char *tag, const char *msg)
-{
-    struct iovec vec[3];
-    char tmp_tag[32];
-
-    if (!tag)
-        tag = "";
-
-    /* XXX: This needs to go! */
-    if ((bufID != LOG_ID_RADIO) &&
-         (!strcmp(tag, "HTC_RIL") ||
-        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
-        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
-        !strcmp(tag, "AT") ||
-        !strcmp(tag, "GSM") ||
-        !strcmp(tag, "STK") ||
-        !strcmp(tag, "CDMA") ||
-        !strcmp(tag, "PHONE") ||
-        !strcmp(tag, "SMS"))) {
-            bufID = LOG_ID_RADIO;
-            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
-            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-            tag = tmp_tag;
-    }
-
-#if __BIONIC__
-    if (prio == ANDROID_LOG_FATAL) {
-        android_set_abort_message(msg);
-    }
-#endif
-
-    vec[0].iov_base   = (unsigned char *) &prio;
-    vec[0].iov_len    = 1;
-    vec[1].iov_base   = (void *) tag;
-    vec[1].iov_len    = strlen(tag) + 1;
-    vec[2].iov_base   = (void *) msg;
-    vec[2].iov_len    = strlen(msg) + 1;
-
-    return write_to_log(bufID, vec, 3);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char *tag,
-                                           const char *fmt, va_list ap)
-{
-    char buf[LOG_BUF_SIZE];
-
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-
-    return __android_log_write(prio, tag, buf);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char *tag,
-                                          const char *fmt, ...)
-{
-    va_list ap;
-    char buf[LOG_BUF_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-
-    return __android_log_write(prio, tag, buf);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
-                                              const char *tag,
-                                              const char *fmt, ...)
-{
-    va_list ap;
-    char buf[LOG_BUF_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-
-    return __android_log_buf_write(bufID, prio, tag, buf);
-}
-
-LIBLOG_ABI_PUBLIC void __android_log_assert(
-        const char *cond,
-        const char *tag,
-        const char *fmt, ...)
-{
-    char buf[LOG_BUF_SIZE];
-
-    if (fmt) {
-        va_list ap;
-        va_start(ap, fmt);
-        vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-        va_end(ap);
-    } else {
-        /* Msg not provided, log condition.  N.B. Do not use cond directly as
-         * format string as it could contain spurious '%' syntax (e.g.
-         * "%d" in "blocks%devs == 0").
-         */
-        if (cond)
-            snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
-        else
-            strcpy(buf, "Unspecified assertion failed");
-    }
-
-    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-    abort(); /* abort so we have a chance to debug the situation */
-    /* NOTREACHED */
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag,
-                                           const void *payload, size_t len)
-{
-    struct iovec vec[2];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = (void*)payload;
-    vec[1].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 2);
-}
-
-LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
-                                                    const void *payload,
-                                                    size_t len)
-{
-    struct iovec vec[2];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = (void*)payload;
-    vec[1].iov_len = len;
-
-    return write_to_log(LOG_ID_SECURITY, vec, 2);
-}
-
-/*
- * Like __android_log_bwrite, but takes the type as well.  Doesn't work
- * for the general case where we're generating lists of stuff, but very
- * handy if we just want to dump an integer into the log.
- */
-LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
-                                            const void *payload, size_t len)
-{
-    struct iovec vec[3];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = (void*)payload;
-    vec[2].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 3);
-}
-
-/*
- * Like __android_log_bwrite, but used for writing strings to the
- * event log.
- */
-LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char *payload)
-{
-    struct iovec vec[4];
-    char type = EVENT_TYPE_STRING;
-    uint32_t len = strlen(payload);
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = &len;
-    vec[2].iov_len = sizeof(len);
-    vec[3].iov_base = (void*)payload;
-    vec[3].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 4);
-}
-
-/*
- * Like __android_log_security_bwrite, but used for writing strings to the
- * security log.
- */
-LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
-                                                     const char *payload)
-{
-    struct iovec vec[4];
-    char type = EVENT_TYPE_STRING;
-    uint32_t len = strlen(payload);
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = &len;
-    vec[2].iov_len = sizeof(len);
-    vec[3].iov_base = (void*)payload;
-    vec[3].iov_len = len;
-
-    return write_to_log(LOG_ID_SECURITY, vec, 4);
-}
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
new file mode 100644
index 0000000..696237d
--- /dev/null
+++ b/liblog/logd_writer.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2007-2016 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 <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <cutils/sockets.h>
+#include <log/logd.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+/* branchless on many architectures. */
+#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static int logdAvailable(log_id_t LogId);
+static int logdOpen();
+static void logdClose();
+static int logdWrite(log_id_t logId, struct timespec *ts,
+                     struct iovec *vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
+    .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
+    .context.sock = -1,
+    .name = "logd",
+    .available = logdAvailable,
+    .open = logdOpen,
+    .close = logdClose,
+    .write = logdWrite,
+};
+
+/* log_init_lock assumed */
+static int logdOpen()
+{
+    int i, ret = 0;
+
+    if (logdLoggerWrite.context.sock < 0) {
+        i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+        if (i < 0) {
+            ret = -errno;
+        } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {
+            ret = -errno;
+            close(i);
+        } 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 (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,
+                                           sizeof(struct sockaddr_un))) < 0) {
+                ret = -errno;
+                close(i);
+            } else {
+                logdLoggerWrite.context.sock = i;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static void logdClose()
+{
+    if (logdLoggerWrite.context.sock >= 0) {
+        close(logdLoggerWrite.context.sock);
+        logdLoggerWrite.context.sock = -1;
+    }
+}
+
+static int logdAvailable(log_id_t logId)
+{
+    if (logId > LOG_ID_SECURITY) {
+        return -EINVAL;
+    }
+    if (logId == LOG_ID_SECURITY) {
+        uid_t uid = __android_log_uid();
+        if ((uid != AID_LOG) && (uid != AID_ROOT) && (uid != AID_SYSTEM)) {
+            return -EPERM;
+        }
+    }
+    if (logdLoggerWrite.context.sock < 0) {
+        if (access("/dev/socket/logdw", W_OK) == 0) {
+            return 0;
+        }
+        return -EBADF;
+    }
+    return 1;
+}
+
+static int logdWrite(log_id_t logId, struct timespec *ts,
+                     struct iovec *vec, size_t nr)
+{
+    ssize_t ret;
+    static const unsigned headerLength = 1;
+    struct iovec newVec[nr + headerLength];
+    android_log_header_t header;
+    size_t i, payloadSize;
+    static atomic_int_fast32_t dropped;
+    static atomic_int_fast32_t droppedSecurity;
+
+    if (logdLoggerWrite.context.sock < 0) {
+        return -EBADF;
+    }
+
+    /* logd, after initialization and priv drop */
+    if (__android_log_uid() == AID_LOGD) {
+        /*
+         * ignore log messages we send to ourself (logd).
+         * Such log messages are often generated by libraries we depend on
+         * which use standard Android logging.
+         */
+        return 0;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    header.tid = gettid();
+    header.realtime.tv_sec = ts->tv_sec;
+    header.realtime.tv_nsec = ts->tv_nsec;
+
+    newVec[0].iov_base = (unsigned char *)&header;
+    newVec[0].iov_len  = sizeof(header);
+
+    if (logdLoggerWrite.context.sock > 0) {
+        int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0,
+                                                    memory_order_relaxed);
+        if (snapshot) {
+            android_log_event_int_t buffer;
+
+            header.id = LOG_ID_SECURITY;
+            buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+            buffer.payload.type = EVENT_TYPE_INT;
+            buffer.payload.data = htole32(snapshot);
+
+            newVec[headerLength].iov_base = &buffer;
+            newVec[headerLength].iov_len  = sizeof(buffer);
+
+            ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, 2));
+            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+                atomic_fetch_add_explicit(&droppedSecurity, snapshot,
+                                          memory_order_relaxed);
+            }
+        }
+        snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+        if (snapshot && __android_log_is_loggable(ANDROID_LOG_INFO,
+                                                  "liblog",
+                                                  ANDROID_LOG_VERBOSE)) {
+            android_log_event_int_t buffer;
+
+            header.id = LOG_ID_EVENTS;
+            buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+            buffer.payload.type = EVENT_TYPE_INT;
+            buffer.payload.data = htole32(snapshot);
+
+            newVec[headerLength].iov_base = &buffer;
+            newVec[headerLength].iov_len  = sizeof(buffer);
+
+            ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, 2));
+            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+                atomic_fetch_add_explicit(&dropped, snapshot,
+                                          memory_order_relaxed);
+            }
+        }
+    }
+
+    header.id = logId;
+
+    for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+        newVec[i].iov_base = vec[i - headerLength].iov_base;
+        payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+        if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+            newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+            if (newVec[i].iov_len) {
+                ++i;
+            }
+            break;
+        }
+    }
+
+    /*
+     * The write below could be lost, but will never block.
+     *
+     * ENOTCONN occurs if logd dies.
+     * EAGAIN occurs if logd is overloaded.
+     */
+    ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, i));
+    if (ret < 0) {
+        ret = -errno;
+        if (ret == -ENOTCONN) {
+            __android_log_lock();
+            logdClose();
+            ret = logdOpen();
+            __android_log_unlock();
+
+            if (ret < 0) {
+                return ret;
+            }
+
+            ret = TEMP_FAILURE_RETRY(writev(logdLoggerWrite.context.sock, newVec, i));
+            if (ret < 0) {
+                ret = -errno;
+            }
+        }
+    }
+
+    if (ret > (ssize_t)sizeof(header)) {
+        ret -= sizeof(header);
+    } else if (ret == -EAGAIN) {
+        atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+        if (logId == LOG_ID_SECURITY) {
+            atomic_fetch_add_explicit(&droppedSecurity, 1,
+                                      memory_order_relaxed);
+        }
+    }
+
+    return ret;
+}
diff --git a/liblog/logger.h b/liblog/logger.h
new file mode 100644
index 0000000..61bc396
--- /dev/null
+++ b/liblog/logger.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_LOGGER_H__
+#define _LIBLOG_LOGGER_H__
+
+#include <stdbool.h>
+#include <log/uio.h>
+
+#include <cutils/list.h>
+#include <log/log.h>
+#include <log/log_read.h>
+#include <log/logger.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+/* Union, sock or fd of zero is not allowed unless static initialized */
+union android_log_context {
+  void *private;
+  int sock;
+  int fd;
+  struct listnode *node;
+};
+
+struct android_log_transport_write {
+  struct listnode node;
+  const char *name;
+  union android_log_context context; /* Initialized by static allocation */
+
+  int (*available)(log_id_t logId);
+  int (*open)();
+  void (*close)();
+  int (*write)(log_id_t logId, struct timespec *ts, struct iovec *vec, size_t nr);
+};
+
+struct android_log_logger_list;
+struct android_log_transport_context;
+struct android_log_logger;
+
+struct android_log_transport_read {
+  struct listnode node;
+  const char *name;
+
+  int (*available)(log_id_t logId);
+  int (*version)(struct android_log_logger *logger,
+                 struct android_log_transport_context *transp);
+  void (*close)(struct android_log_logger_list *logger_list,
+                struct android_log_transport_context *transp);
+
+  /*
+   * Expect all to instantiate open on any call, so we do not have
+   * an expicit open call
+   */
+  int (*read)(struct android_log_logger_list *logger_list,
+              struct android_log_transport_context *transp,
+              struct log_msg *log_msg);
+  /* Assumption is only called if not ANDROID_LOG_NONBLOCK */
+  int (*poll)(struct android_log_logger_list *logger_list,
+              struct android_log_transport_context *transp);
+
+  int (*clear)(struct android_log_logger *logger,
+               struct android_log_transport_context *transp);
+  ssize_t (*setSize)(struct android_log_logger *logger,
+                     struct android_log_transport_context *transp,
+                     size_t size);
+  ssize_t (*getSize)(struct android_log_logger *logger,
+                     struct android_log_transport_context *transp);
+  ssize_t (*getReadableSize)(struct android_log_logger *logger,
+                             struct android_log_transport_context *transp);
+
+  ssize_t (*getPrune)(struct android_log_logger_list *logger_list,
+                      struct android_log_transport_context *transp,
+                      char *buf, size_t len);
+  ssize_t (*setPrune)(struct android_log_logger_list *logger_list,
+                      struct android_log_transport_context *transp,
+                      char *buf, size_t len);
+  ssize_t (*getStats)(struct android_log_logger_list *logger_list,
+                      struct android_log_transport_context *transp,
+                      char *buf, size_t len);
+};
+
+struct android_log_logger_list {
+  struct listnode logger;
+  struct listnode transport;
+  int mode;
+  unsigned int tail;
+  log_time start;
+  pid_t pid;
+};
+
+struct android_log_logger {
+  struct listnode node;
+  struct android_log_logger_list *parent;
+
+  log_id_t logId;
+};
+
+struct android_log_transport_context {
+  struct listnode node;
+  union android_log_context context; /* zero init per-transport context */
+  struct android_log_logger_list *parent;
+
+  struct android_log_transport_read *transport;
+  unsigned logMask;
+  int ret;
+  struct log_msg logMsg; /* valid is logMsg.len != 0 */
+};
+
+/* assumes caller has structures read-locked, single threaded, or fenced */
+#define transport_context_for_each(transp, logger_list)              \
+  for (transp = node_to_item((logger_list)->transport.next,          \
+                             struct android_log_transport_context,   \
+                             node);                                  \
+       (transp != node_to_item(&(logger_list)->transport,            \
+                               struct android_log_transport_context, \
+                               node)) &&                             \
+           (transp->parent == (logger_list));                        \
+       transp = node_to_item(transp->node.next,                      \
+                             struct android_log_transport_context, node))
+
+#define logger_for_each(logp, logger_list)                          \
+    for (logp = node_to_item((logger_list)->logger.next,            \
+                             struct android_log_logger, node);      \
+         (logp != node_to_item(&(logger_list)->logger,              \
+                               struct android_log_logger, node)) && \
+             (logp->parent == (logger_list));                       \
+         logp = node_to_item((logp)->node.next,                     \
+                             struct android_log_logger, node))
+
+/* OS specific dribs and drabs */
+
+#if defined(_WIN32)
+typedef uint32_t uid_t;
+#endif
+
+LIBLOG_HIDDEN uid_t __android_log_uid();
+LIBLOG_HIDDEN pid_t __android_log_pid();
+LIBLOG_HIDDEN void __android_log_lock();
+LIBLOG_HIDDEN int __android_log_trylock();
+LIBLOG_HIDDEN void __android_log_unlock();
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_lock.c b/liblog/logger_lock.c
new file mode 100644
index 0000000..ee979bd
--- /dev/null
+++ b/liblog/logger_lock.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007-2016 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.
+ */
+
+/*
+ * Some OS specific dribs and drabs (locking etc).
+ */
+
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
+
+#include <private/android_filesystem_config.h>
+
+#include "logger.h"
+
+LIBLOG_HIDDEN uid_t __android_log_uid()
+{
+#if defined(_WIN32)
+    return AID_SYSTEM;
+#else
+    static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
+
+    if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
+        last_uid = getuid();
+    }
+    return last_uid;
+#endif
+}
+
+LIBLOG_HIDDEN pid_t __android_log_pid()
+{
+    static pid_t last_pid = (pid_t) -1;
+
+    if (last_pid == (pid_t) -1) {
+        last_pid = getpid();
+    }
+    return last_pid;
+}
+
+#if !defined(_WIN32)
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+LIBLOG_HIDDEN void __android_log_lock()
+{
+#if !defined(_WIN32)
+    /*
+     * If we trigger a signal handler in the middle of locked activity and the
+     * signal handler logs a message, we could get into a deadlock state.
+     */
+    pthread_mutex_lock(&log_init_lock);
+#endif
+}
+
+LIBLOG_HIDDEN int __android_log_trylock()
+{
+#if !defined(_WIN32)
+    return pthread_mutex_trylock(&log_init_lock);
+#else
+    return 0;
+#endif
+}
+
+LIBLOG_HIDDEN void __android_log_unlock()
+{
+#if !defined(_WIN32)
+    pthread_mutex_unlock(&log_init_lock);
+#endif
+}
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
new file mode 100644
index 0000000..b7ccac5
--- /dev/null
+++ b/liblog/logger_name.c
@@ -0,0 +1,65 @@
+/*
+** Copyright 2013-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#include "log_portability.h"
+
+/* In the future, we would like to make this list extensible */
+static const char *LOG_NAME[LOG_ID_MAX] = {
+    [LOG_ID_MAIN] = "main",
+    [LOG_ID_RADIO] = "radio",
+    [LOG_ID_EVENTS] = "events",
+    [LOG_ID_SYSTEM] = "system",
+    [LOG_ID_CRASH] = "crash",
+    [LOG_ID_SECURITY] = "security",
+    [LOG_ID_KERNEL] = "kernel",
+};
+
+LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
+{
+    if (log_id >= LOG_ID_MAX) {
+        log_id = LOG_ID_MAIN;
+    }
+    return LOG_NAME[log_id];
+}
+
+LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName)
+{
+    const char *b;
+    int ret;
+
+    if (!logName) {
+        return -1; /* NB: log_id_t is unsigned */
+    }
+    b = strrchr(logName, '/');
+    if (!b) {
+        b = logName;
+    } else {
+        ++b;
+    }
+
+    for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
+        const char *l = LOG_NAME[ret];
+        if (l && !strcmp(b, l)) {
+            return ret;
+        }
+    }
+    return -1;   /* should never happen */
+}
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
new file mode 100644
index 0000000..f15c7cd
--- /dev/null
+++ b/liblog/logger_read.c
@@ -0,0 +1,474 @@
+/*
+** Copyright 2013-2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/list.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+#include "config_read.h"
+#include "log_portability.h"
+#include "logger.h"
+
+/* android_logger_alloc unimplemented, no use case */
+/* android_logger_free not exported */
+static void android_logger_free(struct logger *logger)
+{
+    struct android_log_logger *logger_internal =
+            (struct android_log_logger *)logger;
+
+    if (!logger_internal) {
+        return;
+    }
+
+    list_remove(&logger_internal->node);
+
+    free(logger_internal);
+}
+
+/* android_logger_alloc unimplemented, no use case */
+
+/* method for getting the associated sublog id */
+LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger)
+{
+    return ((struct android_log_logger *)logger)->logId;
+}
+
+static int init_transport_context(struct android_log_logger_list *logger_list)
+{
+    struct android_log_transport_read *transport;
+    struct listnode *node;
+
+    if (!logger_list) {
+        return -EINVAL;
+    }
+
+    if (list_empty(&logger_list->logger)) {
+        return -EINVAL;
+    }
+
+    if (!list_empty(&logger_list->transport)) {
+        return 0;
+    }
+
+    __android_log_lock();
+    /* mini __write_to_log_initialize() to populate transports */
+    if (list_empty(&__android_log_transport_read) &&
+            list_empty(&__android_log_persist_read)) {
+        __android_log_config_read();
+    }
+    __android_log_unlock();
+
+    node = (logger_list->mode & ANDROID_LOG_PSTORE) ?
+            &__android_log_persist_read : &__android_log_transport_read;
+
+    read_transport_for_each(transport, node) {
+        struct android_log_transport_context *transp;
+        struct android_log_logger *logger;
+        unsigned logMask = 0;
+
+        logger_for_each(logger, logger_list) {
+            log_id_t logId = logger->logId;
+
+            if (transport->read &&
+                    (!transport->available ||
+                        (transport->available(logId) >= 0))) {
+                logMask |= 1 << logId;
+            }
+        }
+        if (!logMask) {
+            continue;
+        }
+        transp = calloc(1, sizeof(*transp));
+        if (!transp) {
+            return -ENOMEM;
+        }
+        transp->parent = logger_list;
+        transp->transport = transport;
+        transp->logMask = logMask;
+        transp->ret = 1;
+        list_add_tail(&logger_list->transport, &transp->node);
+    }
+    if (list_empty(&logger_list->transport)) {
+        return -ENODEV;
+    }
+    return 0;
+}
+
+#define LOGGER_FUNCTION(logger, def, func, args...)                           \
+    ssize_t ret = -EINVAL;                                                    \
+    struct android_log_transport_context *transp;                             \
+    struct android_log_logger *logger_internal =                              \
+            (struct android_log_logger *)logger;                              \
+                                                                              \
+    if (!logger_internal) {                                                   \
+        return ret;                                                           \
+    }                                                                         \
+    ret = init_transport_context(logger_internal->parent);                    \
+    if (ret < 0) {                                                            \
+        return ret;                                                           \
+    }                                                                         \
+                                                                              \
+    ret = (def);                                                              \
+    transport_context_for_each(transp, logger_internal->parent) {             \
+        if ((transp->logMask & (1 << logger_internal->logId)) &&              \
+                transp->transport && transp->transport->func) {               \
+            ssize_t retval = (transp->transport->func)(logger_internal,       \
+                                                       transp, ## args);      \
+            if ((ret >= 0) || (ret == (def))) {                               \
+                ret = retval;                                                 \
+            }                                                                 \
+        }                                                                     \
+    }                                                                         \
+    return ret
+
+LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger)
+{
+    LOGGER_FUNCTION(logger, -ENODEV, clear);
+}
+
+/* returns the total size of the log's ring buffer */
+LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger *logger)
+{
+    LOGGER_FUNCTION(logger, -ENODEV, getSize);
+}
+
+LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger,
+                                                  unsigned long size)
+{
+    LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
+}
+
+/*
+ * returns the readable size of the log's ring buffer (that is, amount of the
+ * log consumed)
+ */
+LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
+        struct logger *logger)
+{
+    LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
+}
+
+/*
+ * returns the logger version
+ */
+LIBLOG_ABI_PUBLIC int android_logger_get_log_version(struct logger *logger)
+{
+    LOGGER_FUNCTION(logger, 4, version);
+}
+
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                 \
+    struct android_log_transport_context *transp;                             \
+    struct android_log_logger_list *logger_list_internal =                    \
+            (struct android_log_logger_list *)logger_list;                    \
+                                                                              \
+    ssize_t ret = init_transport_context(logger_list_internal);               \
+    if (ret < 0) {                                                            \
+        return ret;                                                           \
+    }                                                                         \
+                                                                              \
+    ret = (def);                                                              \
+    transport_context_for_each(transp, logger_list_internal) {                \
+        if (transp->transport && (transp->transport->func)) {                 \
+            ssize_t retval = (transp->transport->func)(logger_list_internal,  \
+                                                       transp, ## args);      \
+            if ((ret >= 0) || (ret == (def))) {                               \
+                ret = retval;                                                 \
+            }                                                                 \
+        }                                                                     \
+    }                                                                         \
+    return ret
+
+/*
+ * returns statistics
+ */
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
+        struct logger_list *logger_list,
+        char *buf, size_t len)
+{
+    LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
+}
+
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
+        struct logger_list *logger_list,
+        char *buf, size_t len)
+{
+    LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
+}
+
+LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
+        struct logger_list *logger_list,
+        char *buf, size_t len)
+{
+    LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
+}
+
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
+        int mode,
+        unsigned int tail,
+        pid_t pid)
+{
+    struct android_log_logger_list *logger_list;
+
+    logger_list = calloc(1, sizeof(*logger_list));
+    if (!logger_list) {
+        return NULL;
+    }
+
+    list_init(&logger_list->logger);
+    list_init(&logger_list->transport);
+    logger_list->mode = mode;
+    logger_list->tail = tail;
+    logger_list->pid = pid;
+
+    return (struct logger_list *)logger_list;
+}
+
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc_time(
+        int mode,
+        log_time start,
+        pid_t pid)
+{
+    struct android_log_logger_list *logger_list;
+
+    logger_list = calloc(1, sizeof(*logger_list));
+    if (!logger_list) {
+        return NULL;
+    }
+
+    list_init(&logger_list->logger);
+    list_init(&logger_list->transport);
+    logger_list->mode = mode;
+    logger_list->start = start;
+    logger_list->pid = pid;
+
+    return (struct logger_list *)logger_list;
+}
+
+/* android_logger_list_register unimplemented, no use case */
+/* android_logger_list_unregister unimplemented, no use case */
+
+/* Open the named log and add it to the logger list */
+LIBLOG_ABI_PUBLIC struct logger *android_logger_open(
+        struct logger_list *logger_list,
+        log_id_t logId)
+{
+    struct android_log_logger_list *logger_list_internal =
+            (struct android_log_logger_list *)logger_list;
+    struct android_log_logger *logger;
+
+    if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
+        goto err;
+    }
+
+    logger_for_each(logger, logger_list_internal) {
+        if (logger->logId == logId) {
+            goto ok;
+        }
+    }
+
+    logger = calloc(1, sizeof(*logger));
+    if (!logger) {
+        goto err;
+    }
+
+    logger->logId = logId;
+    list_add_tail(&logger_list_internal->logger, &logger->node);
+    logger->parent = logger_list_internal;
+
+    /* Reset known transports to re-evaluate, we just added one */
+    while (!list_empty(&logger_list_internal->transport)) {
+        struct listnode *node = list_head(&logger_list_internal->transport);
+        struct android_log_transport_context *transp =
+                node_to_item(node, struct android_log_transport_context, node);
+
+        list_remove(&transp->node);
+        free(transp);
+    }
+    goto ok;
+
+err:
+    logger = NULL;
+ok:
+    return (struct logger *)logger;
+}
+
+/* Open the single named log and make it part of a new logger list */
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open(
+        log_id_t logId,
+        int mode,
+        unsigned int tail,
+        pid_t pid)
+{
+    struct logger_list *logger_list =
+            android_logger_list_alloc(mode, tail, pid);
+
+    if (!logger_list) {
+        return NULL;
+    }
+
+    if (!android_logger_open(logger_list, logId)) {
+        android_logger_list_free(logger_list);
+        return NULL;
+    }
+
+    return logger_list;
+}
+
+/* Read from the selected logs */
+LIBLOG_ABI_PUBLIC int android_logger_list_read(struct logger_list *logger_list,
+                                               struct log_msg *log_msg)
+{
+    struct android_log_transport_context *transp;
+    struct android_log_logger_list *logger_list_internal =
+            (struct android_log_logger_list *)logger_list;
+
+    int ret = init_transport_context(logger_list_internal);
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* at least one transport */
+    transp = node_to_item(logger_list_internal->transport.next,
+                          struct android_log_transport_context, node);
+
+    /* more than one transport? */
+    if (transp->node.next != &logger_list_internal->transport) {
+        /* Poll and merge sort the entries if from multiple transports */
+        struct android_log_transport_context *oldest = NULL;
+        int ret;
+        int polled = 0;
+        do {
+            if (polled) {
+                sched_yield();
+            }
+            ret = -1000;
+            polled = 0;
+            do {
+                int retval = transp->ret;
+                if ((retval > 0) && !transp->logMsg.entry.len) {
+                    if (!transp->transport->read) {
+                        retval = transp->ret = 0;
+                    } else if ((logger_list_internal->mode &
+                                ANDROID_LOG_NONBLOCK) ||
+                            !transp->transport->poll) {
+                        retval = transp->ret = (*transp->transport->read)(
+                                logger_list_internal,
+                                transp,
+                                &transp->logMsg);
+                    } else {
+                        int pollval = (*transp->transport->poll)(
+                                logger_list_internal, transp);
+                        if (pollval <= 0) {
+                            sched_yield();
+                            pollval = (*transp->transport->poll)(
+                                    logger_list_internal, transp);
+                        }
+                        polled = 1;
+                        if (pollval < 0) {
+                            if ((pollval == -EINTR) || (pollval == -EAGAIN)) {
+                                return -EAGAIN;
+                            }
+                            retval = transp->ret = pollval;
+                        } else if (pollval > 0) {
+                            retval = transp->ret = (*transp->transport->read)(
+                                    logger_list_internal,
+                                    transp,
+                                    &transp->logMsg);
+                        }
+                    }
+                }
+                if (ret < retval) {
+                    ret = retval;
+                }
+                if ((transp->ret > 0) && transp->logMsg.entry.len &&
+                        (!oldest ||
+                            (oldest->logMsg.entry.sec >
+                                transp->logMsg.entry.sec) ||
+                            ((oldest->logMsg.entry.sec ==
+                                    transp->logMsg.entry.sec) &&
+                                (oldest->logMsg.entry.nsec >
+                                    transp->logMsg.entry.nsec)))) {
+                    oldest = transp;
+                }
+                transp = node_to_item(transp->node.next,
+                                      struct android_log_transport_context,
+                                      node);
+            } while (transp != node_to_item(
+                    &logger_list_internal->transport,
+                    struct android_log_transport_context,
+                    node));
+            if (!oldest &&
+                    (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
+                return (ret < 0) ? ret : -EAGAIN;
+            }
+            transp = node_to_item(logger_list_internal->transport.next,
+                                  struct android_log_transport_context, node);
+        } while (!oldest && (ret > 0));
+        if (!oldest) {
+            return ret;
+        }
+        memcpy(log_msg, &oldest->logMsg, oldest->logMsg.entry.len +
+                    (oldest->logMsg.entry.hdr_size ?
+                        oldest->logMsg.entry.hdr_size :
+                        sizeof(struct logger_entry)));
+        oldest->logMsg.entry.len = 0; /* Mark it as copied */
+        return oldest->ret;
+    }
+
+    /* if only one, no need to copy into transport_context and merge-sort */
+    return (transp->transport->read)(logger_list_internal, transp, log_msg);
+}
+
+/* Close all the logs */
+LIBLOG_ABI_PUBLIC void android_logger_list_free(struct logger_list *logger_list)
+{
+    struct android_log_logger_list *logger_list_internal =
+            (struct android_log_logger_list *)logger_list;
+
+    if (logger_list_internal == NULL) {
+        return;
+    }
+
+    while (!list_empty(&logger_list_internal->transport)) {
+        struct listnode *node = list_head(&logger_list_internal->transport);
+        struct android_log_transport_context *transp =
+                node_to_item(node, struct android_log_transport_context, node);
+
+        if (transp->transport && transp->transport->close) {
+            (*transp->transport->close)(logger_list_internal, transp);
+        }
+        list_remove(&transp->node);
+        free(transp);
+    }
+
+    while (!list_empty(&logger_list_internal->logger)) {
+        struct listnode *node = list_head(&logger_list_internal->logger);
+        struct android_log_logger *logger =
+                node_to_item(node, struct android_log_logger, node);
+        android_logger_free((struct logger *)logger);
+    }
+
+    free(logger_list_internal);
+}
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
new file mode 100644
index 0000000..a4155e9
--- /dev/null
+++ b/liblog/logger_write.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2007-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#ifdef __BIONIC__
+#include <android/set_abort_message.h>
+#endif
+
+#include <log/event_tag_map.h>
+#include <log/logd.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+#define LOG_BUF_SIZE 1024
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code.  Basically, if /dev/socket/logd is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+    kLogUninitialized, kLogNotAvailable, kLogAvailable
+} g_log_status = kLogUninitialized;
+
+LIBLOG_ABI_PUBLIC int __android_log_dev_available()
+{
+    struct android_log_transport_write *node;
+    size_t i;
+
+    if (list_empty(&__android_log_transport_write)) {
+        return kLogUninitialized;
+    }
+    for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+        write_transport_for_each(node, &__android_log_transport_write) {
+            if (node->write &&
+                    (!node->available || ((*node->available)(i) >= 0))) {
+                return kLogAvailable;
+            }
+        }
+    }
+    return kLogNotAvailable;
+}
+
+/* log_init_lock assumed */
+static int __write_to_log_initialize()
+{
+    struct android_log_transport_write *transport;
+    struct listnode *n;
+    int i = 0, ret = 0;
+
+    __android_log_config_write();
+    write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+        if (!transport->open || ((*transport->open)() < 0)) {
+            if (transport->close) {
+                (*transport->close)();
+            }
+            list_remove(&transport->node);
+            continue;
+        }
+        ++ret;
+    }
+    write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+        if (!transport->open || ((*transport->open)() < 0)) {
+            if (transport->close) {
+                (*transport->close)();
+            }
+            list_remove(&transport->node);
+            continue;
+        }
+        ++i;
+    }
+    if (!ret && !i) {
+        return -ENODEV;
+    }
+
+    return ret;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream. le32toh open coded
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+    struct android_log_transport_write *node;
+    int ret;
+    struct timespec ts;
+    size_t len, i;
+
+    for (len = i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+    if (!len) {
+        return -EINVAL;
+    }
+
+#if defined(__BIONIC__)
+    if (log_id == LOG_ID_SECURITY) {
+        uid_t uid;
+
+        if (vec[0].iov_len < 4) {
+            return -EINVAL;
+        }
+
+        uid = __android_log_uid();
+        /* Matches clientHasLogCredentials() in logd */
+        if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+            uid = geteuid();
+            if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+                gid_t gid = getgid();
+                if ((gid != AID_SYSTEM) &&
+                        (gid != AID_ROOT) &&
+                        (gid != AID_LOG)) {
+                    gid = getegid();
+                    if ((gid != AID_SYSTEM) &&
+                            (gid != AID_ROOT) &&
+                            (gid != AID_LOG)) {
+                        int num_groups;
+                        gid_t *groups;
+
+                        num_groups = getgroups(0, NULL);
+                        if (num_groups <= 0) {
+                            return -EPERM;
+                        }
+                        groups = calloc(num_groups, sizeof(gid_t));
+                        if (!groups) {
+                            return -ENOMEM;
+                        }
+                        num_groups = getgroups(num_groups, groups);
+                        while (num_groups > 0) {
+                            if (groups[num_groups - 1] == AID_LOG) {
+                                break;
+                            }
+                            --num_groups;
+                        }
+                        free(groups);
+                        if (num_groups <= 0) {
+                            return -EPERM;
+                        }
+                    }
+                }
+            }
+        }
+        if (!__android_log_security()) {
+            /* If only we could reset downstream logd counter */
+            return -EPERM;
+        }
+    } else if (log_id == LOG_ID_EVENTS) {
+        static atomic_uintptr_t map;
+        const char *tag;
+        EventTagMap *m, *f;
+
+        if (vec[0].iov_len < 4) {
+            return -EINVAL;
+        }
+
+        tag = NULL;
+        f = NULL;
+        m = (EventTagMap *)atomic_load(&map);
+
+        if (!m) {
+            ret = __android_log_trylock();
+            m = (EventTagMap *)atomic_load(&map); /* trylock flush cache */
+            if (!m) {
+                m = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+                if (ret) { /* trylock failed, use local copy, mark for close */
+                    f = m;
+                } else {
+                    if (!m) { /* One chance to open map file */
+                        m = (EventTagMap *)(uintptr_t)-1LL;
+                    }
+                    atomic_store(&map, (uintptr_t)m);
+                }
+            }
+            if (!ret) { /* trylock succeeded, unlock */
+                __android_log_unlock();
+            }
+        }
+        if (m && (m != (EventTagMap *)(uintptr_t)-1LL)) {
+            tag = android_lookupEventTag(m, get4LE(vec[0].iov_base));
+        }
+        ret = __android_log_is_loggable(ANDROID_LOG_INFO,
+                                        tag,
+                                        ANDROID_LOG_VERBOSE);
+        if (f) { /* local copy marked for close */
+            android_closeEventTagMap(f);
+        }
+        if (!ret) {
+            return -EPERM;
+        }
+    } else {
+        /* Validate the incoming tag, tag content can not split across iovec */
+        char prio = ANDROID_LOG_VERBOSE;
+        const char *tag = vec[0].iov_base;
+        size_t len = vec[0].iov_len;
+        if (!tag) {
+            len = 0;
+        }
+        if (len > 0) {
+            prio = *tag;
+            if (len > 1) {
+                --len;
+                ++tag;
+            } else {
+                len = vec[1].iov_len;
+                tag = ((const char *)vec[1].iov_base);
+                if (!tag) {
+                    len = 0;
+                }
+            }
+        }
+        /* tag must be nul terminated */
+        if (strnlen(tag, len) >= len) {
+            tag = NULL;
+        }
+
+        if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+            return -EPERM;
+        }
+    }
+
+    clock_gettime(android_log_clockid(), &ts);
+#else
+    /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
+    {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        ts.tv_sec = tv.tv_sec;
+        ts.tv_nsec = tv.tv_usec * 1000;
+    }
+#endif
+
+    ret = 0;
+    write_transport_for_each(node, &__android_log_transport_write) {
+        if (node->write) {
+            ssize_t retval;
+            retval = (*node->write)(log_id, &ts, vec, nr);
+            if (ret >= 0) {
+                ret = retval;
+            }
+        }
+    }
+
+    write_transport_for_each(node, &__android_log_persist_write) {
+        if (node->write) {
+            (void)(*node->write)(log_id, &ts, vec, nr);
+        }
+    }
+
+    return ret;
+}
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+    __android_log_lock();
+
+    if (write_to_log == __write_to_log_init) {
+        int ret;
+
+        ret = __write_to_log_initialize();
+        if (ret < 0) {
+            __android_log_unlock();
+            if (!list_empty(&__android_log_persist_write)) {
+                __write_to_log_daemon(log_id, vec, nr);
+            }
+            return ret;
+        }
+
+        write_to_log = __write_to_log_daemon;
+    }
+
+    __android_log_unlock();
+
+    return write_to_log(log_id, vec, nr);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char *tag,
+                                          const char *msg)
+{
+    return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
+                                              const char *tag, const char *msg)
+{
+    struct iovec vec[3];
+    char tmp_tag[32];
+
+    if (!tag)
+        tag = "";
+
+    /* XXX: This needs to go! */
+    if ((bufID != LOG_ID_RADIO) &&
+         (!strcmp(tag, "HTC_RIL") ||
+        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
+        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
+        !strcmp(tag, "AT") ||
+        !strcmp(tag, "GSM") ||
+        !strcmp(tag, "STK") ||
+        !strcmp(tag, "CDMA") ||
+        !strcmp(tag, "PHONE") ||
+        !strcmp(tag, "SMS"))) {
+            bufID = LOG_ID_RADIO;
+            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
+            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+            tag = tmp_tag;
+    }
+
+#if __BIONIC__
+    if (prio == ANDROID_LOG_FATAL) {
+        android_set_abort_message(msg);
+    }
+#endif
+
+    vec[0].iov_base   = (unsigned char *) &prio;
+    vec[0].iov_len    = 1;
+    vec[1].iov_base   = (void *) tag;
+    vec[1].iov_len    = strlen(tag) + 1;
+    vec[2].iov_base   = (void *) msg;
+    vec[2].iov_len    = strlen(msg) + 1;
+
+    return write_to_log(bufID, vec, 3);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char *tag,
+                                           const char *fmt, va_list ap)
+{
+    char buf[LOG_BUF_SIZE];
+
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+    return __android_log_write(prio, tag, buf);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char *tag,
+                                          const char *fmt, ...)
+{
+    va_list ap;
+    char buf[LOG_BUF_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+
+    return __android_log_write(prio, tag, buf);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
+                                              const char *tag,
+                                              const char *fmt, ...)
+{
+    va_list ap;
+    char buf[LOG_BUF_SIZE];
+
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+
+    return __android_log_buf_write(bufID, prio, tag, buf);
+}
+
+LIBLOG_ABI_PUBLIC void __android_log_assert(const char *cond, const char *tag,
+                                            const char *fmt, ...)
+{
+    char buf[LOG_BUF_SIZE];
+
+    if (fmt) {
+        va_list ap;
+        va_start(ap, fmt);
+        vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+        va_end(ap);
+    } else {
+        /* Msg not provided, log condition.  N.B. Do not use cond directly as
+         * format string as it could contain spurious '%' syntax (e.g.
+         * "%d" in "blocks%devs == 0").
+         */
+        if (cond)
+            snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+        else
+            strcpy(buf, "Unspecified assertion failed");
+    }
+
+    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+    abort(); /* abort so we have a chance to debug the situation */
+    /* NOTREACHED */
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag,
+                                           const void *payload, size_t len)
+{
+    struct iovec vec[2];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = (void*)payload;
+    vec[1].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
+                                                    const void *payload,
+                                                    size_t len)
+{
+    struct iovec vec[2];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = (void*)payload;
+    vec[1].iov_len = len;
+
+    return write_to_log(LOG_ID_SECURITY, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well.  Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
+                                            const void *payload, size_t len)
+{
+    struct iovec vec[3];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = (void*)payload;
+    vec[2].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
+
+/*
+ * Like __android_log_bwrite, but used for writing strings to the
+ * event log.
+ */
+LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char *payload)
+{
+    struct iovec vec[4];
+    char type = EVENT_TYPE_STRING;
+    uint32_t len = strlen(payload);
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = &len;
+    vec[2].iov_len = sizeof(len);
+    vec[3].iov_base = (void*)payload;
+    vec[3].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 4);
+}
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
+                                                     const char *payload)
+{
+    struct iovec vec[4];
+    char type = EVENT_TYPE_STRING;
+    uint32_t len = strlen(payload);
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = &len;
+    vec[2].iov_len = sizeof(len);
+    vec[3].iov_base = (void*)payload;
+    vec[3].iov_len = len;
+
+    return write_to_log(LOG_ID_SECURITY, vec, 4);
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 19a3e27..d7de864 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -34,7 +34,7 @@
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 #define MS_PER_NSEC 1000000
 #define US_PER_NSEC 1000
@@ -1429,8 +1429,16 @@
      * possibly causing heap corruption.  To avoid this we double check and
      * set the length at the maximum (size minus null byte)
      */
-    prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
-    suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+    prefixLen += len;
+    if (prefixLen >= sizeof(prefixBuf)) {
+        prefixLen = sizeof(prefixBuf) - 1;
+        prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+    }
+    if (suffixLen >= sizeof(suffixBuf)) {
+        suffixLen = sizeof(suffixBuf) - 1;
+        suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+        suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+    }
 
     /* the following code is tragically unreadable */
 
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
new file mode 100644
index 0000000..5695e8a
--- /dev/null
+++ b/liblog/pmsg_reader.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2007-2016 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 <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_read.h"
+#include "logger.h"
+
+static int pmsgAvailable(log_id_t logId);
+static int pmsgVersion(struct android_log_logger *logger,
+                       struct android_log_transport_context *transp);
+static int pmsgRead(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp,
+                    struct log_msg *log_msg);
+static void pmsgClose(struct android_log_logger_list *logger_list,
+                      struct android_log_transport_context *transp);
+static int pmsgClear(struct android_log_logger *logger,
+                     struct android_log_transport_context *transp);
+
+LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = {
+    .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node },
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .version = pmsgVersion,
+    .read = pmsgRead,
+    .poll = NULL,
+    .close = pmsgClose,
+    .clear = pmsgClear,
+    .setSize = NULL,
+    .getSize = NULL,
+    .getReadableSize = NULL,
+    .getPrune = NULL,
+    .setPrune = NULL,
+    .getStats = NULL,
+};
+
+static int pmsgAvailable(log_id_t logId)
+{
+    if (logId > LOG_ID_SECURITY) {
+        return -EINVAL;
+    }
+    if (access("/dev/pmsg0", W_OK) == 0) {
+        return 0;
+    }
+    return -EBADF;
+}
+
+/* Determine the credentials of the caller */
+static bool uid_has_log_permission(uid_t uid)
+{
+    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
+}
+
+static uid_t get_best_effective_uid()
+{
+    uid_t euid;
+    uid_t uid;
+    gid_t gid;
+    ssize_t i;
+    static uid_t last_uid = (uid_t) -1;
+
+    if (last_uid != (uid_t) -1) {
+        return last_uid;
+    }
+    uid = __android_log_uid();
+    if (uid_has_log_permission(uid)) {
+        return last_uid = uid;
+    }
+    euid = geteuid();
+    if (uid_has_log_permission(euid)) {
+        return last_uid = euid;
+    }
+    gid = getgid();
+    if (uid_has_log_permission(gid)) {
+        return last_uid = gid;
+    }
+    gid = getegid();
+    if (uid_has_log_permission(gid)) {
+        return last_uid = gid;
+    }
+    i = getgroups((size_t) 0, NULL);
+    if (i > 0) {
+        gid_t list[i];
+
+        getgroups(i, list);
+        while (--i >= 0) {
+            if (uid_has_log_permission(list[i])) {
+                return last_uid = list[i];
+            }
+        }
+    }
+    return last_uid = uid;
+}
+
+static int pmsgClear(struct android_log_logger *logger __unused,
+                     struct android_log_transport_context *transp __unused)
+{
+    if (uid_has_log_permission(get_best_effective_uid())) {
+        return unlink("/sys/fs/pstore/pmsg-ramoops-0");
+    }
+    errno = EPERM;
+    return -1;
+}
+
+/*
+ * returns the logger version
+ */
+static int pmsgVersion(struct android_log_logger *logger __unused,
+                       struct android_log_transport_context *transp __unused)
+{
+    return 4;
+}
+
+static int pmsgRead(struct android_log_logger_list *logger_list,
+                    struct android_log_transport_context *transp,
+                    struct log_msg *log_msg)
+{
+    ssize_t ret;
+    off_t current, next;
+    uid_t uid;
+    struct android_log_logger *logger;
+    struct __attribute__((__packed__)) {
+        android_pmsg_log_header_t p;
+        android_log_header_t l;
+    } buf;
+    static uint8_t preread_count;
+    bool is_system;
+
+    memset(log_msg, 0, sizeof(*log_msg));
+
+    if (transp->context.fd <= 0) {
+        int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
+
+        if (fd < 0) {
+            return -errno;
+        }
+        if (fd == 0) { /* Argggg */
+            fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
+            close(0);
+            if (fd < 0) {
+                return -errno;
+            }
+        }
+        transp->context.fd = fd;
+        preread_count = 0;
+    }
+
+    while(1) {
+        if (preread_count < sizeof(buf)) {
+            ret = TEMP_FAILURE_RETRY(read(transp->context.fd,
+                                          &buf.p.magic + preread_count,
+                                          sizeof(buf) - preread_count));
+            if (ret < 0) {
+                return -errno;
+            }
+            preread_count += ret;
+        }
+        if (preread_count != sizeof(buf)) {
+            return preread_count ? -EIO : -EAGAIN;
+        }
+        if ((buf.p.magic != LOGGER_MAGIC)
+         || (buf.p.len <= sizeof(buf))
+         || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD))
+         || (buf.l.id >= LOG_ID_MAX)
+         || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) {
+            do {
+                memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
+            } while (preread_count && (buf.p.magic != LOGGER_MAGIC));
+            continue;
+        }
+        preread_count = 0;
+
+        if ((transp->logMask & (1 << buf.l.id)) &&
+                ((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
+                    ((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
+                        ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
+                            (logger_list->start.tv_nsec <=
+                                buf.l.realtime.tv_nsec)))) &&
+                (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
+            uid = get_best_effective_uid();
+            is_system = uid_has_log_permission(uid);
+            if (is_system || (uid == buf.p.uid)) {
+                ret = TEMP_FAILURE_RETRY(read(transp->context.fd,
+                                          is_system ?
+                                              log_msg->entry_v4.msg :
+                                              log_msg->entry_v3.msg,
+                                          buf.p.len - sizeof(buf)));
+                if (ret < 0) {
+                    return -errno;
+                }
+                if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+                    return -EIO;
+                }
+
+                log_msg->entry_v4.len = buf.p.len - sizeof(buf);
+                log_msg->entry_v4.hdr_size = is_system ?
+                    sizeof(log_msg->entry_v4) :
+                    sizeof(log_msg->entry_v3);
+                log_msg->entry_v4.pid = buf.p.pid;
+                log_msg->entry_v4.tid = buf.l.tid;
+                log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
+                log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
+                log_msg->entry_v4.lid = buf.l.id;
+                if (is_system) {
+                    log_msg->entry_v4.uid = buf.p.uid;
+                }
+
+                return ret;
+            }
+        }
+
+        current = TEMP_FAILURE_RETRY(lseek(transp->context.fd,
+                                           (off_t)0, SEEK_CUR));
+        if (current < 0) {
+            return -errno;
+        }
+        next = TEMP_FAILURE_RETRY(lseek(transp->context.fd,
+                                        (off_t)(buf.p.len - sizeof(buf)),
+                                        SEEK_CUR));
+        if (next < 0) {
+            return -errno;
+        }
+        if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
+            return -EIO;
+        }
+    }
+}
+
+static void pmsgClose(struct android_log_logger_list *logger_list __unused,
+                      struct android_log_transport_context *transp) {
+    if (transp->context.fd > 0) {
+        close (transp->context.fd);
+    }
+    transp->context.fd = 0;
+}
+
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_read(
+        log_id_t logId,
+        char prio,
+        const char *prefix,
+        __android_log_pmsg_file_read_fn fn, void *arg) {
+    ssize_t ret;
+    struct android_log_logger_list logger_list;
+    struct android_log_transport_context transp;
+    struct content {
+        struct listnode node;
+        union {
+            struct logger_entry_v4 entry;
+            struct logger_entry_v4 entry_v4;
+            struct logger_entry_v3 entry_v3;
+            struct logger_entry_v2 entry_v2;
+            struct logger_entry    entry_v1;
+        };
+    } *content;
+    struct names {
+        struct listnode node;
+        struct listnode content;
+        log_id_t id;
+        char prio;
+        char name[];
+    } *names;
+    struct listnode name_list;
+    struct listnode *node, *n;
+    size_t len, prefix_len;
+
+    if (!fn) {
+        return -EINVAL;
+    }
+
+    /* Add just enough clues in logger_list and transp to make API function */
+    memset(&logger_list, 0, sizeof(logger_list));
+    memset(&transp, 0, sizeof(transp));
+
+    logger_list.mode = ANDROID_LOG_PSTORE |
+                       ANDROID_LOG_NONBLOCK |
+                       ANDROID_LOG_RDONLY;
+    transp.logMask = (unsigned)-1;
+    if (logId != LOG_ID_ANY) {
+        transp.logMask = (1 << logId);
+    }
+    transp.logMask &= ~((1 << LOG_ID_KERNEL) |
+                        (1 << LOG_ID_EVENTS) |
+                        (1 << LOG_ID_SECURITY));
+    if (!transp.logMask) {
+        return -EINVAL;
+    }
+
+    /* Initialize name list */
+    list_init(&name_list);
+
+    ret = SSIZE_MAX;
+
+    /* Validate incoming prefix, shift until it contains only 0 or 1 : or / */
+    prefix_len = 0;
+    if (prefix) {
+        const char *prev = NULL, *last = NULL, *cp = prefix;
+        while ((cp = strpbrk(cp, "/:"))) {
+            prev = last;
+            last = cp;
+            cp = cp + 1;
+        }
+        if (prev) {
+            prefix = prev + 1;
+        }
+        prefix_len = strlen(prefix);
+    }
+
+    /* Read the file content */
+    while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
+        char *cp;
+        size_t hdr_size = transp.logMsg.entry.hdr_size ?
+            transp.logMsg.entry.hdr_size : sizeof(transp.logMsg.entry_v1);
+        char *msg = (char *)&transp.logMsg + hdr_size;
+        char *split = NULL;
+
+        /* Check for invalid sequence number */
+        if ((transp.logMsg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE) ||
+                ((transp.logMsg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+                    ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE)) {
+            continue;
+        }
+
+        /* Determine if it has <dirbase>:<filebase> format for tag */
+        len = transp.logMsg.entry.len - sizeof(prio);
+        for (cp = msg + sizeof(prio);
+                *cp && isprint(*cp) && !isspace(*cp) && --len;
+                ++cp) {
+            if (*cp == ':') {
+                if (split) {
+                    break;
+                }
+                split = cp;
+            }
+        }
+        if (*cp || !split) {
+            continue;
+        }
+
+        /* Filters */
+        if (prefix_len && strncmp(msg + sizeof(prio), prefix, prefix_len)) {
+            size_t offset;
+            /*
+             *   Allow : to be a synonym for /
+             * Things we do dealing with const char * and do not alloc
+             */
+            split = strchr(prefix, ':');
+            if (split) {
+                continue;
+            }
+            split = strchr(prefix, '/');
+            if (!split) {
+                continue;
+            }
+            offset = split - prefix;
+            if ((msg[offset + sizeof(prio)] != ':') ||
+                    strncmp(msg + sizeof(prio), prefix, offset)) {
+                continue;
+            }
+            ++offset;
+            if ((prefix_len > offset) &&
+                    strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
+                continue;
+            }
+        }
+
+        if ((prio != ANDROID_LOG_ANY) && (*msg < prio)) {
+            continue;
+        }
+
+        /* check if there is an existing entry */
+        list_for_each(node, &name_list) {
+            names = node_to_item(node, struct names, node);
+            if (!strcmp(names->name, msg + sizeof(prio)) &&
+                    (names->id == transp.logMsg.entry.lid) &&
+                    (names->prio == *msg)) {
+                break;
+            }
+        }
+
+        /* We do not have an existing entry, create and add one */
+        if (node == &name_list) {
+            static const char numbers[] = "0123456789";
+            unsigned long long nl;
+
+            len = strlen(msg + sizeof(prio)) + 1;
+            names = calloc(1, sizeof(*names) + len);
+            if (!names) {
+                ret = -ENOMEM;
+                break;
+            }
+            strcpy(names->name, msg + sizeof(prio));
+            names->id = transp.logMsg.entry.lid;
+            names->prio = *msg;
+            list_init(&names->content);
+            /*
+             * Insert in reverse numeric _then_ alpha sorted order as
+             * representative of log rotation:
+             *
+             *   log.10
+             *   klog.10
+             *   . . .
+             *   log.2
+             *   klog.2
+             *   log.1
+             *   klog.1
+             *   log
+             *   klog
+             *
+             * thus when we present the content, we are provided the oldest
+             * first, which when 'refreshed' could spill off the end of the
+             * pmsg FIFO but retaining the newest data for last with best
+             * chances to survive.
+             */
+            nl = 0;
+            cp = strpbrk(names->name, numbers);
+            if (cp) {
+                nl = strtoull(cp, NULL, 10);
+            }
+            list_for_each_reverse(node, &name_list) {
+                struct names *a_name = node_to_item(node, struct names, node);
+                const char *r = a_name->name;
+                int compare = 0;
+
+                unsigned long long nr = 0;
+                cp = strpbrk(r, numbers);
+                if (cp) {
+                    nr = strtoull(cp, NULL, 10);
+                }
+                if (nr != nl) {
+                    compare = (nl > nr) ? 1 : -1;
+                }
+                if (compare == 0) {
+                    compare = strcmp(names->name, r);
+                }
+                if (compare <= 0) {
+                    break;
+                }
+            }
+            list_add_head(node, &names->node);
+        }
+
+        /* Remove any file fragments that match our sequence number */
+        list_for_each_safe(node, n, &names->content) {
+            content = node_to_item(node, struct content, node);
+            if (transp.logMsg.entry.nsec == content->entry.nsec) {
+                list_remove(&content->node);
+                free(content);
+            }
+        }
+
+        /* Add content */
+        content = calloc(1, sizeof(content->node) +
+                hdr_size + transp.logMsg.entry.len);
+        if (!content) {
+            ret = -ENOMEM;
+            break;
+        }
+        memcpy(&content->entry, &transp.logMsg.entry,
+               hdr_size + transp.logMsg.entry.len);
+
+        /* Insert in sequence number sorted order, to ease reconstruction */
+        list_for_each_reverse(node, &names->content) {
+            if ((node_to_item(node, struct content, node))->entry.nsec <
+                    transp.logMsg.entry.nsec) {
+                break;
+            }
+        }
+        list_add_head(node, &content->node);
+    }
+    pmsgClose(&logger_list, &transp);
+
+    /* Progress through all the collected files */
+    list_for_each_safe(node, n, &name_list) {
+        struct listnode *content_node, *m;
+        char *buf;
+        size_t sequence, tag_len;
+
+        names = node_to_item(node, struct names, node);
+
+        /* Construct content into a linear buffer */
+        buf = NULL;
+        len = 0;
+        sequence = 0;
+        tag_len = strlen(names->name) + sizeof(char); /* tag + nul */
+        list_for_each_safe(content_node, m, &names->content) {
+            ssize_t add_len;
+
+            content = node_to_item(content_node, struct content, node);
+            add_len = content->entry.len - tag_len - sizeof(prio);
+            if (add_len <= 0) {
+                list_remove(content_node);
+                free(content);
+                continue;
+            }
+
+            if (!buf) {
+                buf = malloc(sizeof(char));
+                if (!buf) {
+                    ret = -ENOMEM;
+                    list_remove(content_node);
+                    free(content);
+                    continue;
+                }
+                *buf = '\0';
+            }
+
+            /* Missing sequence numbers */
+            while (sequence < content->entry.nsec) {
+                /* plus space for enforced nul */
+                buf = realloc(buf, len + sizeof(char) + sizeof(char));
+                if (!buf) {
+                    break;
+                }
+                buf[len] = '\f'; /* Mark missing content with a form feed */
+                buf[++len] = '\0';
+                sequence += ANDROID_LOG_PMSG_FILE_SEQUENCE;
+            }
+            if (!buf) {
+                ret = -ENOMEM;
+                list_remove(content_node);
+                free(content);
+                continue;
+            }
+            /* plus space for enforced nul */
+            buf = realloc(buf, len + add_len + sizeof(char));
+            if (!buf) {
+                ret = -ENOMEM;
+                list_remove(content_node);
+                free(content);
+                continue;
+            }
+            memcpy(buf + len,
+                   (char *)&content->entry + content->entry.hdr_size +
+                       tag_len + sizeof(prio),
+                   add_len);
+            len += add_len;
+            buf[len] = '\0'; /* enforce trailing hidden nul */
+            sequence = content->entry.nsec + ANDROID_LOG_PMSG_FILE_SEQUENCE;
+
+            list_remove(content_node);
+            free(content);
+        }
+        if (buf) {
+            if (len) {
+                /* Buffer contains enforced trailing nul just beyond length */
+                ssize_t r;
+                *strchr(names->name, ':') = '/'; /* Convert back to filename */
+                r = (*fn)(names->id, names->prio, names->name, buf, len, arg);
+                if ((ret >= 0) && (r > 0)) {
+                    if (ret == SSIZE_MAX) {
+                        ret = r;
+                    } else {
+                        ret += r;
+                    }
+                } else if (r < ret) {
+                    ret = r;
+                }
+            }
+            free(buf);
+        }
+        list_remove(node);
+        free(names);
+    }
+    return (ret == SSIZE_MAX) ? -ENOENT : ret;
+}
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
new file mode 100644
index 0000000..7034ceb
--- /dev/null
+++ b/liblog/pmsg_writer.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2007-2016 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.
+ */
+
+/*
+ * pmsg write handler
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static int pmsgOpen();
+static void pmsgClose();
+static int pmsgAvailable(log_id_t logId);
+static int pmsgWrite(log_id_t logId, struct timespec *ts,
+                      struct iovec *vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
+    .node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
+    .context.fd = -1,
+    .name = "pmsg",
+    .available = pmsgAvailable,
+    .open = pmsgOpen,
+    .close = pmsgClose,
+    .write = pmsgWrite,
+};
+
+static int pmsgOpen()
+{
+    if (pmsgLoggerWrite.context.fd < 0) {
+        pmsgLoggerWrite.context.fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    }
+
+    return pmsgLoggerWrite.context.fd;
+}
+
+static void pmsgClose()
+{
+    if (pmsgLoggerWrite.context.fd >= 0) {
+        close(pmsgLoggerWrite.context.fd);
+        pmsgLoggerWrite.context.fd = -1;
+    }
+}
+
+static int pmsgAvailable(log_id_t logId)
+{
+    if (logId > LOG_ID_SECURITY) {
+        return -EINVAL;
+    }
+    if (pmsgLoggerWrite.context.fd < 0) {
+        if (access("/dev/pmsg0", W_OK) == 0) {
+            return 0;
+        }
+        return -EBADF;
+    }
+    return 1;
+}
+
+static int pmsgWrite(log_id_t logId, struct timespec *ts,
+                      struct iovec *vec, size_t nr)
+{
+    static const unsigned headerLength = 2;
+    struct iovec newVec[nr + headerLength];
+    android_log_header_t header;
+    android_pmsg_log_header_t pmsgHeader;
+    size_t i, payloadSize;
+    ssize_t ret;
+
+    if (pmsgLoggerWrite.context.fd < 0) {
+        return -EBADF;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsgHeader;
+     *      // what we provide to file
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    pmsgHeader.magic = LOGGER_MAGIC;
+    pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
+    pmsgHeader.uid = __android_log_uid();
+    pmsgHeader.pid = __android_log_pid();
+
+    header.id = logId;
+    header.tid = gettid();
+    header.realtime.tv_sec = ts->tv_sec;
+    header.realtime.tv_nsec = ts->tv_nsec;
+
+    newVec[0].iov_base   = (unsigned char *)&pmsgHeader;
+    newVec[0].iov_len    = sizeof(pmsgHeader);
+    newVec[1].iov_base   = (unsigned char *)&header;
+    newVec[1].iov_len    = sizeof(header);
+
+    for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+        newVec[i].iov_base = vec[i - headerLength].iov_base;
+        payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+        if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+            newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+            if (newVec[i].iov_len) {
+                ++i;
+            }
+            payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
+            break;
+        }
+    }
+    pmsgHeader.len += payloadSize;
+
+    ret = TEMP_FAILURE_RETRY(writev(pmsgLoggerWrite.context.fd, newVec, i));
+    if (ret < 0) {
+        ret = errno ? -errno : -ENOTCONN;
+    }
+
+    if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
+        ret -= sizeof(header) - sizeof(pmsgHeader);
+    }
+
+    return ret;
+}
+
+/*
+ * Virtual pmsg filesystem
+ *
+ * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
+ * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
+ * file.
+ *
+ * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
+ */
+
+static inline const char *strnrchr(const char *buf, size_t len, char c) {
+    const char *cp = buf + len;
+    while ((--cp > buf) && (*cp != c));
+    if (cp <= buf) {
+        return buf + len;
+    }
+    return cp;
+}
+
+/* Write a buffer as filename references (tag = <basedir>:<basename>) */
+LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(
+        log_id_t logId,
+        char prio,
+        const char *filename,
+        const char *buf, size_t len) {
+    int fd;
+    size_t length, packet_len;
+    const char *tag;
+    char *cp, *slash;
+    struct timespec ts;
+    struct iovec vec[3];
+
+    /* Make sure the logId value is not a bad idea */
+    if ((logId == LOG_ID_KERNEL) ||       /* Verbotten */
+            (logId == LOG_ID_EVENTS) ||   /* Do not support binary content */
+            (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
+            ((unsigned)logId >= 32)) {    /* fit within logMask on arch32 */
+        return -EINVAL;
+    }
+
+    clock_gettime(android_log_clockid(), &ts);
+
+    cp = strdup(filename);
+    if (!cp) {
+        return -ENOMEM;
+    }
+
+    fd = pmsgLoggerWrite.context.fd;
+    if (fd < 0) {
+        __android_log_lock();
+        fd = pmsgOpen();
+        __android_log_unlock();
+        if (fd < 0) {
+            return -EBADF;
+        }
+    }
+
+    tag = cp;
+    slash = strrchr(cp, '/');
+    if (slash) {
+        *slash = ':';
+        slash = strrchr(cp, '/');
+        if (slash) {
+            tag = slash + 1;
+        }
+    }
+
+    length = strlen(tag) + 1;
+    packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
+
+    vec[0].iov_base = &prio;
+    vec[0].iov_len  = sizeof(char);
+    vec[1].iov_base = (unsigned char *)tag;
+    vec[1].iov_len  = length;
+
+    for (ts.tv_nsec = 0, length = len;
+            length;
+            ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+        ssize_t ret;
+        size_t transfer;
+
+        if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
+                ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+            len -= length;
+            break;
+        }
+
+        transfer = length;
+        if (transfer > packet_len) {
+            transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
+            if ((transfer < length) && (buf[transfer] == '\n')) {
+                ++transfer;
+            }
+        }
+
+        vec[2].iov_base = (unsigned char *)buf;
+        vec[2].iov_len  = transfer;
+
+        ret = pmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
+
+        if (ret <= 0) {
+            free(cp);
+            return ret;
+        }
+        length -= transfer;
+        buf += transfer;
+    }
+    free(cp);
+    return len;
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 50ecd2d..941b9b9 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <semaphore.h>
 #include <signal.h>
 #include <string.h>
 #include <sys/types.h>
@@ -531,9 +532,15 @@
 }
 
 static unsigned signaled;
-log_time signal_time;
+static log_time signal_time;
 
-static void caught_blocking(int /*signum*/)
+/*
+ *  Strictly, we are not allowed to log messages in a signal context, but we
+ * do make an effort to keep the failure surface minimized, and this in-effect
+ * should catch any regressions in that effort. The odds of a logged message
+ * in a signal handler causing a lockup problem should be _very_ small.
+ */
+static void caught_blocking_signal(int /*signum*/)
 {
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -583,7 +590,7 @@
     }
 }
 
-TEST(liblog, android_logger_list_read__cpu) {
+TEST(liblog, android_logger_list_read__cpu_signal) {
     struct logger_list *logger_list;
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -606,7 +613,7 @@
 
     memset(&signal_time, 0, sizeof(signal_time));
 
-    signal(SIGALRM, caught_blocking);
+    signal(SIGALRM, caught_blocking_signal);
     alarm(alarm_time);
 
     signaled = 0;
@@ -651,7 +658,158 @@
     alarm(0);
     signal(SIGALRM, SIG_DFL);
 
-    EXPECT_LT(1, count);
+    EXPECT_LE(1, count);
+
+    EXPECT_EQ(1, signals);
+
+    android_logger_list_close(logger_list);
+
+    unsigned long long uticks_end;
+    unsigned long long sticks_end;
+    get_ticks(&uticks_end, &sticks_end);
+
+    // Less than 1% in either user or system time, or both
+    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;
+    EXPECT_GT(one_percent_ticks, user_ticks);
+    EXPECT_GT(one_percent_ticks, system_ticks);
+    EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+}
+
+/*
+ *  Strictly, we are not allowed to log messages in a signal context, the
+ * correct way to handle this is to ensure the messages are constructed in
+ * a thread; the signal handler should only unblock the thread.
+ */
+static sem_t thread_trigger;
+
+static void caught_blocking_thread(int /*signum*/)
+{
+    sem_post(&thread_trigger);
+}
+
+static void *running_thread(void *) {
+    unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+    v += getpid() & 0xFFFF;
+
+    struct timespec timeout;
+    clock_gettime(CLOCK_REALTIME, &timeout);
+    timeout.tv_sec += 55;
+    sem_timedwait(&thread_trigger, &timeout);
+
+    ++signaled;
+    if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+        signal_time = log_time(CLOCK_MONOTONIC);
+        signal_time.tv_sec += 2;
+    }
+
+    LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+    return NULL;
+}
+
+int start_thread()
+{
+    sem_init(&thread_trigger, 0, 0);
+
+    pthread_attr_t attr;
+    if (pthread_attr_init(&attr)) {
+        return -1;
+    }
+
+    struct sched_param param;
+
+    memset(&param, 0, sizeof(param));
+    pthread_attr_setschedparam(&attr, &param);
+    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+
+    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+        pthread_attr_destroy(&attr);
+        return -1;
+    }
+
+    pthread_t thread;
+    if (pthread_create(&thread, &attr, running_thread, NULL)) {
+        pthread_attr_destroy(&attr);
+        return -1;
+    }
+
+    pthread_attr_destroy(&attr);
+    return 0;
+}
+
+TEST(liblog, android_logger_list_read__cpu_thread) {
+    struct logger_list *logger_list;
+    unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+    pid_t pid = getpid();
+
+    v += pid & 0xFFFF;
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+    int count = 0;
+
+    int signals = 0;
+
+    unsigned long long uticks_start;
+    unsigned long long sticks_start;
+    get_ticks(&uticks_start, &sticks_start);
+
+    const unsigned alarm_time = 10;
+
+    memset(&signal_time, 0, sizeof(signal_time));
+
+    signaled = 0;
+    EXPECT_EQ(0, start_thread());
+
+    signal(SIGALRM, caught_blocking_thread);
+    alarm(alarm_time);
+
+    do {
+        log_msg log_msg;
+        if (LOG_FAILURE_RETRY(android_logger_list_read(logger_list, &log_msg)) <= 0) {
+            break;
+        }
+
+        alarm(alarm_time);
+
+        ++count;
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.len != (4 + 1 + 8))
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        if (eventData[4] != EVENT_TYPE_LONG) {
+            continue;
+        }
+
+        unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
+        l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
+        l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
+        l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
+        l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
+        l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
+        l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
+        l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+
+        if (l == v) {
+            ++signals;
+            break;
+        }
+    } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+    alarm(0);
+    signal(SIGALRM, SIG_DFL);
+
+    EXPECT_LE(1, count);
 
     EXPECT_EQ(1, signals);
 
@@ -799,7 +957,10 @@
 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 of your grace";
+Never came trouble to my house in the likeness of your grace,\n\
+for trouble being gone, comfort should remain, but\n\
+when you depart from me, sorrow abides and happiness\n\
+takes his leave.";
 
 TEST(liblog, max_payload) {
     pid_t pid = getpid();
@@ -861,6 +1022,60 @@
     EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
 }
 
+TEST(liblog, __android_log_buf_print__maxtag) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    log_time ts(android_log_clockid());
+
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                         max_payload_buf, max_payload_buf));
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.sec < (ts.tv_sec - 1))
+         || ((ts.tv_sec + 1) < log_msg.entry.sec)
+         || ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD)
+         || (log_msg.id() != LOG_ID_MAIN)) {
+            continue;
+        }
+
+        ++count;
+
+        AndroidLogFormat *logformat = android_log_format_new();
+        EXPECT_TRUE(NULL != logformat);
+        AndroidLogEntry entry;
+        int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+                                                            &entry);
+        EXPECT_EQ(0, processLogBuffer);
+        if (processLogBuffer == 0) {
+            fflush(stderr);
+            int printLogLine =
+                    android_log_printLogLine(logformat, fileno(stderr), &entry);
+            EXPECT_LE(128, printLogLine);
+            EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD, printLogLine);
+        }
+        android_log_format_free(logformat);
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
 TEST(liblog, too_big_payload) {
     pid_t pid = getpid();
     static const char big_payload_tag[] = "TEST_big_payload_XXXX";
@@ -978,11 +1193,20 @@
         struct logger * 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));
-        /* crash buffer is allowed to be empty, that is actually healthy! */
-        if (android_logger_get_log_readable_size(logger) ||
-                (strcmp("crash", name) && strcmp("security", name))) {
-            EXPECT_LT(0, android_logger_get_log_readable_size(logger));
+        ssize_t get_log_size = android_logger_get_log_size(logger);
+        /* security buffer is allowed to be denied */
+        if (strcmp("security", name)) {
+            EXPECT_LT(0, get_log_size);
+            /* crash buffer is allowed to be empty, that is actually healthy! */
+            EXPECT_LE((strcmp("crash", name)) != 0,
+                      android_logger_get_log_readable_size(logger));
+        } else {
+            EXPECT_NE(0, get_log_size);
+            if (get_log_size < 0) {
+                EXPECT_GT(0, android_logger_get_log_readable_size(logger));
+            } else {
+                EXPECT_LE(0, android_logger_get_log_readable_size(logger));
+            }
         }
         EXPECT_LT(0, android_logger_get_log_version(logger));
     }
@@ -2299,3 +2523,56 @@
     EXPECT_LE(0, android_log_destroy(&ctx));
     ASSERT_TRUE(NULL == ctx);
 }
+
+static const char __pmsg_file[] =
+        "/data/william-shakespeare/MuchAdoAboutNothing.txt";
+
+TEST(liblog, __android_log_pmsg_file_write) {
+    EXPECT_LT(0, __android_log_pmsg_file_write(
+            LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+            __pmsg_file, max_payload_buf, sizeof(max_payload_buf)));
+    fprintf(stderr, "Reboot, ensure file %s matches\n"
+                    "with liblog.__android_log_msg_file_read test\n",
+                    __pmsg_file);
+}
+
+ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
+                  const char *buf, size_t len, void *arg) {
+    EXPECT_TRUE(NULL == arg);
+    EXPECT_EQ(LOG_ID_CRASH, logId);
+    EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
+    EXPECT_FALSE(NULL == strstr(__pmsg_file, filename));
+    EXPECT_EQ(len, sizeof(max_payload_buf));
+    EXPECT_EQ(0, strcmp(max_payload_buf, buf));
+
+    ++signaled;
+    if ((len != sizeof(max_payload_buf)) ||
+            strcmp(max_payload_buf, buf)) {
+        fprintf(stderr, "comparison fails on content \"%s\"\n", buf);
+    }
+    return !arg ||
+           (LOG_ID_CRASH != logId) ||
+           (ANDROID_LOG_VERBOSE != prio) ||
+           !strstr(__pmsg_file, filename) ||
+           (len != sizeof(max_payload_buf)) ||
+           !!strcmp(max_payload_buf, buf) ? -ENOEXEC : 1;
+}
+
+TEST(liblog, __android_log_pmsg_file_read) {
+    signaled = 0;
+
+    ssize_t ret = __android_log_pmsg_file_read(
+            LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
+            __pmsg_file, __pmsg_fn, NULL);
+
+    if (ret == -ENOENT) {
+        fprintf(stderr,
+            "No pre-boot results of liblog.__android_log_mesg_file_write to "
+            "compare with,\n"
+            "false positive test result.\n");
+        return;
+    }
+
+    EXPECT_LT(0, ret);
+    EXPECT_EQ(1U, signaled);
+}
diff --git a/liblog/uio.c b/liblog/uio.c
index d0184dc..ac0558f 100644
--- a/liblog/uio.c
+++ b/liblog/uio.c
@@ -20,7 +20,7 @@
 
 #include <log/uio.h>
 
-#include "log_cdefs.h"
+#include "log_portability.h"
 
 LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec *vecs, int count)
 {
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index b16c0e6..1bd3b8f 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -26,7 +26,7 @@
 namespace android {
 
 __attribute__((visibility("default")))
-void PreloadPublicNativeLibraries();
+void InitializeNativeLoader();
 
 __attribute__((visibility("default")))
 jstring CreateClassLoaderNamespace(JNIEnv* env,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 86d9d77..899c98c 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -29,32 +29,14 @@
 #include <string>
 #include <mutex>
 
+#include "android-base/file.h"
 #include "android-base/macros.h"
 #include "android-base/strings.h"
 
 namespace android {
 
-#ifdef __ANDROID__
-// TODO(dimitry): move this to system properties.
-static const char* kPublicNativeLibraries = "libandroid.so:"
-                                            "libc.so:"
-                                            "libdl.so:"
-                                            "libEGL.so:"
-                                            "libGLESv1_CM.so:"
-                                            "libGLESv2.so:"
-                                            "libGLESv3.so:"
-                                            "libicui18n.so:"
-                                            "libicuuc.so:"
-                                            "libjnigraphics.so:"
-                                            "liblog.so:"
-                                            "libmediandk.so:"
-                                            "libm.so:"
-                                            "libOpenMAXAL.so:"
-                                            "libOpenSLES.so:"
-                                            "libRS.so:"
-                                            "libstdc++.so:"
-                                            "libwebviewchromium_plat_support.so:"
-                                            "libz.so";
+#if defined(__ANDROID__)
+static constexpr const char* kPublicNativeLibrariesConfig = "/system/etc/public.libraries.txt";
 
 class LibraryNamespaces {
  public:
@@ -79,7 +61,8 @@
 
     android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
 
-    LOG_FATAL_IF(ns != nullptr, "There is already a namespace associated with this classloader");
+    LOG_ALWAYS_FATAL_IF(ns != nullptr,
+                        "There is already a namespace associated with this classloader");
 
     uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
     if (is_shared) {
@@ -109,10 +92,30 @@
     return it != namespaces_.end() ? it->second : nullptr;
   }
 
-  void PreloadPublicLibraries() {
+  void Initialize() {
+    // Read list of public native libraries from the config file.
+    std::string file_content;
+    LOG_ALWAYS_FATAL_IF(!base::ReadFileToString(kPublicNativeLibrariesConfig, &file_content),
+                        "Error reading public native library list from \"%s\": %s",
+                        kPublicNativeLibrariesConfig, strerror(errno));
+
+    std::vector<std::string> lines = base::Split(file_content, "\n");
+
+    std::vector<std::string> sonames;
+
+    for (const auto& line : lines) {
+      auto trimmed_line = base::Trim(line);
+      if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+        continue;
+      }
+
+      sonames.push_back(trimmed_line);
+    }
+
+    public_libraries_ = base::Join(sonames, ':');
+
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
-    std::vector<std::string> sonames = android::base::Split(kPublicNativeLibraries, ":");
     for (const auto& soname : sonames) {
       dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
     }
@@ -120,15 +123,19 @@
 
  private:
   bool InitPublicNamespace(const char* library_path) {
-    // Some apps call dlopen from generated code unknown to linker in which
-    // case linker uses anonymous namespace. See b/25844435 for details.
-    initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
+    // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+    // code is one example) unknown to linker in which  case linker uses anonymous
+    // namespace. The second argument specifies the search path for the anonymous
+    // namespace which is the library_path of the classloader.
+    initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
 
     return initialized_;
   }
 
   bool initialized_;
   std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
+  std::string public_libraries_;
+
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
@@ -141,10 +148,10 @@
 }
 #endif
 
-void PreloadPublicNativeLibraries() {
+void InitializeNativeLoader() {
 #if defined(__ANDROID__)
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  g_namespaces->PreloadPublicLibraries();
+  g_namespaces->Initialize();
 #endif
 }
 
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index 0dc59a4..333a7e6 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -183,6 +183,7 @@
   base::StatisticsRecorder::GetHistograms(&histograms);
 
   histogram_snapshot_manager_.PrepareDeltas(
+      histograms.begin(), histograms.end(),
       base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
 
   // Gather and reset the crash counters, shared with the binder threads.
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 5ab6195..60834fe 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -43,7 +43,14 @@
 
     uint32_t tags_addr;    /* physical addr for kernel tags */
     uint32_t page_size;    /* flash page size we assume */
-    uint32_t unused[2];    /* future expansion: should be 0 */
+    uint32_t unused;       /* reserved for future expansion: MUST be 0 */
+
+    /* operating system version and security patch level; for
+     * version "A.B.C" and patch level "Y-M-D":
+     * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C)
+     * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M)
+     * os_version = ver << 11 | lvl */
+    uint32_t os_version;
 
     uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
 
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index f95d703..7b04bcc 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -20,6 +20,7 @@
 from struct import pack
 from hashlib import sha1
 import sys
+import re
 
 def filesize(f):
     if f is None:
@@ -47,7 +48,7 @@
 def write_header(args):
     BOOT_MAGIC = 'ANDROID!'.encode()
     args.output.write(pack('8s', BOOT_MAGIC))
-    args.output.write(pack('8I',
+    args.output.write(pack('10I',
         filesize(args.kernel),                          # size in bytes
         args.base + args.kernel_offset,                 # physical load addr
         filesize(args.ramdisk),                         # size in bytes
@@ -55,8 +56,9 @@
         filesize(args.second),                          # size in bytes
         args.base + args.second_offset,                 # physical load addr
         args.base + args.tags_offset,                   # physical addr for kernel tags
-        args.pagesize))                                 # flash page size we assume
-    args.output.write(pack('8x'))                       # future expansion: should be 0
+        args.pagesize,                                  # flash page size we assume
+        0,                                              # future expansion: MUST be 0
+        (args.os_version << 11) | args.os_patch_level)) # os version and patch level
     args.output.write(pack('16s', args.board.encode())) # asciiz product name
     args.output.write(pack('512s', args.cmdline[:512].encode()))
 
@@ -97,6 +99,32 @@
 def parse_int(x):
     return int(x, 0)
 
+def parse_os_version(x):
+    match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
+    if match:
+        a = parse_int(match.group(1))
+        b = c = 0
+        if match.lastindex >= 2:
+            b = parse_int(match.group(2))
+        if match.lastindex == 3:
+            c = parse_int(match.group(3))
+        # 7 bits allocated for each field
+        assert a < 128
+        assert b < 128
+        assert c < 128
+        return (a << 14) | (b << 7) | c
+    return 0
+
+def parse_os_patch_level(x):
+    match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
+    if match:
+        y = parse_int(match.group(1)) - 2000
+        m = parse_int(match.group(2))
+        # 7 bits allocated for the year, 4 bits for the month
+        assert y >= 0 and y < 128
+        assert m > 0 and m <= 12
+        return (y << 4) | m
+    return 0
 
 def parse_cmdline():
     parser = ArgumentParser()
@@ -111,6 +139,10 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
+    parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
+                        default=0)
+    parser.add_argument('--os_patch_level', help='operating system patch level',
+                        type=parse_os_patch_level, default=0)
     parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
     parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
                         maxlen=16)
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
new file mode 100644
index 0000000..8db2ba8
--- /dev/null
+++ b/rootdir/etc/public.libraries.android.txt
@@ -0,0 +1,19 @@
+libandroid.so
+libc.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libwebviewchromium_plat_support.so
+libz.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
new file mode 100644
index 0000000..673e115
--- /dev/null
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -0,0 +1,18 @@
+libandroid.so
+libc.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libz.so
diff --git a/rootdir/init-debug.rc b/rootdir/init-debug.rc
index 435d4cb..44d34d8 100644
--- a/rootdir/init-debug.rc
+++ b/rootdir/init-debug.rc
@@ -6,3 +6,6 @@
 
 on property:persist.mmc.cache_size=*
     write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}
+
+on early-init
+    mount debugfs debugfs /sys/kernel/debug
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
new file mode 100644
index 0000000..18c54d5
--- /dev/null
+++ b/trusty/nvram/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# nvram.trusty is the Trusty NVRAM HAL module.
+include $(CLEAR_VARS)
+LOCAL_MODULE := nvram.trusty
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+	module.c \
+	trusty_nvram_implementation.cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
+LOCAL_STATIC_LIBRARIES := libnvram-hal
+LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c
new file mode 100644
index 0000000..06819c0
--- /dev/null
+++ b/trusty/nvram/module.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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 <hardware/nvram.h>
+
+// This function is defined in trusty_nvram_implementation.cpp.
+int trusty_nvram_open(const hw_module_t* module,
+                      const char* device_id,
+                      hw_device_t** device_ptr);
+
+static struct hw_module_methods_t nvram_module_methods = {
+    .open = trusty_nvram_open,
+};
+
+struct nvram_module HAL_MODULE_INFO_SYM
+    __attribute__((visibility("default"))) = {
+        .common = {.tag = HARDWARE_MODULE_TAG,
+                   .module_api_version = NVRAM_MODULE_API_VERSION_0_1,
+                   .hal_api_version = HARDWARE_HAL_API_VERSION,
+                   .id = NVRAM_HARDWARE_MODULE_ID,
+                   .name = "Trusty NVRAM HAL",
+                   .author = "The Android Open Source Project",
+                   .methods = &nvram_module_methods,
+                   .dso = 0,
+                   .reserved = {}},
+};
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
new file mode 100644
index 0000000..39496b4
--- /dev/null
+++ b/trusty/nvram/trusty_nvram_implementation.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <hardware/nvram.h>
+#include <trusty/tipc.h>
+
+#define LOG_TAG "TrustyNVRAM"
+#include <log/log.h>
+
+#include <nvram/hal/nvram_device_adapter.h>
+#include <nvram/messages/blob.h>
+#include <nvram/messages/nvram_messages.h>
+
+namespace {
+
+// Character device to open for Trusty IPC connections.
+const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+
+// App identifier of the NVRAM app.
+const char kTrustyNvramAppId[] = "com.android.trusty.nvram";
+
+// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It
+// serializes the request objects, sends it to the Trusty app and finally reads
+// back the result and decodes it.
+class TrustyNvramImplementation : public nvram::NvramImplementation {
+ public:
+  ~TrustyNvramImplementation() override;
+
+  void Execute(const nvram::Request& request,
+               nvram::Response* response) override;
+
+ private:
+  // Connects the IPC channel to the Trusty app if it is not already open.
+  // Returns true if the channel is open, false on errors.
+  bool Connect();
+
+  // Dispatches a command to the trust app. Returns true if successful (note
+  // that the response may still indicate an error on the Trusty side), false if
+  // there are any I/O or encoding/decoding errors.
+  bool SendRequest(const nvram::Request& request,
+                   nvram::Response* response);
+
+  // The file descriptor for the IPC connection to the Trusty app.
+  int tipc_nvram_fd_ = -1;
+
+  // Response buffer. This puts a hard size limit on the responses from the
+  // Trusty app. 4096 matches the maximum IPC message size currently supported
+  // by Trusty.
+  uint8_t response_buffer_[4096];
+};
+
+TrustyNvramImplementation::~TrustyNvramImplementation() {
+  if (tipc_nvram_fd_ != -1) {
+    tipc_close(tipc_nvram_fd_);
+    tipc_nvram_fd_ = -1;
+  }
+}
+
+void TrustyNvramImplementation::Execute(const nvram::Request& request,
+                                        nvram::Response* response) {
+  if (!SendRequest(request, response)) {
+    response->result = NV_RESULT_INTERNAL_ERROR;
+  }
+}
+
+bool TrustyNvramImplementation::Connect() {
+  if (tipc_nvram_fd_ != -1) {
+    return true;
+  }
+
+  int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId);
+  if (rc < 0) {
+    ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc));
+    return false;
+  }
+
+  tipc_nvram_fd_ = rc;
+  return true;
+}
+
+bool TrustyNvramImplementation::SendRequest(const nvram::Request& request,
+                                            nvram::Response* response) {
+  if (!Connect()) {
+    return false;
+  }
+
+  nvram::Blob request_buffer;
+  if (!nvram::Encode(request, &request_buffer)) {
+    ALOGE("Failed to encode NVRAM request.\n");
+    return false;
+  }
+
+  ssize_t rc =
+      write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size());
+  if (rc < 0) {
+    ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc));
+    return false;
+  }
+  if (static_cast<size_t>(rc) != request_buffer.size()) {
+    ALOGE("Failed to send full request buffer: %zd\n", rc);
+    return false;
+  }
+
+  rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_));
+  if (rc < 0) {
+    ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc));
+    return false;
+  }
+
+  if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) {
+    ALOGE("NVRAM response exceeds response buffer size.\n");
+    return false;
+  }
+
+  if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) {
+    ALOGE("Failed to decode NVRAM response.\n");
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+extern "C" int trusty_nvram_open(const hw_module_t* module,
+                                 const char* device_id,
+                                 hw_device_t** device_ptr) {
+  if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) {
+    return -EINVAL;
+  }
+
+  nvram::NvramDeviceAdapter* adapter =
+      new nvram::NvramDeviceAdapter(module, new TrustyNvramImplementation);
+  *device_ptr = adapter->as_device();
+  return 0;
+}