Merge "adb: use TCP keepalive."
diff --git a/adb/Android.mk b/adb/Android.mk
index 38e7058..2dba41d 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -101,7 +101,6 @@
 LOCAL_SRC_FILES := \
     $(LIBADB_SRC_FILES) \
     adb_auth_client.cpp \
-    fdevent.cpp \
     jdwp_service.cpp \
     usb_linux_client.cpp \
 
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index c4ffc85..128c3df 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -44,6 +44,7 @@
 };
 
 static fdevent listener_fde;
+static fdevent framework_fde;
 static int framework_fd = -1;
 
 static void usb_disconnected(void* unused, atransport* t);
@@ -161,29 +162,30 @@
     return ret;
 }
 
-static void usb_disconnected(void* unused, atransport* t)
-{
+static void usb_disconnected(void* unused, atransport* t) {
     D("USB disconnect");
     usb_transport = NULL;
     needs_retry = false;
 }
 
-static void adb_auth_event(int fd, unsigned events, void *data)
-{
+static void framework_disconnected() {
+    D("Framework disconnect");
+    fdevent_remove(&framework_fde);
+    framework_fd = -1;
+}
+
+static void adb_auth_event(int fd, unsigned events, void*) {
     char response[2];
     int ret;
 
     if (events & FDE_READ) {
         ret = unix_read(fd, response, sizeof(response));
         if (ret <= 0) {
-            D("Framework disconnect");
-            if (usb_transport)
-                fdevent_remove(&usb_transport->auth_fde);
-            framework_fd = -1;
-        }
-        else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
-            if (usb_transport)
+            framework_disconnected();
+        } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+            if (usb_transport) {
                 adb_auth_verified(usb_transport);
+            }
         }
     }
 }
@@ -221,13 +223,9 @@
         D("Failed to write PK, errno=%d", errno);
         return;
     }
-
-    fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
-    fdevent_add(&t->auth_fde, FDE_READ);
 }
 
-static void adb_auth_listener(int fd, unsigned events, void *data)
-{
+static void adb_auth_listener(int fd, unsigned events, void* data) {
     sockaddr_storage addr;
     socklen_t alen;
     int s;
@@ -240,7 +238,14 @@
         return;
     }
 
+    if (framework_fd >= 0) {
+        LOG(WARNING) << "adb received framework auth socket connection again";
+        framework_disconnected();
+    }
+
     framework_fd = s;
+    fdevent_install(&framework_fde, framework_fd, adb_auth_event, nullptr);
+    fdevent_add(&framework_fde, FDE_READ);
 
     if (needs_retry) {
         needs_retry = false;
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 8e76168..37d1146 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1106,8 +1106,9 @@
         }
 
         fprintf(stderr,"- waiting for device -\n");
-        adb_sleep_ms(1000);
-        wait_for_device("wait-for-device", transport_type, serial);
+        if (!wait_for_device("wait-for-device", transport_type, serial)) {
+            return 1;
+        }
     }
 
     int exit_code = read_and_dump(fd, use_shell_protocol);
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 4721e2f..7f40b96 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -43,24 +43,15 @@
 
 static const char* root_seclabel = nullptr;
 
-static void drop_capabilities_bounding_set_if_needed() {
-#ifdef ALLOW_ADBD_ROOT
+static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
+#if defined(ALLOW_ADBD_ROOT)
     char value[PROPERTY_VALUE_MAX];
     property_get("ro.debuggable", value, "");
     if (strcmp(value, "1") == 0) {
         return;
     }
 #endif
-    for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
-        if (i == CAP_SETUID || i == CAP_SETGID) {
-            // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
-            continue;
-        }
-
-        if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
-            PLOG(FATAL) << "Could not drop capabilities";
-        }
-    }
+    minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
 }
 
 static bool should_drop_privileges() {
@@ -131,7 +122,7 @@
     // Don't listen on a port (default 5037) if running in secure mode.
     // Don't run as root if running in secure mode.
     if (should_drop_privileges()) {
-        drop_capabilities_bounding_set_if_needed();
+        drop_capabilities_bounding_set_if_needed(jail.get());
 
         minijail_change_gid(jail.get(), AID_SHELL);
         minijail_change_uid(jail.get(), AID_SHELL);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 85aaa61..3a81ce6 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -517,12 +517,6 @@
     if (!sc.SendRequest(ID_RECV, rpath)) return false;
 
     adb_unlink(lpath);
-    const std::string dirpath = adb_dirname(lpath);
-    if (!mkdirs(dirpath.c_str())) {
-        sc.Error("failed to create parent directory '%s': %s", dirpath.c_str(), strerror(errno));
-        return false;
-    }
-
     int lfd = adb_creat(lpath, 0644);
     if (lfd < 0) {
         sc.Error("cannot create '%s': %s", lpath, strerror(errno));
@@ -803,13 +797,14 @@
     return S_ISDIR(mode);
 }
 
-static bool remote_build_list(SyncConnection& sc,
-                              std::vector<copyinfo>* file_list,
-                              const std::string& rpath,
-                              const std::string& lpath) {
+static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+                              const std::string& rpath, const std::string& lpath) {
     std::vector<copyinfo> dirlist;
     std::vector<copyinfo> linklist;
-    bool empty_dir = true;
+
+    // Add an entry for the current directory to ensure it gets created before pulling its contents.
+    copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
+    file_list->push_back(ci);
 
     // Put the files/dirs in rpath on the lists.
     auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
@@ -817,9 +812,6 @@
             return;
         }
 
-        // We found a child that isn't '.' or '..'.
-        empty_dir = false;
-
         copyinfo ci(lpath, rpath, name, mode);
         if (S_ISDIR(mode)) {
             dirlist.push_back(ci);
@@ -836,13 +828,6 @@
         return false;
     }
 
-    // Add the current directory to the list if it was empty, to ensure that it gets created.
-    if (empty_dir) {
-        copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
-        file_list->push_back(ci);
-        return true;
-    }
-
     // Check each symlink we found to see whether it's a file or directory.
     for (copyinfo& link_ci : linklist) {
         if (remote_symlink_isdir(sc, link_ci.rpath)) {
diff --git a/adb/transport.h b/adb/transport.h
index 76d6afa..4c0c008 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -50,7 +50,6 @@
     // it's better to do this piece by piece.
 
     atransport() {
-        auth_fde = {};
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
@@ -87,7 +86,6 @@
 
     void* key = nullptr;
     unsigned char token[TOKEN_SIZE] = {};
-    fdevent auth_fde;
     size_t failed_auth_attempts = 0;
 
     const std::string connection_state_name() const;
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 97fc069..1bdea2a 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -53,7 +53,6 @@
 
         EXPECT_EQ(key, rhs.key);
         EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
-        EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
         EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
 
         EXPECT_EQ(features(), rhs.features());
diff --git a/bootstat/README.md b/bootstat/README.md
index b3964ce..76ea6c1 100644
--- a/bootstat/README.md
+++ b/bootstat/README.md
@@ -12,6 +12,7 @@
       -p, --print           Dump the boot event records to the console
       -r, --record          Record the timestamp of a named boot event
       --record_boot_reason  Record the reason why the device booted
+      --record_time_since_factory_reset Record the time since the device was reset
 
 ## Relative time ##
 
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 0c49f82..c199190 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -80,7 +80,8 @@
           "  -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"
-          "  --record_boot_reason  Record the reason why the device booted\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");
 }
 
 // Constructs a readable, printable string from the givencommand line
@@ -192,7 +193,7 @@
 
   int option_index = 0;
   static const char boot_reason_str[] = "record_boot_reason";
-  static const char factory_reset_str[] = "record_factory_reset";
+  static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
     { "help",            no_argument,       NULL,   'h' },
     { "log",             no_argument,       NULL,   'l' },
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 218b9f8..13ef27e 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -13,5 +13,8 @@
     # Record the boot reason.
     exec - root root -- /system/bin/bootstat --record_boot_reason
 
+    # Record time since factory reset.
+    exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+
     # Log all boot events.
     exec - root root -- /system/bin/bootstat -l
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index b3fdcb4..31d9f0f 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -36,6 +36,7 @@
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <brillo/key_value_store.h>
+#include <brillo/osrelease_reader.h>
 #include <brillo/process.h>
 
 namespace {
@@ -52,6 +53,11 @@
 const char kUploadVarPrefix[] = "upload_var_";
 const char kUploadFilePrefix[] = "upload_file_";
 
+// Product information keys in the /etc/os-release.d folder.
+static const char kBdkVersionKey[] = "bdk_version";
+static const char kProductIDKey[] = "product_id";
+static const char kProductVersionKey[] = "product_version";
+
 // Normally this path is not used.  Unfortunately, there are a few edge cases
 // where we need this.  Any process that runs as kDefaultUserName that crashes
 // is consider a "user crash".  That includes the initial Chrome browser that
@@ -384,14 +390,49 @@
                                         const std::string &payload_path) {
   int64_t payload_size = -1;
   base::GetFileSize(FilePath(payload_path), &payload_size);
+
+  brillo::OsReleaseReader reader;
+  if (!forced_osreleased_directory_.empty()) {
+    reader.LoadTestingOnly(forced_osreleased_directory_);
+  } else {
+    reader.Load();
+  }
+  std::string bdk_version = "undefined";
+  std::string product_id = "undefined";
+  std::string product_version = "undefined";
+
+  if (!reader.GetString(kBdkVersionKey, &bdk_version)) {
+    LOG(ERROR) << "Could not read " << kBdkVersionKey
+               << " from /etc/os-release.d/";
+  }
+
+  if (!reader.GetString(kProductIDKey, &product_id)) {
+    LOG(ERROR) << "Could not read " << kProductIDKey
+               << " from /etc/os-release.d/";
+  }
+
+  if (!reader.GetString(kProductVersionKey, &product_version)) {
+    LOG(ERROR) << "Could not read " << kProductVersionKey
+               << " from /etc/os-release.d/";
+  }
+
   std::string meta_data = StringPrintf("%sexec_name=%s\n"
                                        "payload=%s\n"
                                        "payload_size=%" PRId64 "\n"
+                                       "%s=%s\n"
+                                       "%s=%s\n"
+                                       "%s=%s\n"
                                        "done=1\n",
                                        extra_metadata_.c_str(),
                                        exec_name.c_str(),
                                        payload_path.c_str(),
-                                       payload_size);
+                                       payload_size,
+                                       kBdkVersionKey,
+                                       bdk_version.c_str(),
+                                       kProductIDKey,
+                                       product_id.c_str(),
+                                       kProductVersionKey,
+                                       product_version.c_str());
   // We must use WriteNewFile instead of base::WriteFile as we
   // do not want to write with root access to a symlink that an attacker
   // might have created.
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
index 24cbfb3..21b9198 100644
--- a/crash_reporter/crash_collector.h
+++ b/crash_reporter/crash_collector.h
@@ -84,6 +84,12 @@
     forced_crash_directory_ = forced_directory;
   }
 
+  // For testing, set the root directory to read etc/os-release.d properties
+  // from.
+  void ForceOsReleaseDDirectory(const base::FilePath &forced_directory) {
+    forced_osreleased_directory_ = forced_directory;
+  }
+
   base::FilePath GetCrashDirectoryInfo(mode_t *mode,
                                        uid_t *directory_owner,
                                        gid_t *directory_group);
@@ -158,6 +164,7 @@
   IsFeedbackAllowedFunction is_feedback_allowed_function_;
   std::string extra_metadata_;
   base::FilePath forced_crash_directory_;
+  base::FilePath forced_osreleased_directory_;
   base::FilePath log_config_path_;
 
  private:
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 11c8c0d..a386cd1 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -74,10 +74,11 @@
 TEST_F(CrashCollectorTest, WriteNewFile) {
   FilePath test_file = test_dir_.path().Append("test_new");
   const char kBuffer[] = "buffer";
-  EXPECT_EQ(strlen(kBuffer),
-            collector_.WriteNewFile(test_file,
-                                    kBuffer,
-                                    strlen(kBuffer)));
+  unsigned int numBytesWritten = collector_.WriteNewFile(
+      test_file,
+      kBuffer,
+      strlen(kBuffer));
+  EXPECT_EQ(strlen(kBuffer), numBytesWritten);
   EXPECT_LT(collector_.WriteNewFile(test_file,
                                     kBuffer,
                                     strlen(kBuffer)), 0);
@@ -94,7 +95,7 @@
 }
 
 TEST_F(CrashCollectorTest, FormatDumpBasename) {
-  struct tm tm = {0};
+  struct tm tm = {};
   tm.tm_sec = 15;
   tm.tm_min = 50;
   tm.tm_hour = 13;
@@ -182,9 +183,26 @@
   const char kMetaFileBasename[] = "generated.meta";
   FilePath meta_file = test_dir_.path().Append(kMetaFileBasename);
   FilePath payload_file = test_dir_.path().Append("payload-file");
+  FilePath osreleased_directory =
+      test_dir_.path().Append("etc").Append("os-release.d");
+  ASSERT_TRUE(base::CreateDirectory(osreleased_directory));
+  collector_.ForceOsReleaseDDirectory(test_dir_.path());
+
   std::string contents;
   const char kPayload[] = "foo";
   ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+  const char kBdkVersion[] = "1";
+  ASSERT_TRUE(base::WriteFile(osreleased_directory.Append("bdk_version"),
+                              kBdkVersion,
+                              strlen(kBdkVersion)));
+  const char kProductId[] = "baz";
+  ASSERT_TRUE(base::WriteFile(osreleased_directory.Append("product_id"),
+                              kProductId,
+                              strlen(kProductId)));
+  const char kProductVersion[] = "1.2.3.4";
+  ASSERT_TRUE(base::WriteFile(osreleased_directory.Append("product_version"),
+                              kProductVersion,
+                              strlen(kProductVersion)));
   collector_.AddCrashMetaData("foo", "bar");
   collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
   EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
@@ -193,6 +211,9 @@
           "exec_name=kernel\n"
           "payload=%s\n"
           "payload_size=3\n"
+          "bdk_version=1\n"
+          "product_id=baz\n"
+          "product_version=1.2.3.4\n"
           "done=1\n",
           test_dir_.path().Append("payload-file").value().c_str());
   EXPECT_EQ(kExpectedMeta, contents);
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index 015f624..60fd832 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -49,7 +49,9 @@
  protected:
   void WriteStringToFile(const FilePath &file_path,
                          const char *data) {
-    ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+    unsigned int numBytesWritten =
+        base::WriteFile(file_path, data, strlen(data));
+    ASSERT_EQ(strlen(data), numBytesWritten);
   }
 
   void SetUpSuccessfulCollect();
@@ -284,7 +286,7 @@
   size_t end_pos = filename.find_first_of("\n");
   ASSERT_NE(std::string::npos, end_pos);
   filename = filename.substr(0, end_pos);
-  ASSERT_EQ(0, filename.find(test_crash_directory().value()));
+  ASSERT_EQ(0U, filename.find(test_crash_directory().value()));
   ASSERT_TRUE(base::PathExists(FilePath(filename)));
   std::string contents;
   ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents));
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index 56d2704..36372ae 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -73,7 +73,9 @@
  protected:
   void WriteStringToFile(const FilePath &file_path,
                          const char *data) {
-    ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+    unsigned int numBytesWritten =
+        base::WriteFile(file_path, data, strlen(data));
+    ASSERT_EQ(strlen(data), numBytesWritten);
   }
 
   UncleanShutdownCollectorMock collector_;
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index 98d7448..48b64e9 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -37,7 +37,6 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <brillo/osrelease_reader.h>
 #include <brillo/process.h>
 #include <brillo/syslog_logging.h>
 #include <cutils/properties.h>
@@ -59,11 +58,6 @@
 const char *UserCollector::kUserId = "Uid:\t";
 const char *UserCollector::kGroupId = "Gid:\t";
 
-// Product information keys in the /etc/os-release.d folder.
-static const char kBdkVersionKey[] = "bdk_version";
-static const char kProductIDKey[] = "product_id";
-static const char kProductVersionKey[] = "product_version";
-
 
 using base::FilePath;
 using base::StringPrintf;
@@ -505,29 +499,6 @@
   if (GetLogContents(FilePath(log_config_path_), exec, log_path))
     AddCrashMetaData("log", log_path.value());
 
-  brillo::OsReleaseReader reader;
-  reader.Load();
-  std::string value = "undefined";
-  if (!reader.GetString(kBdkVersionKey, &value)) {
-    LOG(ERROR) << "Could not read " << kBdkVersionKey
-               << " from /etc/os-release.d/";
-  }
-  AddCrashMetaData(kBdkVersionKey, value);
-
-  value = "undefined";
-  if (!reader.GetString(kProductIDKey, &value)) {
-    LOG(ERROR) << "Could not read " << kProductIDKey
-               << " from /etc/os-release.d/";
-  }
-  AddCrashMetaData(kProductIDKey, value);
-
-  value = "undefined";
-  if (!reader.GetString(kProductVersionKey, &value)) {
-    LOG(ERROR) << "Could not read " << kProductVersionKey
-               << " from /etc/os-release.d/";
-  }
-  AddCrashMetaData(kProductVersionKey, value);
-
   ErrorType error_type =
       ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
   if (error_type != kErrorNone) {
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index d9c9a5b..16a5cd5 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -101,15 +101,15 @@
       &pid, &signal, &uid, &gid, &exec_name));
   EXPECT_EQ(123456, pid);
   EXPECT_EQ(11, signal);
-  EXPECT_EQ(1000, uid);
-  EXPECT_EQ(2000, gid);
+  EXPECT_EQ(1000U, uid);
+  EXPECT_EQ(2000U, gid);
   EXPECT_EQ("foobar", exec_name);
   EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
       &pid, &signal, &uid, &gid, &exec_name));
   EXPECT_EQ(4321, pid);
   EXPECT_EQ(6, signal);
-  EXPECT_EQ(-1, uid);
-  EXPECT_EQ(-1, gid);
+  EXPECT_EQ(-1U, uid);
+  EXPECT_EQ(-1U, gid);
   EXPECT_EQ("barfoo", exec_name);
 
   EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
@@ -186,10 +186,10 @@
         test_dir_.path().Append("test/this_link").value().c_str();
     this_link.assign(long_link.c_str(), len);
     ASSERT_EQ(len, this_link.size());
-    unlink(kLink);
     ASSERT_EQ(0, symlink(this_link.c_str(), kLink));
     ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result));
     ASSERT_EQ(this_link, result.value());
+    unlink(kLink);
   }
 }
 
@@ -333,8 +333,8 @@
   gid_t gid = 100;
   uid_t uid = 100;
   EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid));
-  EXPECT_EQ(0, uid);
-  EXPECT_EQ(0, gid);
+  EXPECT_EQ(0U, uid);
+  EXPECT_EQ(0U, gid);
 }
 
 TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) {
@@ -387,7 +387,9 @@
 
   // maps file is not empty
   const char data[] = "test data";
-  ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data)));
+  unsigned int numBytesWritten =
+      base::WriteFile(maps_file, data, sizeof(data));
+  ASSERT_EQ(sizeof(data), numBytesWritten);
   ASSERT_TRUE(base::PathExists(maps_file));
   EXPECT_TRUE(collector_.ValidateProcFiles(container_dir));
 }
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 294bda9..ac1c58c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -685,10 +685,9 @@
   act.sa_flags = SA_NOCLDWAIT;
   sigaction(SIGCHLD, &act, 0);
 
-  int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-  if (s < 0)
-    return 1;
-  fcntl(s, F_SETFD, FD_CLOEXEC);
+  int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
+                              SOCK_STREAM | SOCK_CLOEXEC);
+  if (s == -1) return 1;
 
   ALOGI("debuggerd: starting\n");
 
@@ -698,14 +697,12 @@
     socklen_t alen = sizeof(ss);
 
     ALOGV("waiting for connection\n");
-    int fd = accept(s, addrp, &alen);
-    if (fd < 0) {
-      ALOGV("accept failed: %s\n", strerror(errno));
+    int fd = accept4(s, addrp, &alen, SOCK_CLOEXEC);
+    if (fd == -1) {
+      ALOGE("accept failed: %s\n", strerror(errno));
       continue;
     }
 
-    fcntl(fd, F_SETFD, FD_CLOEXEC);
-
     handle_request(fd);
   }
   return 0;
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 477148b..b33dd28 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -323,11 +323,10 @@
                                 " cc=%d", props.batteryCycleCount);
             }
         } else {
-            snprintf(dmesgline, sizeof(dmesgline),
+            len = snprintf(dmesgline, sizeof(dmesgline),
                  "battery none");
         }
 
-        len = strlen(dmesgline);
         snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
                  props.chargerAcOnline ? "a" : "",
                  props.chargerUsbOnline ? "u" : "",
diff --git a/include/log/log.h b/include/log/log.h
index 1bd9165..e606a84 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -484,15 +484,19 @@
  */
 
 /*
- * Event log entry types.  These must match up with the declarations in
- * java/android/android/util/EventLog.java.
+ * Event log entry types.
  */
 typedef enum {
-    EVENT_TYPE_INT      = 0,
-    EVENT_TYPE_LONG     = 1,
-    EVENT_TYPE_STRING   = 2,
-    EVENT_TYPE_LIST     = 3,
-    EVENT_TYPE_FLOAT    = 4,
+    /* Special markers for android_log_list_element type */
+    EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */
+    EVENT_TYPE_UNKNOWN   = '?',  /* protocol error       */
+
+    /* must match with declaration in java/android/android/util/EventLog.java */
+    EVENT_TYPE_INT       = 0,    /* uint32_t */
+    EVENT_TYPE_LONG      = 1,    /* uint64_t */
+    EVENT_TYPE_STRING    = 2,
+    EVENT_TYPE_LIST      = 3,
+    EVENT_TYPE_FLOAT     = 4,
 } AndroidEventLogType;
 #define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
 #define typeof_AndroidEventLogType unsigned char
@@ -522,7 +526,87 @@
 #define LOG_EVENT_STRING(_tag, _value)                                      \
         (void) __android_log_bswrite(_tag, _value);
 #endif
-/* TODO: something for LIST */
+
+typedef enum log_id {
+    LOG_ID_MIN = 0,
+
+#ifndef LINT_RLOG
+    LOG_ID_MAIN = 0,
+#endif
+    LOG_ID_RADIO = 1,
+#ifndef LINT_RLOG
+    LOG_ID_EVENTS = 2,
+    LOG_ID_SYSTEM = 3,
+    LOG_ID_CRASH = 4,
+    LOG_ID_SECURITY = 5,
+    LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+#endif
+
+    LOG_ID_MAX
+} log_id_t;
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
+/* For manipulating lists of events. */
+
+#define ANDROID_MAX_LIST_NEST_DEPTH 8
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+typedef struct android_log_context_internal *android_log_context;
+
+/*
+ * Elements returned when reading a list of events.
+ */
+typedef struct {
+    AndroidEventLogType type;
+    uint16_t complete;
+    uint16_t len;
+    union {
+        int32_t int32;
+        int64_t int64;
+        char *string;
+        float float32;
+    } data;
+} android_log_list_element;
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char *value);
+int android_log_write_string8_len(android_log_context ctx,
+                                  const char *value, size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/*
+ * Creates a context from a raw buffer representing a list of events to be read.
+ */
+android_log_context create_android_log_parser(const char *msg, size_t len);
+
+android_log_list_element android_log_read_next(android_log_context ctx);
+android_log_list_element android_log_peek_next(android_log_context ctx);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context *ctx);
 
 /*
  * ===========================================================================
@@ -585,26 +669,6 @@
     (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
 #endif
 
-typedef enum log_id {
-    LOG_ID_MIN = 0,
-
-#ifndef LINT_RLOG
-    LOG_ID_MAIN = 0,
-#endif
-    LOG_ID_RADIO = 1,
-#ifndef LINT_RLOG
-    LOG_ID_EVENTS = 2,
-    LOG_ID_SYSTEM = 3,
-    LOG_ID_CRASH = 4,
-    LOG_ID_SECURITY = 5,
-    LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
-#endif
-
-    LOG_ID_MAX
-} log_id_t;
-#define sizeof_log_id_t sizeof(typeof_log_id_t)
-#define typeof_log_id_t unsigned char
-
 /*
  * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
  * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 85d6c19..53966d5 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -88,6 +88,10 @@
 #define AID_WEBSERV       1044  /* webservd process */
 #define AID_DEBUGGERD     1045  /* debuggerd unprivileged user */
 #define AID_MEDIA_CODEC   1046  /* mediacodec process */
+#define AID_CAMERASERVER  1047  /* cameraserver process */
+#define AID_FIREWALL      1048  /* firewalld process */
+#define AID_TRUNKS        1049  /* trunksd process (TPM daemon) */
+/* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -194,6 +198,9 @@
     { "webserv",       AID_WEBSERV },
     { "debuggerd",     AID_DEBUGGERD, },
     { "mediacodec",    AID_MEDIA_CODEC, },
+    { "cameraserver",  AID_CAMERASERVER, },
+    { "firewall",      AID_FIREWALL, },
+    { "trunks",        AID_TRUNKS, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
diff --git a/init/perfboot.py b/init/perfboot.py
index 91e6c2b..713290b 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -191,9 +191,9 @@
 
 def init_perf(device, output, record_list, tags):
     device.wait()
-    build_type = device.get_prop('ro.build.type')
+    debuggable = device.get_prop('ro.debuggable')
     original_dropbox_max_files = None
-    if build_type != 'user':
+    if debuggable == '1':
         # Workaround for Dropbox issue (http://b/20890386).
         original_dropbox_max_files = disable_dropbox(device)
 
diff --git a/init/util.cpp b/init/util.cpp
index aefdf8f..84b4155 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -102,7 +102,7 @@
                   gid_t gid, const char *socketcon)
 {
     struct sockaddr_un addr;
-    int fd, ret;
+    int fd, ret, savederrno;
     char *filecon;
 
     if (socketcon) {
@@ -140,16 +140,26 @@
     }
 
     ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
-    if (ret) {
-        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
-        goto out_unlink;
-    }
+    savederrno = errno;
 
     setfscreatecon(NULL);
     freecon(filecon);
 
-    chown(addr.sun_path, uid, gid);
-    chmod(addr.sun_path, perm);
+    if (ret) {
+        ERROR("Failed to bind socket '%s': %s\n", name, strerror(savederrno));
+        goto out_unlink;
+    }
+
+    ret = lchown(addr.sun_path, uid, gid);
+    if (ret) {
+        ERROR("Failed to lchown socket '%s': %s\n", addr.sun_path, strerror(errno));
+        goto out_unlink;
+    }
+    ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
+    if (ret) {
+        ERROR("Failed to fchmodat socket '%s': %s\n", addr.sun_path, strerror(errno));
+        goto out_unlink;
+    }
 
     INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
          addr.sun_path, perm, uid, gid);
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
index 00e211c..14a58ca 100644
--- a/libcutils/qtaguid.c
+++ b/libcutils/qtaguid.c
@@ -47,10 +47,7 @@
 
 /* Only call once per process. */
 void qtaguid_resTrack(void) {
-    resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY));
-    if (resTrackFd >=0) {
-        TEMP_FAILURE_RETRY(fcntl(resTrackFd, F_SETFD, FD_CLOEXEC));
-    }
+    resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
 }
 
 /*
@@ -63,7 +60,7 @@
 
     ALOGV("write_ctrl(%s)", cmd);
 
-    fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY));
+    fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
     if (fd < 0) {
         return -errno;
     }
@@ -85,7 +82,7 @@
     int param_fd;
     int res;
 
-    param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY));
+    param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY | O_CLOEXEC));
     if (param_fd < 0) {
         return -errno;
     }
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
index 4da5ed6..52cf5f4 100644
--- a/libcutils/tests/Android.mk
+++ b/libcutils/tests/Android.mk
@@ -23,8 +23,9 @@
 test_target_only_src_files := \
     MemsetTest.cpp \
     PropertiesTest.cpp \
+    trace-dev_test.cpp \
 
-test_libraries := libcutils liblog
+test_libraries := libcutils liblog libbase
 
 
 #
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
new file mode 100644
index 0000000..edf981b
--- /dev/null
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -0,0 +1,295 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "../trace-dev.c"
+
+class TraceDevTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    lseek(tmp_file_.fd, 0, SEEK_SET);
+    atrace_marker_fd = tmp_file_.fd;
+  }
+
+  void TearDown() override {
+    atrace_marker_fd = -1;
+  }
+
+  TemporaryFile tmp_file_;
+
+  static std::string MakeName(size_t length) {
+    std::string name;
+    for (size_t i = 0; i < length; i++) {
+      name += '0' + (i % 10);
+    }
+    return name;
+  }
+};
+
+TEST_F(TraceDevTest, atrace_begin_body_normal) {
+  atrace_begin_body("fake_name");
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("B|%d|fake_name", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_exact) {
+  std::string expected = android::base::StringPrintf("B|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
+  atrace_begin_body(name.c_str());
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name;
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_begin_body(name.c_str());
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_truncated) {
+  std::string expected = android::base::StringPrintf("B|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_begin_body(name.c_str());
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+  expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_normal) {
+  atrace_async_begin_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("S|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_exact) {
+  std::string expected = android::base::StringPrintf("S|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_async_begin_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_async_begin_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_truncated) {
+  std::string expected = android::base::StringPrintf("S|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_async_begin_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_normal) {
+  atrace_async_end_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("F|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_exact) {
+  std::string expected = android::base::StringPrintf("F|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_async_end_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_async_end_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_truncated) {
+  std::string expected = android::base::StringPrintf("F|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_async_end_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_normal) {
+  atrace_int_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("C|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_exact) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_int_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_int_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_truncated) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_int_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_normal) {
+  atrace_int64_body("fake_name", 17179869183L);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("C|%d|fake_name|17179869183", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_exact) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);
+  atrace_int64_body(name.c_str(), 17179869183L);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|17179869183";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_int64_body(name.c_str(), 17179869183L);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_truncated) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_int64_body(name.c_str(), 17179869183L);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;
+  expected += android::base::StringPrintf("%.*s|17179869183", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index f025256..5df1c5a 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -194,49 +194,47 @@
 void atrace_begin_body(const char* name)
 {
     char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
 
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
+    int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
+    if (len >= (int) sizeof(buf)) {
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
+        len = sizeof(buf) - 1;
+    }
     write(atrace_marker_fd, buf, len);
 }
 
+#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
+    char buf[ATRACE_MESSAGE_LENGTH]; \
+    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+        name, value); \
+    if (len >= (int) sizeof(buf)) { \
+        /* Given the sizeof(buf), and all of the current format buffers, \
+         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+        /* Truncate the name to make the message fit. */ \
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+            name_len, name, value); \
+    } \
+    write(atrace_marker_fd, buf, len); \
+}
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32,
-            getpid(), name, cookie);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32,
-            getpid(), name, cookie);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32,
-            getpid(), name, value);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64,
-            getpid(), name, value);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
 }
diff --git a/liblog/Android.mk b/liblog/Android.mk
index a183db8..dd5b518 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,10 +24,10 @@
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
-liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
-liblog_target_sources += logprint.c
-liblog_target_sources += log_read.c
+liblog_sources := logd_write.c log_event_list.c log_event_write.c
+liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
+liblog_target_sources := $(liblog_sources) event_tag_map.c
+liblog_target_sources += log_time.cpp log_is_loggable.c logprint.c log_read.c
 
 # Shared and static library for host
 # ========================================================
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 4942c08..aa88bed 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -469,13 +469,13 @@
     if (numLines > MAX_LINES)
         numLines = MAX_LINES;
 
-    numVecs = numLines*3;  // 3 iovecs per line.
+    numVecs = numLines * 3;  // 3 iovecs per line.
     if (numVecs > INLINE_VECS) {
         vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
         if (vec == NULL) {
             msg = "LOG: write failed, no memory";
-            numVecs = 3;
-            numLines = 1;
+            numVecs = INLINE_VECS;
+            numLines = numVecs / 3;
             vec = stackVec;
         }
     }
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
new file mode 100644
index 0000000..2213f21
--- /dev/null
+++ b/liblog/log_event_list.c
@@ -0,0 +1,527 @@
+/*
+ * 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 <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+    uint32_t tag;
+    unsigned pos; /* Read/write position into buffer */
+    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
+    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
+    unsigned list_nest_depth;
+    unsigned len; /* Length or raw buffer. */
+    bool overflow;
+    bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+    enum {
+        kAndroidLoggerRead = 1,
+        kAndroidLoggerWrite = 2,
+    } read_write_flag;
+    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+android_log_context create_android_logger(uint32_t tag) {
+    size_t needed, i;
+    android_log_context_internal *context;
+
+    context = calloc(1, sizeof(android_log_context_internal));
+    if (!context) {
+        return NULL;
+    }
+    context->tag = tag;
+    context->read_write_flag = kAndroidLoggerWrite;
+    needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+    }
+    /* Everything is a list */
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->list[0] = context->pos + 1;
+    context->pos += needed;
+
+    return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char *msg, size_t len) {
+    android_log_context_internal *context;
+    size_t i;
+
+    context = calloc(1, sizeof(android_log_context_internal));
+    if (!context) {
+        return NULL;
+    }
+    len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+    context->len = len;
+    memcpy(context->storage, msg, len);
+    context->read_write_flag = kAndroidLoggerRead;
+
+    return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context *ctx) {
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)*ctx;
+    if (!context) {
+        return -EBADF;
+    }
+    memset(context, 0, sizeof(*context));
+    free(context);
+    *ctx = NULL;
+    return 0;
+}
+
+int android_log_write_list_begin(android_log_context ctx) {
+    size_t needed;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context ||
+            (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    context->count[context->list_nest_depth]++;
+    context->list_nest_depth++;
+    if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->storage[context->pos + 1] = 0;
+    context->list[context->list_nest_depth] = context->pos + 1;
+    context->count[context->list_nest_depth] = 0;
+    context->pos += needed;
+    return 0;
+}
+
+static inline void copy4LE(uint8_t *buf, uint32_t val)
+{
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+int android_log_write_int32(android_log_context ctx, int32_t value) {
+    size_t needed;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    needed = sizeof(uint8_t) + sizeof(value);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_INT;
+    copy4LE(&context->storage[context->pos + 1], value);
+    context->pos += needed;
+    return 0;
+}
+
+static inline void copy8LE(uint8_t *buf, uint64_t val)
+{
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+    buf[4] = (val >> 32) & 0xFF;
+    buf[5] = (val >> 40) & 0xFF;
+    buf[6] = (val >> 48) & 0xFF;
+    buf[7] = (val >> 56) & 0xFF;
+}
+
+int android_log_write_int64(android_log_context ctx, int64_t value) {
+    size_t needed;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    needed = sizeof(uint8_t) + sizeof(value);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_LONG;
+    copy8LE(&context->storage[context->pos + 1], value);
+    context->pos += needed;
+    return 0;
+}
+
+int android_log_write_string8_len(android_log_context ctx,
+                                  const char *value, size_t maxlen) {
+    size_t needed;
+    ssize_t len;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+    }
+    len = strnlen(value, maxlen);
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char *value) {
+    return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context ctx, float value) {
+    size_t needed;
+    uint32_t ivalue;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    needed = sizeof(uint8_t) + sizeof(ivalue);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    ivalue = *(uint32_t *)&value;
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
+    copy4LE(&context->storage[context->pos + 1], ivalue);
+    context->pos += needed;
+    return 0;
+}
+
+int android_log_write_list_end(android_log_context ctx) {
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->overflow = true;
+        context->list_nest_depth--;
+        return -EOVERFLOW;
+    }
+    if (!context->list_nest_depth) {
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    if (context->list[context->list_nest_depth] <= 0) {
+        context->list_nest_depth--;
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    context->storage[context->list[context->list_nest_depth]] =
+        context->count[context->list_nest_depth];
+    context->list_nest_depth--;
+    return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context ctx, log_id_t id) {
+    android_log_context_internal *context;
+    const char *msg;
+    ssize_t len;
+
+    if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+        return -EINVAL;
+    }
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->list_nest_depth) {
+        return -EIO;
+    }
+    /* NB: if there was overflow, then log is truncated. Nothing reported */
+    context->storage[1] = context->count[0];
+    len = context->len = context->pos;
+    msg = (const char *)context->storage;
+    /* it'snot a list */
+    if (context->count[0] <= 1) {
+        len -= sizeof(uint8_t) + sizeof(uint8_t);
+        if (len < 0) {
+            len = 0;
+        }
+        msg += sizeof(uint8_t) + sizeof(uint8_t);
+    }
+    return (id == LOG_ID_EVENTS) ?
+        __android_log_bwrite(context->tag, msg, len) :
+        __android_log_security_bwrite(context->tag, msg, len);
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+    uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+    uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+    return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(
+        android_log_context ctx, int peek) {
+    android_log_list_element elem;
+    unsigned pos;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+
+    memset(&elem, 0, sizeof(elem));
+
+    /* Nothing to parse from this context, so return complete. */
+    if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+            (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+            (context->count[context->list_nest_depth] >=
+                (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        if (context &&
+                (context->list_stop ||
+                ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+                    !context->count[context->list_nest_depth]))) {
+            elem.type = EVENT_TYPE_LIST_STOP;
+        }
+        elem.complete = true;
+        return elem;
+    }
+
+    /*
+     * Use a different variable to update the position in case this
+     * operation is a "peek".
+     */
+    pos = context->pos;
+    if (context->list_stop) {
+        elem.type = EVENT_TYPE_LIST_STOP;
+        elem.complete = !context->count[0] && (!context->list_nest_depth ||
+            ((context->list_nest_depth == 1) && !context->count[1]));
+        if (!peek) {
+            /* Suck in superfluous stop */
+            if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+                context->pos = pos + 1;
+            }
+            if (context->list_nest_depth) {
+                --context->list_nest_depth;
+                if (context->count[context->list_nest_depth]) {
+                    context->list_stop = false;
+                }
+            } else {
+                context->list_stop = false;
+            }
+        }
+        return elem;
+    }
+    if ((pos + 1) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = true;
+        return elem;
+    }
+
+    elem.type = context->storage[pos++];
+    switch ((int)elem.type) {
+    case EVENT_TYPE_FLOAT:
+        /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+        /* FALLTHRU */
+    case EVENT_TYPE_INT:
+        elem.len = sizeof(int32_t);
+        if ((pos + elem.len) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            return elem;
+        }
+        elem.data.int32 = get4LE(&context->storage[pos]);
+        /* common tangeable object suffix */
+        pos += elem.len;
+        elem.complete = !context->list_nest_depth && !context->count[0];
+        if (!peek) {
+            if (!context->count[context->list_nest_depth] ||
+                    !--(context->count[context->list_nest_depth])) {
+                context->list_stop = true;
+            }
+            context->pos = pos;
+        }
+        return elem;
+
+    case EVENT_TYPE_LONG:
+        elem.len = sizeof(int64_t);
+        if ((pos + elem.len) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            return elem;
+        }
+        elem.data.int64 = get8LE(&context->storage[pos]);
+        /* common tangeable object suffix */
+        pos += elem.len;
+        elem.complete = !context->list_nest_depth && !context->count[0];
+        if (!peek) {
+            if (!context->count[context->list_nest_depth] ||
+                    !--(context->count[context->list_nest_depth])) {
+                context->list_stop = true;
+            }
+            context->pos = pos;
+        }
+        return elem;
+
+    case EVENT_TYPE_STRING:
+        if ((pos + sizeof(int32_t)) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            elem.complete = true;
+            return elem;
+        }
+        elem.len = get4LE(&context->storage[pos]);
+        pos += sizeof(int32_t);
+        if ((pos + elem.len) > context->len) {
+            elem.len = context->len - pos; /* truncate string */
+            elem.complete = true;
+            if (!elem.len) {
+                elem.type = EVENT_TYPE_UNKNOWN;
+                return elem;
+            }
+        }
+        elem.data.string = (char *)&context->storage[pos];
+        /* common tangeable object suffix */
+        pos += elem.len;
+        elem.complete = !context->list_nest_depth && !context->count[0];
+        if (!peek) {
+            if (!context->count[context->list_nest_depth] ||
+                    !--(context->count[context->list_nest_depth])) {
+                context->list_stop = true;
+            }
+            context->pos = pos;
+        }
+        return elem;
+
+    case EVENT_TYPE_LIST:
+        if ((pos + sizeof(uint8_t)) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            elem.complete = true;
+            return elem;
+        }
+        elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+        if (peek) {
+            return elem;
+        }
+        if (context->count[context->list_nest_depth]) {
+            context->count[context->list_nest_depth]--;
+        }
+        context->list_stop = !context->storage[pos];
+        context->list_nest_depth++;
+        if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+            context->count[context->list_nest_depth] = context->storage[pos];
+        }
+        context->pos = pos + sizeof(uint8_t);
+        return elem;
+
+    case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+        if (!peek) {
+            context->pos = pos;
+        }
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = !context->list_nest_depth;
+        if (context->list_nest_depth > 0) {
+            elem.type = EVENT_TYPE_LIST_STOP;
+            if (!peek) {
+                context->list_nest_depth--;
+            }
+        }
+        return elem;
+
+    default:
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+    }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+    return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+    return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
index 0bc42d5..ad42edd 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.c
@@ -15,74 +15,33 @@
  */
 
 #include <errno.h>
-#include <string.h>
 
 #include <log/log.h>
-#include <log/logger.h>
 
-#define MAX_EVENT_PAYLOAD 512
 #define MAX_SUBTAG_LEN 32
 
-static inline void copy4LE(uint8_t *buf, size_t pos, int val)
+int __android_log_error_write(int tag, const char *subTag, int32_t uid,
+                              const char *data, uint32_t dataLen)
 {
-    buf[pos] = val & 0xFF;
-    buf[pos+1] = (val >> 8) & 0xFF;
-    buf[pos+2] = (val >> 16) & 0xFF;
-    buf[pos+3] = (val >> 24) & 0xFF;
-}
+    int ret = -EINVAL;
 
-int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
-                              uint32_t dataLen)
-{
-    uint8_t buf[MAX_EVENT_PAYLOAD];
-    size_t pos = 0;
-    uint32_t subTagLen = 0;
-    uint32_t roomLeftForData = 0;
+    if (subTag && (data || !dataLen)) {
+        android_log_context ctx = create_android_logger(tag);
 
-    if ((subTag == NULL) || ((data == NULL) && (dataLen != 0))) return -EINVAL;
-
-    subTagLen = strlen(subTag);
-
-    // Truncate subtags that are too long.
-    subTagLen = subTagLen > MAX_SUBTAG_LEN ? MAX_SUBTAG_LEN : subTagLen;
-
-    // Truncate dataLen if it is too long.
-    roomLeftForData = MAX_EVENT_PAYLOAD -
-            (1 + // EVENT_TYPE_LIST
-             1 + // Number of elements in list
-             1 + // EVENT_TYPE_STRING
-             sizeof(subTagLen) +
-             subTagLen +
-             1 + // EVENT_TYPE_INT
-             sizeof(uid) +
-             1 + // EVENT_TYPE_STRING
-             sizeof(dataLen));
-    dataLen = dataLen > roomLeftForData ? roomLeftForData : dataLen;
-
-    buf[pos++] = EVENT_TYPE_LIST;
-    buf[pos++] = 3; // Number of elements in the list (subTag, uid, data)
-
-    // Write sub tag.
-    buf[pos++] = EVENT_TYPE_STRING;
-    copy4LE(buf, pos, subTagLen);
-    pos += 4;
-    memcpy(&buf[pos], subTag, subTagLen);
-    pos += subTagLen;
-
-    // Write UID.
-    buf[pos++] = EVENT_TYPE_INT;
-    copy4LE(buf, pos, uid);
-    pos += 4;
-
-    // Write data.
-    buf[pos++] = EVENT_TYPE_STRING;
-    copy4LE(buf, pos, dataLen);
-    pos += 4;
-    if (dataLen != 0)
-    {
-        memcpy(&buf[pos], data, dataLen);
-        pos += dataLen;
+        ret = -ENOMEM;
+        if (ctx) {
+            ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+            if (ret >= 0) {
+                ret = android_log_write_int32(ctx, uid);
+                if (ret >= 0) {
+                    ret = android_log_write_string8_len(ctx, data, dataLen);
+                    if (ret >= 0) {
+                        ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+                    }
+                }
+            }
+            android_log_destroy(&ctx);
+        }
     }
-
-    return __android_log_bwrite(tag, buf, pos);
+    return ret;
 }
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 1aff272..fc63d2c 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -503,10 +503,7 @@
     }
 
     if (logger_list->pid) {
-        n = snprintf(cp, remaining, " pid=%u", logger_list->pid);
-        n = min(n, remaining);
-        remaining -= n;
-        cp += n;
+        snprintf(cp, remaining, " pid=%u", logger_list->pid);
     }
 
     return send_log_msg(NULL, NULL, buf, len);
@@ -657,7 +654,6 @@
         preread_count = 0;
     }
 
-    ret = 0;
     while(1) {
         if (preread_count < sizeof(buf)) {
             ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
@@ -834,7 +830,6 @@
         if (logger_list->pid) {
             ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
             ret = min(ret, remaining);
-            remaining -= ret;
             cp += ret;
         }
 
@@ -867,7 +862,6 @@
         logger_list->sock = sock;
     }
 
-    ret = 0;
     while(1) {
         memset(log_msg, 0, sizeof(*log_msg));
 
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 9dd6f03..3d58147 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -14,126 +14,9 @@
  * limitations under the License.
  */
 
-#include <fcntl.h>
-#include <sys/cdefs.h>
-
 #include <gtest/gtest.h>
 
-// Should be in bionic test suite, *but* we are using liblog to confirm
-// end-to-end logging, so let the overly cute oedipus complex begin ...
-#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone
-#define _ANDROID_LOG_H // Priorities redefined
-#define _LIBS_LOG_LOG_H // log ids redefined
-typedef unsigned char log_id_t; // log_id_t missing as a result
-#define _LIBS_LOG_LOG_READ_H // log_time redefined
-
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/log_read.h>
-
-TEST(libc, __libc_android_log_event_int) {
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    struct timespec ts;
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-    int value = ts.tv_nsec;
-
-    __libc_android_log_event_int(0, value);
-    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.len != (4 + 1 + 4))
-         || ((int)log_msg.id() != LOG_ID_EVENTS)) {
-            continue;
-        }
-
-        char *eventData = log_msg.msg();
-
-        int incoming = (eventData[0] & 0xFF) |
-                      ((eventData[1] & 0xFF) << 8) |
-                      ((eventData[2] & 0xFF) << 16) |
-                      ((eventData[3] & 0xFF) << 24);
-
-        if (incoming != 0) {
-            continue;
-        }
-
-        if (eventData[4] != EVENT_TYPE_INT) {
-            continue;
-        }
-
-        incoming = (eventData[4 + 1 + 0] & 0xFF) |
-                  ((eventData[4 + 1 + 1] & 0xFF) << 8) |
-                  ((eventData[4 + 1 + 2] & 0xFF) << 16) |
-                  ((eventData[4 + 1 + 3] & 0xFF) << 24);
-
-        if (incoming == value) {
-            ++count;
-        }
-    }
-
-    EXPECT_EQ(1, count);
-
-    android_logger_list_close(logger_list);
-}
-
-TEST(libc, __libc_fatal_no_abort) {
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        (log_id_t)LOG_ID_CRASH, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    char b[80];
-    struct timespec ts;
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-
-    __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
-    snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
-    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 ((int)log_msg.id() != LOG_ID_CRASH) {
-            continue;
-        }
-
-        char *data = log_msg.msg();
-
-        if ((*data == ANDROID_LOG_FATAL)
-                && !strcmp(data + 1, "libc")
-                && !strcmp(data + 1 + strlen(data + 1) + 1, b)) {
-            ++count;
-        }
-    }
-
-    EXPECT_EQ(1, count);
-
-    android_logger_list_close(logger_list);
-}
+#include <stdio.h>
 
 TEST(libc, __pstore_append) {
     FILE *fp;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index f8d4c4c..50ecd2d 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2014 The Android Open Source Project
+ * Copyright (C) 2013-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.
@@ -1429,7 +1429,7 @@
     const int TAG = 123456782;
     const char SUBTAG[] = "test-subtag";
     const int UID = -1;
-    const int DATA_LEN = SIZEOF_MAX_PAYLOAD_BUF;
+    const int DATA_LEN = sizeof(max_payload_buf);
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -1500,9 +1500,9 @@
         }
         eventData += dataLen;
 
-        // 4 bytes for the tag, and 512 bytes for the log since the
-        // max_payload_buf should be truncated.
-        ASSERT_EQ(4 + 512, eventData - original);
+        // 4 bytes for the tag, and max_payload_buf should be truncated.
+        ASSERT_LE(4 + 512, eventData - original);      // worst expectations
+        ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
 
         ++count;
     }
@@ -1741,3 +1741,561 @@
 
     android_logger_list_close(logger_list);
 }
+
+static int is_real_element(int type) {
+    return ((type == EVENT_TYPE_INT) ||
+            (type == EVENT_TYPE_LONG) ||
+            (type == EVENT_TYPE_STRING) ||
+            (type == EVENT_TYPE_FLOAT));
+}
+
+int android_log_buffer_to_string(const char *msg, size_t len,
+                                 char *strOut, size_t strOutLen) {
+    android_log_context context = create_android_log_parser(msg, len);
+    android_log_list_element elem;
+    bool overflow = false;
+    /* Reserve 1 byte for null terminator. */
+    size_t origStrOutLen = strOutLen--;
+
+    if (!context) {
+        return -EBADF;
+    }
+
+    memset(&elem, 0, sizeof(elem));
+
+    size_t outCount;
+
+    do {
+        elem = android_log_read_next(context);
+        switch ((int)elem.type) {
+        case EVENT_TYPE_LIST:
+            if (strOutLen == 0) {
+                overflow = true;
+            } else {
+                *strOut++ = '[';
+                strOutLen--;
+            }
+            break;
+
+        case EVENT_TYPE_LIST_STOP:
+            if (strOutLen == 0) {
+                overflow = true;
+            } else {
+                *strOut++ = ']';
+                strOutLen--;
+            }
+            break;
+
+        case EVENT_TYPE_INT:
+            /*
+             * snprintf also requires room for the null terminator, which
+             * we don't care about  but we have allocated enough room for
+             * that
+             */
+            outCount = snprintf(strOut, strOutLen + 1,
+                                "%" PRId32, elem.data.int32);
+            if (outCount <= strOutLen) {
+                strOut += outCount;
+                strOutLen -= outCount;
+            } else {
+                overflow = true;
+            }
+            break;
+
+        case EVENT_TYPE_LONG:
+            /*
+             * snprintf also requires room for the null terminator, which
+             * we don't care about but we have allocated enough room for
+             * that
+             */
+            outCount = snprintf(strOut, strOutLen + 1,
+                                "%" PRId64, elem.data.int64);
+            if (outCount <= strOutLen) {
+                strOut += outCount;
+                strOutLen -= outCount;
+            } else {
+                overflow = true;
+            }
+            break;
+
+        case EVENT_TYPE_FLOAT:
+            /*
+             * snprintf also requires room for the null terminator, which
+             * we don't care about but we have allocated enough room for
+             * that
+             */
+            outCount = snprintf(strOut, strOutLen + 1, "%f", elem.data.float32);
+            if (outCount <= strOutLen) {
+                strOut += outCount;
+                strOutLen -= outCount;
+            } else {
+                overflow = true;
+            }
+            break;
+
+        default:
+            elem.complete = true;
+            break;
+
+        case EVENT_TYPE_UNKNOWN:
+#if 0 // Ideal purity in the test, we want to complain about UNKNOWN showing up
+            if (elem.complete) {
+                break;
+            }
+#endif
+            elem.data.string = const_cast<char *>("<unknown>");
+            elem.len = strlen(elem.data.string);
+            /* FALLTHRU */
+        case EVENT_TYPE_STRING:
+            if (elem.len <= strOutLen) {
+                memcpy(strOut, elem.data.string, elem.len);
+                strOut += elem.len;
+                strOutLen -= elem.len;
+            } else if (strOutLen > 0) {
+                /* copy what we can */
+                memcpy(strOut, elem.data.string, strOutLen);
+                strOut += strOutLen;
+                strOutLen = 0;
+                overflow = true;
+            }
+            break;
+        }
+
+        if (elem.complete) {
+            break;
+        }
+        /* Determine whether to put a comma or not. */
+        if (!overflow && (is_real_element(elem.type) ||
+                (elem.type == EVENT_TYPE_LIST_STOP))) {
+            android_log_list_element next = android_log_peek_next(context);
+            if (!next.complete && (is_real_element(next.type) ||
+                    (next.type == EVENT_TYPE_LIST))) {
+                if (strOutLen == 0) {
+                    overflow = true;
+                } else {
+                    *strOut++ = ',';
+                    strOutLen--;
+                }
+            }
+        }
+    } while ((elem.type != EVENT_TYPE_UNKNOWN) && !overflow && !elem.complete);
+
+    android_log_destroy(&context);
+
+    if (overflow) {
+        if (strOutLen < origStrOutLen) {
+            /* leave an indicator */
+            *(strOut-1) = '!';
+        } else {
+            /* nothing was written at all */
+            *strOut++ = '!';
+        }
+    }
+    *strOut++ = '\0';
+
+    if ((elem.type == EVENT_TYPE_UNKNOWN) && !elem.complete) {
+        fprintf(stderr, "Binary log entry conversion failed\n");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static const char *event_test_int32(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint32_t);
+
+    return "1076895760";
+}
+
+static const char *event_test_int64(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint64_t);
+
+    return "-9191740941672636400";
+}
+
+static const char *event_test_list_int64(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t) +
+                       sizeof(uint8_t) + sizeof(uint64_t);
+
+    return "[-9191740941672636400]";
+}
+
+static const char *event_test_simple_automagic_list(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    // The convenience API where we allow a simple list to be
+    // created without explicit begin or end calls.
+    EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t) +
+                       sizeof(uint8_t) + sizeof(uint32_t) +
+                       sizeof(uint8_t) + sizeof(uint64_t);
+
+    return "[1076895760,-9191740941672636400]";
+}
+
+static const char *event_test_list_empty(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t);
+
+    return "[]";
+}
+
+static const char *event_test_complex_nested_list(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+
+    EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+    EXPECT_LE(0, android_log_write_int32(ctx, 0x01020304));
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x0102030405060708));
+    EXPECT_LE(0, android_log_write_string8(ctx, "Hello World"));
+    EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+    EXPECT_LE(0, android_log_write_int32(ctx, 1));
+    EXPECT_LE(0, android_log_write_int32(ctx, 2));
+    EXPECT_LE(0, android_log_write_int32(ctx, 3));
+    EXPECT_LE(0, android_log_write_int32(ctx, 4));
+    EXPECT_LE(0, android_log_write_list_end(ctx));   // ]
+    EXPECT_LE(0, android_log_write_float32(ctx, 1.0102030405060708));
+    EXPECT_LE(0, android_log_write_list_end(ctx));   // ]
+
+    //
+    // This one checks for the automagic list creation because a list
+    // begin and end was missing for it! This is actually an <oops> corner
+    // case, and not the behavior we morally support. The automagic API is to
+    // allow for a simple case of a series of objects in a single list. e.g.
+    //   int32,int32,int32,string -> [int32,int32,int32,string]
+    //
+    EXPECT_LE(0, android_log_write_string8(ctx, "dlroW olleH"));
+
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t) +
+                       sizeof(uint8_t) + sizeof(uint8_t) +
+                           sizeof(uint8_t) + sizeof(uint32_t) +
+                           sizeof(uint8_t) + sizeof(uint64_t) +
+                           sizeof(uint8_t) + sizeof(uint32_t) +
+                                             sizeof("Hello World") - 1 +
+                           sizeof(uint8_t) + sizeof(uint8_t) +
+                               4 * (sizeof(uint8_t) + sizeof(uint32_t)) +
+                           sizeof(uint8_t) + sizeof(uint32_t) +
+                       sizeof(uint8_t) + sizeof(uint32_t) +
+                                         sizeof("dlroW olleH") - 1;
+
+    return "[[16909060,72623859790382856,Hello World,[1,2,3,4],1.010203],dlroW olleH]";
+}
+
+static const char *event_test_7_level_prefix(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 1));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 2));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 3));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 4));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 5));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 6));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 7));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) + 7 *
+      (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+    return "[[[[[[[1],2],3],4],5],6],7]";
+}
+
+static const char *event_test_7_level_suffix(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 1));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 2));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 3));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 4));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 5));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 6));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) + 6 *
+      (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+    return "[1,[2,[3,[4,[5,[6]]]]]]";
+}
+
+static const char *event_test_android_log_error_write(uint32_t tag, size_t &expected_len) {
+    EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, "dlroW olleH", 11));
+
+    expected_len = sizeof(uint32_t) +
+      sizeof(uint8_t) + sizeof(uint8_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+          sizeof(uint8_t) + sizeof(uint32_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("dlroW olleH") - 1;
+
+    return "[Hello World,42,dlroW olleH]";
+}
+
+static const char *event_test_android_log_error_write_null(uint32_t tag, size_t &expected_len) {
+    EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, NULL, 0));
+
+    expected_len = sizeof(uint32_t) +
+      sizeof(uint8_t) + sizeof(uint8_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+          sizeof(uint8_t) + sizeof(uint32_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("") - 1;
+
+    return "[Hello World,42,]";
+}
+
+// make sure all user buffers are flushed
+static void print_barrier() {
+    std::cout.flush();
+    fflush(stdout);
+    std::cerr.flush();
+    fflush(stderr); // everything else is paranoia ...
+}
+
+static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    log_time ts(android_log_clockid());
+
+    size_t expected_len;
+    const char *expected_string = (*fn)(1005, expected_len);
+
+    if (!expected_string) {
+        android_logger_list_close(logger_list);
+        return;
+    }
+
+    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 != expected_len)
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        ++count;
+
+        AndroidLogFormat *logformat = android_log_format_new();
+        EXPECT_TRUE(NULL != logformat);
+        AndroidLogEntry entry;
+        char msgBuf[1024];
+        int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+            &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+        EXPECT_EQ(0, processBinaryLogBuffer);
+        if (processBinaryLogBuffer == 0) {
+            print_barrier();
+            int printLogLine = android_log_printLogLine(
+                logformat, fileno(stderr), &entry);
+            print_barrier();
+            EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine);
+        }
+        android_log_format_free(logformat);
+
+        // test buffer reading API
+        snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData));
+        print_barrier();
+        fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
+        memset(msgBuf, 0, sizeof(msgBuf));
+        int buffer_to_string = android_log_buffer_to_string(
+            eventData + sizeof(uint32_t),
+            log_msg.entry.len - sizeof(uint32_t),
+            msgBuf, sizeof(msgBuf));
+        fprintf(stderr, "%s\n", msgBuf);
+        print_barrier();
+        EXPECT_EQ(0, buffer_to_string);
+        EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
+        EXPECT_EQ(0, strcmp(expected_string, msgBuf));
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, create_android_logger_int32) {
+    create_android_logger(event_test_int32);
+}
+
+TEST(liblog, create_android_logger_int64) {
+    create_android_logger(event_test_int64);
+}
+
+TEST(liblog, create_android_logger_list_int64) {
+    create_android_logger(event_test_list_int64);
+}
+
+TEST(liblog, create_android_logger_simple_automagic_list) {
+    create_android_logger(event_test_simple_automagic_list);
+}
+
+TEST(liblog, create_android_logger_list_empty) {
+    create_android_logger(event_test_list_empty);
+}
+
+TEST(liblog, create_android_logger_complex_nested_list) {
+    create_android_logger(event_test_complex_nested_list);
+}
+
+TEST(liblog, create_android_logger_7_level_prefix) {
+    create_android_logger(event_test_7_level_prefix);
+}
+
+TEST(liblog, create_android_logger_7_level_suffix) {
+    create_android_logger(event_test_7_level_suffix);
+}
+
+TEST(liblog, create_android_logger_android_log_error_write) {
+    create_android_logger(event_test_android_log_error_write);
+}
+
+TEST(liblog, create_android_logger_android_log_error_write_null) {
+    create_android_logger(event_test_android_log_error_write_null);
+}
+
+TEST(liblog, create_android_logger_overflow) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
+    if (ctx) {
+        for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+            EXPECT_LE(0, android_log_write_list_begin(ctx));
+        }
+        EXPECT_GT(0, android_log_write_list_begin(ctx));
+        /* One more for good measure, must be permanently unhappy */
+        EXPECT_GT(0, android_log_write_list_begin(ctx));
+        EXPECT_LE(0, android_log_destroy(&ctx));
+        EXPECT_TRUE(NULL == ctx);
+    }
+
+    ASSERT_TRUE(NULL != (ctx = create_android_logger(1005)));
+    for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+        EXPECT_LE(0, android_log_write_list_begin(ctx));
+        EXPECT_LE(0, android_log_write_int32(ctx, i));
+    }
+    EXPECT_GT(0, android_log_write_list_begin(ctx));
+    /* One more for good measure, must be permanently unhappy */
+    EXPECT_GT(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    ASSERT_TRUE(NULL == ctx);
+}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk
index 7906986..09fbbb1 100644
--- a/libmincrypt/Android.mk
+++ b/libmincrypt/Android.mk
@@ -14,5 +14,4 @@
 LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_HOST_STATIC_LIBRARY)
 
-include $(LOCAL_PATH)/tools/Android.mk \
-        $(LOCAL_PATH)/test/Android.mk
+include $(LOCAL_PATH)/test/Android.mk
diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk
deleted file mode 100644
index 3154914..0000000
--- a/libmincrypt/tools/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2008 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)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := dumpkey
-LOCAL_SRC_FILES := DumpPublicKey.java
-LOCAL_JAR_MANIFEST := DumpPublicKey.mf
-LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java
deleted file mode 100644
index 3eb1398..0000000
--- a/libmincrypt/tools/DumpPublicKey.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-package com.android.dumpkey;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-import java.io.FileInputStream;
-import java.math.BigInteger;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.KeyStore;
-import java.security.Key;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.ECPoint;
-
-/**
- * Command line tool to extract RSA public keys from X.509 certificates
- * and output source code with data initializers for the keys.
- * @hide
- */
-class DumpPublicKey {
-    /**
-     * @param key to perform sanity checks on
-     * @return version number of key.  Supported versions are:
-     *     1: 2048-bit RSA key with e=3 and SHA-1 hash
-     *     2: 2048-bit RSA key with e=65537 and SHA-1 hash
-     *     3: 2048-bit RSA key with e=3 and SHA-256 hash
-     *     4: 2048-bit RSA key with e=65537 and SHA-256 hash
-     * @throws Exception if the key has the wrong size or public exponent
-     */
-    static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
-        BigInteger pubexp = key.getPublicExponent();
-        BigInteger modulus = key.getModulus();
-        int version;
-
-        if (pubexp.equals(BigInteger.valueOf(3))) {
-            version = useSHA256 ? 3 : 1;
-        } else if (pubexp.equals(BigInteger.valueOf(65537))) {
-            version = useSHA256 ? 4 : 2;
-        } else {
-            throw new Exception("Public exponent should be 3 or 65537 but is " +
-                                pubexp.toString(10) + ".");
-        }
-
-        if (modulus.bitLength() != 2048) {
-             throw new Exception("Modulus should be 2048 bits long but is " +
-                        modulus.bitLength() + " bits.");
-        }
-
-        return version;
-    }
-
-    /**
-     * @param key to perform sanity checks on
-     * @return version number of key.  Supported versions are:
-     *     5: 256-bit EC key with curve NIST P-256
-     * @throws Exception if the key has the wrong size or public exponent
-     */
-    static int checkEC(ECPublicKey key) throws Exception {
-        if (key.getParams().getCurve().getField().getFieldSize() != 256) {
-            throw new Exception("Curve must be NIST P-256");
-        }
-
-        return 5;
-    }
-
-    /**
-     * Perform sanity check on public key.
-     */
-    static int check(PublicKey key, boolean useSHA256) throws Exception {
-        if (key instanceof RSAPublicKey) {
-            return checkRSA((RSAPublicKey) key, useSHA256);
-        } else if (key instanceof ECPublicKey) {
-            if (!useSHA256) {
-                throw new Exception("Must use SHA-256 with EC keys!");
-            }
-            return checkEC((ECPublicKey) key);
-        } else {
-            throw new Exception("Unsupported key class: " + key.getClass().getName());
-        }
-    }
-
-    /**
-     * @param key to output
-     * @return a String representing this public key.  If the key is a
-     *    version 1 key, the string will be a C initializer; this is
-     *    not true for newer key versions.
-     */
-    static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
-        int version = check(key, useSHA256);
-
-        BigInteger N = key.getModulus();
-
-        StringBuilder result = new StringBuilder();
-
-        int nwords = N.bitLength() / 32;    // # of 32 bit integers in modulus
-
-        if (version > 1) {
-            result.append("v");
-            result.append(Integer.toString(version));
-            result.append(" ");
-        }
-
-        result.append("{");
-        result.append(nwords);
-
-        BigInteger B = BigInteger.valueOf(0x100000000L);  // 2^32
-        BigInteger N0inv = B.subtract(N.modInverse(B));   // -1 / N[0] mod 2^32
-
-        result.append(",0x");
-        result.append(N0inv.toString(16));
-
-        BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
-        BigInteger RR = R.multiply(R).mod(N);    // 2^4096 mod N
-
-        // Write out modulus as little endian array of integers.
-        result.append(",{");
-        for (int i = 0; i < nwords; ++i) {
-            long n = N.mod(B).longValue();
-            result.append(n);
-
-            if (i != nwords - 1) {
-                result.append(",");
-            }
-
-            N = N.divide(B);
-        }
-        result.append("}");
-
-        // Write R^2 as little endian array of integers.
-        result.append(",{");
-        for (int i = 0; i < nwords; ++i) {
-            long rr = RR.mod(B).longValue();
-            result.append(rr);
-
-            if (i != nwords - 1) {
-                result.append(",");
-            }
-
-            RR = RR.divide(B);
-        }
-        result.append("}");
-
-        result.append("}");
-        return result.toString();
-    }
-
-    /**
-     * @param key to output
-     * @return a String representing this public key.  If the key is a
-     *    version 1 key, the string will be a C initializer; this is
-     *    not true for newer key versions.
-     */
-    static String printEC(ECPublicKey key) throws Exception {
-        int version = checkEC(key);
-
-        StringBuilder result = new StringBuilder();
-
-        result.append("v");
-        result.append(Integer.toString(version));
-        result.append(" ");
-
-        BigInteger X = key.getW().getAffineX();
-        BigInteger Y = key.getW().getAffineY();
-        int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8;    // # of 32 bit integers in X coordinate
-
-        result.append("{");
-        result.append(nbytes);
-
-        BigInteger B = BigInteger.valueOf(0x100L);  // 2^8
-
-        // Write out Y coordinate as array of characters.
-        result.append(",{");
-        for (int i = 0; i < nbytes; ++i) {
-            long n = X.mod(B).longValue();
-            result.append(n);
-
-            if (i != nbytes - 1) {
-                result.append(",");
-            }
-
-            X = X.divide(B);
-        }
-        result.append("}");
-
-        // Write out Y coordinate as array of characters.
-        result.append(",{");
-        for (int i = 0; i < nbytes; ++i) {
-            long n = Y.mod(B).longValue();
-            result.append(n);
-
-            if (i != nbytes - 1) {
-                result.append(",");
-            }
-
-            Y = Y.divide(B);
-        }
-        result.append("}");
-
-        result.append("}");
-        return result.toString();
-    }
-
-    static String print(PublicKey key, boolean useSHA256) throws Exception {
-        if (key instanceof RSAPublicKey) {
-            return printRSA((RSAPublicKey) key, useSHA256);
-        } else if (key instanceof ECPublicKey) {
-            return printEC((ECPublicKey) key);
-        } else {
-            throw new Exception("Unsupported key class: " + key.getClass().getName());
-        }
-    }
-
-    public static void main(String[] args) {
-        if (args.length < 1) {
-            System.err.println("Usage: DumpPublicKey certfile ... > source.c");
-            System.exit(1);
-        }
-        Security.addProvider(new BouncyCastleProvider());
-        try {
-            for (int i = 0; i < args.length; i++) {
-                FileInputStream input = new FileInputStream(args[i]);
-                CertificateFactory cf = CertificateFactory.getInstance("X.509");
-                X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
-
-                boolean useSHA256 = false;
-                String sigAlg = cert.getSigAlgName();
-                if ("SHA1withRSA".equals(sigAlg) || "MD5withRSA".equals(sigAlg)) {
-                    // SignApk has historically accepted "MD5withRSA"
-                    // certificates, but treated them as "SHA1withRSA"
-                    // anyway.  Continue to do so for backwards
-                    // compatibility.
-                  useSHA256 = false;
-                } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) {
-                  useSHA256 = true;
-                } else {
-                  System.err.println(args[i] + ": unsupported signature algorithm \"" +
-                                     sigAlg + "\"");
-                  System.exit(1);
-                }
-
-                PublicKey key = cert.getPublicKey();
-                check(key, useSHA256);
-                System.out.print(print(key, useSHA256));
-                System.out.println(i < args.length - 1 ? "," : "");
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-            System.exit(1);
-        }
-        System.exit(0);
-    }
-}
diff --git a/libmincrypt/tools/DumpPublicKey.mf b/libmincrypt/tools/DumpPublicKey.mf
deleted file mode 100644
index 7bb3bc8..0000000
--- a/libmincrypt/tools/DumpPublicKey.mf
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.dumpkey.DumpPublicKey
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index da07253..5644aa6 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -19,14 +19,27 @@
 
 #include "jni.h"
 #include <stdint.h>
+#if defined(__ANDROID__)
+#include <android/dlext.h>
+#endif
 
 namespace android {
 
 __attribute__((visibility("default")))
+void PreloadPublicNativeLibraries();
+
+__attribute__((visibility("default")))
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                         jobject class_loader, bool is_shared, jstring library_path,
                         jstring permitted_path);
 
+#if defined(__ANDROID__)
+// Look up linker namespace by class_loader. Returns nullptr if
+// there is no namespace associated with the class_loader.
+__attribute__((visibility("default")))
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+#endif
+
 };  // namespace android
 
 #endif  // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index a7a0713..f8bb5fd 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -57,9 +57,7 @@
 
 class LibraryNamespaces {
  public:
-  LibraryNamespaces() : initialized_(false) {
-    PreloadPublicLibraries();
-  }
+  LibraryNamespaces() : initialized_(false) { }
 
   android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
                                    bool is_shared,
@@ -79,10 +77,10 @@
 
     std::lock_guard<std::mutex> guard(mutex_);
 
-    auto it = FindNamespaceByClassLoader(env, class_loader);
+    android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
 
-    if (it != namespaces_.end()) {
-      return it->second;
+    if (ns != nullptr) {
+      return ns;
     }
 
     uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
@@ -90,21 +88,27 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
     }
 
-    android_namespace_t* ns =
-            android_create_namespace("classloader-namespace",
-                                     nullptr,
-                                     library_path.c_str(),
-                                     namespace_type,
-                                     java_permitted_path != nullptr ?
-                                        permitted_path.c_str() :
-                                        nullptr);
+    ns = android_create_namespace("classloader-namespace",
+                                  nullptr,
+                                  library_path.c_str(),
+                                  namespace_type,
+                                  java_permitted_path != nullptr ?
+                                      permitted_path.c_str() :
+                                      nullptr);
 
     namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
 
     return ns;
   }
 
- private:
+  android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+    auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+                [&](const std::pair<jweak, android_namespace_t*>& value) {
+                  return env->IsSameObject(value.first, class_loader);
+                });
+    return it != namespaces_.end() ? it->second : nullptr;
+  }
+
   void PreloadPublicLibraries() {
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
@@ -114,6 +118,7 @@
     }
   }
 
+ 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.
@@ -122,14 +127,6 @@
     return initialized_;
   }
 
-  std::vector<std::pair<jweak, android_namespace_t*>>::const_iterator
-  FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
-    return std::find_if(namespaces_.begin(), namespaces_.end(),
-            [&](const std::pair<jweak, android_namespace_t*>& value) {
-              return env->IsSameObject(value.first, class_loader);
-            });
-  }
-
   bool initialized_;
   std::mutex mutex_;
   std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
@@ -140,6 +137,12 @@
 static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
 #endif
 
+void PreloadPublicNativeLibraries() {
+#if defined(__ANDROID__)
+  g_namespaces->PreloadPublicLibraries();
+#endif
+}
+
 
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                         jobject class_loader, bool is_shared, jstring java_library_path,
@@ -169,4 +172,10 @@
 #endif
 }
 
+#if defined(__ANDROID__)
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
+#endif
+
 }; //  android namespace
diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h
index 9db20df..ecc242a 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.h
+++ b/libpixelflinger/codeflinger/GGLAssembler.h
@@ -288,6 +288,14 @@
 
 
 private:
+    // GGLAssembler hides RegisterAllocator's and ARMAssemblerProxy's reset
+    // methods by providing a reset method with a different parameter set. The
+    // intent of GGLAssembler's reset method is to wrap the inherited reset
+    // methods, so make these methods private in order to prevent direct calls
+    // to these methods from clients.
+    using RegisterAllocator::reset;
+    using ARMAssemblerProxy::reset;
+
     struct tex_coord_t {
         reg_t       s;
         reg_t       t;
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 168899c..4d602a6 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -206,7 +206,7 @@
 
             do {
                 alen = sizeof(ss);
-                c = accept(mSock, addrp, &alen);
+                c = accept4(mSock, addrp, &alen, SOCK_CLOEXEC);
                 SLOGV("%s got %d from accept", mSocketName, c);
             } while (c < 0 && errno == EINTR);
             if (c < 0) {
@@ -214,7 +214,6 @@
                 sleep(1);
                 continue;
             }
-            fcntl(c, F_SETFD, FD_CLOEXEC);
             pthread_mutex_lock(&mClientsLock);
             mClients->push_back(new SocketClient(c, true, mUseCmdNum));
             pthread_mutex_unlock(&mClientsLock);
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 3663c52..6f88a6d 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -72,7 +72,7 @@
 ifeq ($(TARGET_ARCH),mips)
 LOCAL_CFLAGS += -DALIGN_DOUBLE
 endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -fvisibility=protected
 
 LOCAL_STATIC_LIBRARIES := \
 	libcutils \
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index fd45c4a..6a26d00 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -16,7 +16,10 @@
 
 #include <stdlib.h>
 
+#include <private/android_filesystem_config.h>
+
 #include "FlushCommand.h"
+#include "LogBuffer.h"
 #include "LogBufferElement.h"
 #include "LogCommand.h"
 #include "LogReader.h"
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index fffc9ba..230dd11 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -20,10 +20,13 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/prctl.h>
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <string>
+
 #include <cutils/properties.h>
 #include <log/logger.h>
 #include <private/android_filesystem_config.h>
@@ -31,7 +34,9 @@
 
 #include "libaudit.h"
 #include "LogAudit.h"
+#include "LogBuffer.h"
 #include "LogKlog.h"
+#include "LogReader.h"
 
 #ifndef AUDITD_ENFORCE_INTEGRITY
 #define AUDITD_ENFORCE_INTEGRITY false
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 455ed58..3a84541 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -18,7 +18,10 @@
 #define _LOGD_LOG_AUDIT_H__
 
 #include <sysutils/SocketListener.h>
-#include "LogReader.h"
+
+#include "LogBuffer.h"
+
+class LogReader;
 
 class LogAudit : public SocketListener {
     LogBuffer *logbuf;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index fde9ad7..eb5194c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -25,9 +25,11 @@
 #include <log/logger.h>
 #include <private/android_logger.h>
 
+#include "LogBuffer.h"
 #include "LogBufferElement.h"
 #include "LogCommand.h"
 #include "LogReader.h"
+#include "LogUtils.h"
 
 const uint64_t LogBufferElement::FLUSH_ERROR(0);
 atomic_int_fast64_t LogBufferElement::sequence(1);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 9690489..ac2b128 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -20,13 +20,16 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/prctl.h>
 #include <sys/uio.h>
 #include <syslog.h>
 
 #include <log/logger.h>
 
+#include "LogBuffer.h"
 #include "LogKlog.h"
+#include "LogReader.h"
 
 #define KMSG_PRIORITY(PRI)           \
     '<',                             \
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 3c8cc87..ee73b71 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -19,10 +19,12 @@
 
 #include <sysutils/SocketListener.h>
 #include <log/log_read.h>
-#include "LogReader.h"
 
 char *log_strntok_r(char *s, size_t *len, char **saveptr, size_t *sublen);
 
+class LogBuffer;
+class LogReader;
+
 class LogKlog : public SocketListener {
     LogBuffer *logbuf;
     LogReader *reader;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 846dd7c..39dd227 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -27,6 +27,7 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "LogBuffer.h"
 #include "LogListener.h"
 #include "LogUtils.h"
 
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 667a3f2..2c07984 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -18,11 +18,15 @@
 #include <poll.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 
 #include <cutils/sockets.h>
 
-#include "LogReader.h"
 #include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReader.h"
+#include "LogUtils.h"
 
 LogReader::LogReader(LogBuffer *logbuf) :
         SocketListener(getLogSocket(), true),
@@ -176,6 +180,11 @@
     }
 
     FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+
+    // Set acceptable upper limit to wait for slow reader processing b/27242723
+    struct timeval t = { LOGD_SNDTIMEO, 0 };
+    setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char *)&t, sizeof(t));
+
     command.runSocketCommand(cli);
     return true;
 }
diff --git a/logd/LogReader.h b/logd/LogReader.h
index 91559a3..98674b8 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -18,8 +18,10 @@
 #define _LOGD_LOG_WRITER_H__
 
 #include <sysutils/SocketListener.h>
-#include "LogBuffer.h"
-#include "LogTimes.h"
+
+#define LOGD_SNDTIMEO 32
+
+class LogBuffer;
 
 class LogReader : public SocketListener {
     LogBuffer &mLogbuf;
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 1117088..f5969df 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -27,6 +27,7 @@
 #include <log/log.h>
 
 class LogReader;
+class LogBufferElement;
 
 class LogTimeEntry {
     static pthread_mutex_t timesLock;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index de19790..2014374 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -30,6 +30,8 @@
 #include <log/log.h>
 #include <log/logger.h>
 
+#include "../LogReader.h" // pickup LOGD_SNDTIMEO
+
 /*
  * returns statistics
  */
@@ -253,6 +255,9 @@
         fprintf(stderr, "lid=crash ");
         break;
     case 5:
+        fprintf(stderr, "lid=security ");
+        break;
+    case 6:
         fprintf(stderr, "lid=kernel ");
         break;
     default:
@@ -710,3 +715,56 @@
     EXPECT_TRUE(content_timeout);
     EXPECT_NE(0U, alarm_timeout);
 }
+
+// b/27242723 confirmed fixed
+TEST(logd, SNDTIMEO) {
+    static const unsigned sndtimeo = LOGD_SNDTIMEO; // <sigh> it has to be done!
+    static const unsigned sleep_time = sndtimeo + 3;
+    static const unsigned alarm_time = sleep_time + 5;
+
+    int fd;
+
+    ASSERT_TRUE((fd = socket_local_client("logdr",
+                                 ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                 SOCK_SEQPACKET)) > 0);
+
+    struct sigaction ignore, old_sigaction;
+    memset(&ignore, 0, sizeof(ignore));
+    ignore.sa_handler = caught_signal;
+    sigemptyset(&ignore.sa_mask);
+    sigaction(SIGALRM, &ignore, &old_sigaction);
+    unsigned int old_alarm = alarm(alarm_time);
+
+    static const char ask[] = "stream lids=0,1,2,3,4,5,6"; // all sources
+    bool reader_requested = write(fd, ask, sizeof(ask)) == sizeof(ask);
+    EXPECT_TRUE(reader_requested);
+
+    log_msg msg;
+    bool read_one = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+    EXPECT_TRUE(read_one);
+    if (read_one) {
+        dump_log_msg("user", &msg, 3, -1);
+    }
+
+    fprintf (stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
+    sleep(sleep_time);
+
+    // flush will block if we did not trigger. if it did, last entry returns 0
+    int recv_ret;
+    do {
+        recv_ret = recv(fd, msg.buf, sizeof(msg), 0);
+    } while (recv_ret > 0);
+    int save_errno = (recv_ret < 0) ? errno : 0;
+
+    EXPECT_NE(0U, alarm(old_alarm));
+    sigaction(SIGALRM, &old_sigaction, NULL);
+
+    EXPECT_EQ(0, recv_ret);
+    if (recv_ret > 0) {
+        dump_log_msg("user", &msg, 3, -1);
+    }
+    EXPECT_EQ(0, save_errno);
+
+    close(fd);
+}
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 29bb1cf..4e702a1 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -11,4 +11,4 @@
     class main
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
-    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
+    writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 8ed5e9e..692af99 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -11,4 +11,4 @@
     class main
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
-    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
+    writepid /dev/cpuset/foreground/tasks