Merge "Update BacktraceOffline for LLVM rebase to r256229."
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index db9b710..d29c08e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -50,6 +50,15 @@
     __adb_serial = serial;
 }
 
+void adb_get_transport(TransportType* type, const char** serial) {
+    if (type) {
+        *type = __adb_transport;
+    }
+    if (serial) {
+        *serial = __adb_serial;
+    }
+}
+
 void adb_set_tcp_specifics(int server_port)
 {
     __adb_server_port = server_port;
diff --git a/adb/adb_client.h b/adb/adb_client.h
index a9df4d7..d5cd922 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -39,6 +39,9 @@
 // Set the preferred transport to connect to.
 void adb_set_transport(TransportType type, const char* _Nullable serial);
 
+// Get the preferred transport to connect to.
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial);
+
 // Set TCP specifics of the transport to use.
 void adb_set_tcp_specifics(int server_port);
 
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f1149b3..89fcd66 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
 
 #include <string>
 
+#include <android-base/macros.h>
+
 void close_stdin();
 
 bool getcwd(std::string* cwd);
@@ -39,4 +41,45 @@
 
 bool set_file_block_mode(int fd, bool block);
 
+extern int adb_close(int fd);
+
+// Helper to automatically close an FD when it goes out of scope.
+class ScopedFd {
+  public:
+    ScopedFd() {
+    }
+
+    ~ScopedFd() {
+        Reset();
+    }
+
+    void Reset(int fd = -1) {
+        if (fd != fd_) {
+            if (valid()) {
+                adb_close(fd_);
+            }
+            fd_ = fd;
+        }
+    }
+
+    int Release() {
+        int temp = fd_;
+        fd_ = -1;
+        return temp;
+    }
+
+    bool valid() const {
+        return fd_ >= 0;
+    }
+
+    int fd() const {
+        return fd_;
+    }
+
+  private:
+    int fd_ = -1;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
 #endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 37d1146..a856672 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1029,8 +1029,8 @@
     // TODO: when we have libc++ for Windows, use a regular expression instead.
     // wait-for-((any|local|usb)-)?(bootloader|device|recovery|sideload)
 
-    char type[20];
-    char state[20];
+    char type[20 + 1]; // sscanf's %20[...] doesn't include the NUL.
+    char state[20 + 1];
     int length = 0;
     if (sscanf(service, "wait-for-%20[a-z]-%20[a-z]%n", type, state, &length) < 2 ||
         length != static_cast<int>(strlen(service))) {
@@ -1072,6 +1072,51 @@
     return adb_command(cmd);
 }
 
+static bool adb_root(const char* command) {
+    std::string error;
+    ScopedFd fd;
+
+    fd.Reset(adb_connect(android::base::StringPrintf("%s:", command), &error));
+    if (!fd.valid()) {
+        fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
+        return false;
+    }
+
+    // Figure out whether we actually did anything.
+    char buf[256];
+    char* cur = buf;
+    ssize_t bytes_left = sizeof(buf);
+    while (bytes_left > 0) {
+        ssize_t bytes_read = adb_read(fd.fd(), cur, bytes_left);
+        if (bytes_read == 0) {
+            break;
+        } else if (bytes_read < 0) {
+            fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
+            return false;
+        }
+        cur += bytes_read;
+        bytes_left -= bytes_read;
+    }
+
+    if (bytes_left == 0) {
+        fprintf(stderr, "adb: unexpected output length for %s\n", command);
+        return false;
+    }
+
+    fflush(stdout);
+    WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
+    if (cur != buf && strstr(buf, "restarting") == nullptr) {
+        return true;
+    }
+
+    // Give adbd 500ms to kill itself, then wait-for-device for it to come back up.
+    adb_sleep_ms(500);
+    TransportType type;
+    const char* serial;
+    adb_get_transport(&type, &serial);
+    return wait_for_device("wait-for-device", type, serial);
+}
+
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
 static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1220,6 +1265,9 @@
     printf("Now unlock your device and confirm the restore operation.\n");
     copy_to_file(tarFd, fd);
 
+    // Wait until the other side finishes, or it'll get sent SIGHUP.
+    copy_to_file(fd, STDOUT_FILENO);
+
     adb_close(fd);
     adb_close(tarFd);
     return 0;
@@ -1632,8 +1680,6 @@
              !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
              !strcmp(argv[0], "usb") ||
-             !strcmp(argv[0], "root") ||
-             !strcmp(argv[0], "unroot") ||
              !strcmp(argv[0], "disable-verity") ||
              !strcmp(argv[0], "enable-verity")) {
         std::string command;
@@ -1645,8 +1691,9 @@
             command = android::base::StringPrintf("%s:", argv[0]);
         }
         return adb_connect_command(command);
-    }
-    else if (!strcmp(argv[0], "bugreport")) {
+    } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
+        return adb_root(argv[0]) ? 0 : 1;
+    } else if (!strcmp(argv[0], "bugreport")) {
         if (argc != 1) return usage();
         // No need for shell protocol with bugreport, always disable for
         // simplicity.
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index cdbff11..6a9e163 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -60,6 +60,18 @@
     }
 }
 
+static bool should_pull_file(mode_t mode) {
+    return mode & (S_IFREG | S_IFBLK | S_IFCHR);
+}
+
+static bool should_push_file(mode_t mode) {
+    mode_t mask = S_IFREG;
+#if !defined(_WIN32)
+    mask |= S_IFLNK;
+#endif
+    return mode & mask;
+}
+
 struct copyinfo {
     std::string lpath;
     std::string rpath;
@@ -483,11 +495,6 @@
 #endif
     }
 
-    if (!S_ISREG(mode)) {
-        sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
-        return false;
-    }
-
     struct stat st;
     if (stat(lpath, &st) == -1) {
         sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
@@ -619,13 +626,12 @@
         if (S_ISDIR(st.st_mode)) {
             dirlist.push_back(ci);
         } else {
-            if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                sc.Error("skipping special file '%s'", lpath.c_str());
+            if (!should_push_file(st.st_mode)) {
+                sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
                 ci.skip = true;
-            } else {
-                ci.time = st.st_mtime;
-                ci.size = st.st_size;
             }
+            ci.time = st.st_mtime;
+            ci.size = st.st_size;
             file_list->push_back(ci);
         }
     }
@@ -767,6 +773,9 @@
             success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
                                              false, false);
             continue;
+        } else if (!should_push_file(st.st_mode)) {
+            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
+            continue;
         }
 
         std::string path_holder;
@@ -804,7 +813,7 @@
     std::vector<copyinfo> linklist;
 
     // 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);
+    copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
     file_list->push_back(ci);
 
     // Put the files/dirs in rpath on the lists.
@@ -819,6 +828,10 @@
         } else if (S_ISLNK(mode)) {
             linklist.push_back(ci);
         } else {
+            if (!should_pull_file(ci.mode)) {
+                sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
+                ci.skip = true;
+            }
             ci.time = time;
             ci.size = size;
             file_list->push_back(ci);
@@ -975,11 +988,6 @@
             src_isdir = remote_symlink_isdir(sc, src_path);
         }
 
-        if ((src_mode & (S_IFREG | S_IFDIR | S_IFBLK | S_IFCHR)) == 0) {
-            sc.Error("skipping remote object '%s' (mode = 0o%o)", src_path, src_mode);
-            continue;
-        }
-
         if (src_isdir) {
             std::string dst_dir = dst;
 
@@ -998,27 +1006,30 @@
 
             success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
             continue;
-        } else {
-            std::string path_holder;
-            if (dst_isdir) {
-                // If we're copying a remote file to a local directory, we
-                // really want to copy to local_dir + OS_PATH_SEPARATOR +
-                // basename(remote).
-                path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
-                                                          adb_basename(src_path).c_str());
-                dst_path = path_holder.c_str();
-            }
+        } else if (!should_pull_file(src_mode)) {
+            sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_mode);
+            continue;
+        }
 
-            sc.SetExpectedTotalBytes(src_size);
-            if (!sync_recv(sc, src_path, dst_path)) {
-                success = false;
-                continue;
-            }
+        std::string path_holder;
+        if (dst_isdir) {
+            // If we're copying a remote file to a local directory, we
+            // really want to copy to local_dir + OS_PATH_SEPARATOR +
+            // basename(remote).
+            path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+                                                      adb_basename(src_path).c_str());
+            dst_path = path_holder.c_str();
+        }
 
-            if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
-                success = false;
-                continue;
-            }
+        sc.SetExpectedTotalBytes(src_size);
+        if (!sync_recv(sc, src_path, dst_path)) {
+            success = false;
+            continue;
+        }
+
+        if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+            success = false;
+            continue;
         }
     }
 
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index f84447f..ce10708 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -135,37 +135,6 @@
     return received;
 }
 
-// Helper to automatically close an FD when it goes out of scope.
-class ScopedFd {
-  public:
-    ScopedFd() {}
-    ~ScopedFd() { Reset(); }
-
-    void Reset(int fd=-1) {
-        if (fd != fd_) {
-            if (valid()) {
-                adb_close(fd_);
-            }
-            fd_ = fd;
-        }
-    }
-
-    int Release() {
-        int temp = fd_;
-        fd_ = -1;
-        return temp;
-    }
-
-    bool valid() const { return fd_ >= 0; }
-
-    int fd() const { return fd_; }
-
-  private:
-    int fd_ = -1;
-
-    DISALLOW_COPY_AND_ASSIGN(ScopedFd);
-};
-
 // Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
 bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) {
     int sockets[2];
@@ -315,7 +284,9 @@
     if (type_ == SubprocessType::kPty) {
         int fd;
         pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
-        stdinout_sfd_.Reset(fd);
+        if (pid_ > 0) {
+          stdinout_sfd_.Reset(fd);
+        }
     } else {
         if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
             *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index ce0f289..81d201e 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -576,8 +576,7 @@
 // Closes a file descriptor that came from adb_open() or adb_open_mode(), but
 // not designed to take a file descriptor from unix_open(). See the comments
 // for adb_open() for more info.
-static __inline__ int  adb_close(int fd)
-{
+__inline__ int adb_close(int fd) {
     return close(fd);
 }
 #undef   close
diff --git a/adb/test_device.py b/adb/test_device.py
index 2acc444..9dab3ae 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -216,8 +216,8 @@
         """Send data through adb forward and read it back via adb reverse"""
         forward_port = 12345
         reverse_port = forward_port + 1
-        forward_spec = "tcp:" + str(forward_port)
-        reverse_spec = "tcp:" + str(reverse_port)
+        forward_spec = 'tcp:' + str(forward_port)
+        reverse_spec = 'tcp:' + str(reverse_port)
         forward_setup = False
         reverse_setup = False
 
@@ -739,7 +739,7 @@
             # Create some random files and a subdirectory containing more files.
             temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
 
-            subdir = os.path.join(host_dir, "subdir")
+            subdir = os.path.join(host_dir, 'subdir')
             os.mkdir(subdir)
             subdir_temp_files = make_random_host_files(in_dir=subdir,
                                                        num_files=4)
@@ -756,7 +756,7 @@
             for subdir_temp_file in subdir_temp_files:
                 remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
                                              # BROKEN: http://b/25394682
-                                             # "subdir",
+                                             # 'subdir';
                                              temp_file.base_name)
                 self._verify_remote(temp_file.checksum, remote_path)
 
@@ -777,11 +777,11 @@
             tmp_file.flush()
             try:
                 self.device.push(local=tmp_file.name, remote='/system/')
-                self.fail("push should not have succeeded")
+                self.fail('push should not have succeeded')
             except subprocess.CalledProcessError as e:
                 output = e.output
 
-            self.assertIn("Permission denied", output)
+            self.assertIn('Permission denied', output)
 
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
@@ -850,13 +850,13 @@
 
         Bug: http://b/27362811
         """
-        if os.name != "posix":
+        if os.name != 'posix':
             raise unittest.SkipTest('requires POSIX')
 
         try:
             host_dir = tempfile.mkdtemp()
-            real_dir = os.path.join(host_dir, "dir")
-            symlink = os.path.join(host_dir, "symlink")
+            real_dir = os.path.join(host_dir, 'dir')
+            symlink = os.path.join(host_dir, 'symlink')
             os.mkdir(real_dir)
             os.symlink(real_dir, symlink)
 
@@ -882,12 +882,12 @@
 
     def test_pull_dir_symlink_collision(self):
         """Pull a directory into a colliding symlink to directory."""
-        if os.name != "posix":
+        if os.name != 'posix':
             raise unittest.SkipTest('requires POSIX')
 
         try:
             host_dir = tempfile.mkdtemp()
-            real_dir = os.path.join(host_dir, "real")
+            real_dir = os.path.join(host_dir, 'real')
             tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
             symlink = os.path.join(host_dir, tmp_dirname)
             os.mkdir(real_dir)
@@ -911,6 +911,30 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
+    def test_pull_dir_nonexistent(self):
+        """Pull a directory of files from the device to a nonexistent path."""
+        try:
+            host_dir = tempfile.mkdtemp()
+            dest_dir = os.path.join(host_dir, 'dest')
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+            self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+            # Populate device directory with random files.
+            temp_files = make_random_device_files(
+                self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+            self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+            for temp_file in temp_files:
+                host_path = os.path.join(dest_dir, temp_file.base_name)
+                self._verify_local(temp_file.checksum, host_path)
+
+            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        finally:
+            if host_dir is not None:
+                shutil.rmtree(host_dir)
+
     def test_pull_symlink_dir(self):
         """Pull a symlink to a directory of symlinks to files."""
         try:
@@ -966,7 +990,7 @@
         try:
             host_dir = tempfile.mkdtemp()
 
-            subdir = posixpath.join(self.DEVICE_TEMP_DIR, "subdir")
+            subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
             self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
             self.device.shell(['mkdir', '-p', subdir])
 
@@ -987,7 +1011,7 @@
 
             for subdir_temp_file in subdir_temp_files:
                 local_path = os.path.join(host_dir,
-                                          "subdir",
+                                          'subdir',
                                           subdir_temp_file.base_name)
                 self._verify_local(subdir_temp_file.checksum, local_path)
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 372bedf..f6c9df4 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -260,7 +260,8 @@
             } else {
                 /* Host is connected. Register the transport, and start the
                  * exchange. */
-                register_socket_transport(fd, "host", port, 1);
+                std::string serial = android::base::StringPrintf("host-%d", fd);
+                register_socket_transport(fd, serial.c_str(), port, 1);
                 if (!WriteFdExactly(fd, _start_req, strlen(_start_req))) {
                     adb_close(fd);
                 }
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cd526d0..e5babed 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -150,6 +150,14 @@
 #define UNIMPLEMENTED(level) \
   LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
 
+#ifdef __clang_analyzer__
+// ClangL static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;;abort())
+#else
+#define ABORT_AFTER_LOG_FATAL
+#endif
+
 // Check whether condition x holds and LOG(FATAL) if not. The value of the
 // expression x is only evaluated once. Extra logging can be appended using <<
 // after. For example:
@@ -160,6 +168,7 @@
   if (LIKELY((x)))                                                            \
     ;                                                                         \
   else                                                                        \
+    ABORT_AFTER_LOG_FATAL                                                     \
     ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                                 ::android::base::FATAL, -1).stream()          \
         << "Check failed: " #x << " "
@@ -169,6 +178,7 @@
   for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);        \
        UNLIKELY(!(_values.lhs OP _values.rhs));                             \
        /* empty */)                                                         \
+  ABORT_AFTER_LOG_FATAL                                                     \
   ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                               ::android::base::FATAL, -1).stream()          \
       << "Check failed: " << #LHS << " " << #OP << " " << #RHS              \
@@ -192,6 +202,7 @@
   if (LIKELY((strcmp(s1, s2) == 0) == sense))                              \
     ;                                                                      \
   else                                                                     \
+    ABORT_AFTER_LOG_FATAL                                                  \
     LOG(FATAL) << "Check failed: "                                         \
                << "\"" << s1 << "\""                                       \
                << (sense ? " == " : " != ") << "\"" << s2 << "\""
@@ -206,6 +217,7 @@
     int rc = call args;                                                \
     if (rc != 0) {                                                     \
       errno = rc;                                                      \
+      ABORT_AFTER_LOG_FATAL                                            \
       PLOG(FATAL) << #call << " failed for " << what; \
     }                                                                  \
   } while (false)
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
index c6349c1..3d02752 100644
--- a/bootstat/Android.mk
+++ b/bootstat/Android.mk
@@ -20,32 +20,33 @@
 
 bootstat_lib_src_files := \
         boot_event_record_store.cpp \
-        event_log_list_builder.cpp
+        event_log_list_builder.cpp \
+        uptime_parser.cpp \
 
 bootstat_src_files := \
-        bootstat.cpp
+        bootstat.cpp \
 
 bootstat_test_src_files := \
         boot_event_record_store_test.cpp \
         event_log_list_builder_test.cpp \
-        testrunner.cpp
+        testrunner.cpp \
 
 bootstat_shared_libs := \
         libbase \
         libcutils \
-        liblog
+        liblog \
 
 bootstat_cflags := \
         -Wall \
         -Wextra \
-        -Werror
+        -Werror \
 
 bootstat_cppflags := \
-        -Wno-non-virtual-dtor
+        -Wno-non-virtual-dtor \
 
 bootstat_debug_cflags := \
         $(bootstat_cflags) \
-        -UNDEBUG
+        -UNDEBUG \
 
 # 524291 corresponds to sysui_histogram, from
 # frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 8282b40..40254f8 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -24,6 +24,7 @@
 #include <utility>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include "uptime_parser.h"
 
 namespace {
 
@@ -51,14 +52,7 @@
 }
 
 void BootEventRecordStore::AddBootEvent(const std::string& event) {
-  std::string uptime_str;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
-    LOG(ERROR) << "Failed to read /proc/uptime";
-  }
-
-  // Cast intentionally rounds down.
-  int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-  AddBootEventWithValue(event, uptime);
+  AddBootEventWithValue(event, bootstat::ParseUptime());
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 0d7bbb0..343f9d0 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -25,6 +25,7 @@
 #include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
+#include "uptime_parser.h"
 
 using testing::UnorderedElementsAreArray;
 
@@ -38,17 +39,6 @@
   return (abs(a - b) <= FUZZ_SECONDS);
 }
 
-// Returns the uptime as read from /proc/uptime, rounded down to an integer.
-int32_t ReadUptime() {
-  std::string uptime_str;
-  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
-    return -1;
-  }
-
-  // Cast to int to round down.
-  return static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-}
-
 // Recursively deletes the directory at |path|.
 void DeleteDirectory(const std::string& path) {
   typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;
@@ -110,7 +100,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  int32_t uptime = ReadUptime();
+  time_t uptime = bootstat::ParseUptime();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cenozoic");
@@ -125,7 +115,7 @@
   BootEventRecordStore store;
   store.SetStorePath(GetStorePathForTesting());
 
-  int32_t uptime = ReadUptime();
+  time_t uptime = bootstat::ParseUptime();
   ASSERT_NE(-1, uptime);
 
   store.AddBootEvent("cretaceous");
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c199190..9214e6d 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -20,6 +20,7 @@
 
 #include <getopt.h>
 #include <unistd.h>
+#include <cmath>
 #include <cstddef>
 #include <cstdio>
 #include <ctime>
@@ -31,6 +32,7 @@
 #include <log/log.h>
 #include "boot_event_record_store.h"
 #include "event_log_list_builder.h"
+#include "uptime_parser.h"
 
 namespace {
 
@@ -134,6 +136,30 @@
   {"Reboot", 18},
   {"rtc", 19},
   {"edl", 20},
+  {"oem_pon1", 21},
+  {"oem_powerkey", 22},
+  {"oem_unknown_reset", 23},
+  {"srto: HWWDT reset SC", 24},
+  {"srto: HWWDT reset platform", 25},
+  {"srto: bootloader", 26},
+  {"srto: kernel panic", 27},
+  {"srto: kernel watchdog reset", 28},
+  {"srto: normal", 29},
+  {"srto: reboot", 30},
+  {"srto: reboot-bootloader", 31},
+  {"srto: security watchdog reset", 32},
+  {"srto: wakesrc", 33},
+  {"srto: watchdog", 34},
+  {"srto:1-1", 35},
+  {"srto:omap_hsmm", 36},
+  {"srto:phy0", 37},
+  {"srto:rtc0", 38},
+  {"srto:touchpad", 39},
+  {"watchdog", 40},
+  {"watchdogr", 41},
+  {"wdog_bark", 42},
+  {"wdog_bite", 43},
+  {"wdog_reset", 44},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -149,6 +175,37 @@
   return kUnknownBootReason;
 }
 
+// Records several metrics related to the time it takes to boot the device,
+// including disambiguating boot time on encrypted or non-encrypted devices.
+void RecordBootComplete() {
+  BootEventRecordStore boot_event_store;
+  time_t uptime = bootstat::ParseUptime();
+
+  BootEventRecordStore::BootEventRecord record;
+
+  // post_decrypt_time_elapsed is only logged on encrypted devices.
+  if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
+    // Log the amount of time elapsed until the device is decrypted, which
+    // includes the variable amount of time the user takes to enter the
+    // decryption password.
+    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+
+    // Subtract the decryption time to normalize the boot cycle timing.
+    time_t boot_complete = uptime - record.second;
+    boot_event_store.AddBootEventWithValue("boot_complete_post_decrypt",
+                                           boot_complete);
+
+
+  } else {
+    boot_event_store.AddBootEventWithValue("boot_complete_no_encryption",
+                                           uptime);
+  }
+
+  // Record the total time from device startup to boot complete, regardless of
+  // encryption state.
+  boot_event_store.AddBootEventWithValue("boot_complete", uptime);
+}
+
 // Records the boot_reason metric by querying the ro.boot.bootreason system
 // property.
 void RecordBootReason() {
@@ -166,17 +223,29 @@
 
   time_t current_time_utc = time(nullptr);
 
+  static const char* factory_reset_current_time = "factory_reset_current_time";
+  if (current_time_utc < 0) {
+    // UMA does not display negative values in buckets, so convert to positive.
+    LogBootEvent(factory_reset_current_time, std::abs(current_time_utc));
+    return;
+  } else {
+    LogBootEvent(factory_reset_current_time, current_time_utc);
+  }
+
   // The factory_reset boot event does not exist after the device is reset, so
   // use this signal to mark the time of the factory reset.
   if (!boot_event_store.GetBootEvent("factory_reset", &record)) {
     boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc);
-    boot_event_store.AddBootEventWithValue("time_since_factory_reset", 0);
+
+    // Don't log the time_since_factory_reset until some time has elapsed.
+    // The data is not meaningful yet and skews the histogram buckets.
     return;
   }
 
   // Calculate and record the difference in time between now and the
   // factory_reset time.
   time_t factory_reset_utc = record.second;
+  LogBootEvent("factory_reset_record_value", factory_reset_utc);
   time_t time_since_factory_reset = difftime(current_time_utc,
                                              factory_reset_utc);
   boot_event_store.AddBootEventWithValue("time_since_factory_reset",
@@ -192,6 +261,7 @@
   LOG(INFO) << "Service started: " << cmd_line;
 
   int option_index = 0;
+  static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
@@ -199,6 +269,7 @@
     { "log",             no_argument,       NULL,   'l' },
     { "print",           no_argument,       NULL,   'p' },
     { "record",          required_argument, NULL,   'r' },
+    { boot_complete_str, no_argument,       NULL,   0 },
     { boot_reason_str,   no_argument,       NULL,   0 },
     { factory_reset_str, no_argument,       NULL,   0 },
     { NULL,              0,                 NULL,   0 }
@@ -210,7 +281,9 @@
       // This case handles long options which have no single-character mapping.
       case 0: {
         const std::string option_name = long_options[option_index].name;
-        if (option_name == boot_reason_str) {
+        if (option_name == boot_complete_str) {
+          RecordBootComplete();
+        } else if (option_name == boot_reason_str) {
           RecordBootReason();
         } else if (option_name == factory_reset_str) {
           RecordFactoryReset();
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 13ef27e..ba8f81c 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -3,12 +3,23 @@
 on post-fs-data
     mkdir /data/misc/bootstat 0700 root root
 
-# This marker, boot animation stopped, is considered the point at which the
+# Record the time at which the user has successfully entered the pin to decrypt
+# the device, /data is decrypted, and the system is entering the main boot phase.
+#
+# post-fs-data: /data is writable
+# property:init.svc.bootanim=running: The boot animation is running
+on post-fs-data && property:init.svc.bootanim=running
+    exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+
+# The first marker, boot animation stopped, is considered the point at which
 # the user may interact with the device, so it is a good proxy for the boot
 # complete signal.
-on property:init.svc.bootanim=stopped
-    # Record boot_complete timing event.
-    exec - root root -- /system/bin/bootstat -r boot_complete
+#
+# The second marker ensures an encrypted device is decrypted before logging
+# boot time data.
+on property:init.svc.bootanim=stopped && property:vold.decrypt=trigger_restart_framework
+    # Record boot_complete and related stats (decryption, etc).
+    exec - root root -- /system/bin/bootstat --record_boot_complete
 
     # Record the boot reason.
     exec - root root -- /system/bin/bootstat --record_boot_reason
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
new file mode 100644
index 0000000..7c2034c
--- /dev/null
+++ b/bootstat/uptime_parser.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uptime_parser.h"
+
+#include <time.h>
+#include <cstdlib>
+#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace bootstat {
+
+time_t ParseUptime() {
+  std::string uptime_str;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+    PLOG(ERROR) << "Failed to read /proc/uptime";
+    return -1;
+  }
+
+  // Cast intentionally rounds down.
+  return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+}  // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.h b/bootstat/uptime_parser.h
new file mode 100644
index 0000000..756ae9b
--- /dev/null
+++ b/bootstat/uptime_parser.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UPTIME_PARSER_H_
+#define UPTIME_PARSER_H_
+
+#include <time.h>
+
+namespace bootstat {
+
+// Returns the number of seconds the system has been on since reboot.
+time_t ParseUptime();
+
+}  // namespace bootstat
+
+#endif  // UPTIME_PARSER_H_
\ No newline at end of file
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 9e4f1f7..6469db4 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -15,6 +15,7 @@
     debuggerd.cpp \
     elf_utils.cpp \
     getevent.cpp \
+    signal_sender.cpp \
     tombstone.cpp \
     utility.cpp \
 
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index b6916e5..32843d8 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -91,7 +91,8 @@
   if (backtrace->Unwind(0)) {
     dump_backtrace_to_log(backtrace.get(), log, "  ");
   } else {
-    ALOGE("Unwind failed: tid = %d", tid);
+    ALOGE("Unwind failed: tid = %d: %s", tid,
+          backtrace->GetErrorString(backtrace->GetError()).c_str());
   }
 }
 
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index ac1c58c..97f4096 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -15,21 +15,20 @@
  */
 
 #include <dirent.h>
+#include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <elf.h>
 #include <sys/poll.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/wait.h>
+#include <time.h>
 
 #include <set>
 
@@ -48,6 +47,7 @@
 
 #include "backtrace.h"
 #include "getevent.h"
+#include "signal_sender.h"
 #include "tombstone.h"
 #include "utility.h"
 
@@ -248,8 +248,8 @@
   return 0;
 }
 
-static bool should_attach_gdb(debugger_request_t* request) {
-  if (request->action == DEBUGGER_ACTION_CRASH) {
+static bool should_attach_gdb(const debugger_request_t& request) {
+  if (request.action == DEBUGGER_ACTION_CRASH) {
     return property_get_bool("debug.debuggerd.wait_for_gdb", false);
   }
   return false;
@@ -374,7 +374,8 @@
 }
 
 static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
-                         BacktraceMap* backtrace_map, const std::set<pid_t>& siblings) {
+                         BacktraceMap* backtrace_map, const std::set<pid_t>& siblings,
+                         int* crash_signal) {
   if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
     ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
     return false;
@@ -416,11 +417,7 @@
 #endif
       case SIGTRAP:
         ALOGV("stopped -- fatal signal\n");
-        // Send a SIGSTOP to the process to make all of
-        // the non-signaled threads stop moving.  Without
-        // this we get a lot of "ptrace detach failed:
-        // No such process".
-        kill(request.pid, SIGSTOP);
+        *crash_signal = signal;
         engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
                           request.original_si_code, request.abort_msg_address);
         break;
@@ -449,104 +446,7 @@
   return true;
 }
 
-static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t target_pid) {
-  int input_pipe[2];
-  int output_pipe[2];
-  if (pipe(input_pipe) != 0) {
-    ALOGE("debuggerd: failed to create input pipe for signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  if (pipe(output_pipe) != 0) {
-    close(input_pipe[0]);
-    close(input_pipe[1]);
-    ALOGE("debuggerd: failed to create output pipe for signal sender: %s", strerror(errno));
-    return false;
-  }
-
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
-    return false;
-  } else if (fork_pid == 0) {
-    close(input_pipe[1]);
-    close(output_pipe[0]);
-    auto wait = [=]() {
-      char buf[1];
-      if (TEMP_FAILURE_RETRY(read(input_pipe[0], buf, 1)) != 1) {
-        ALOGE("debuggerd: signal sender failed to read from pipe");
-        exit(1);
-      }
-    };
-    auto notify_done = [=]() {
-      if (TEMP_FAILURE_RETRY(write(output_pipe[1], "", 1)) != 1) {
-        ALOGE("debuggerd: signal sender failed to write to pipe");
-        exit(1);
-      }
-    };
-
-    wait();
-    if (kill(target_pid, SIGSTOP) != 0) {
-      ALOGE("debuggerd: failed to stop target '%d': %s", target_pid, strerror(errno));
-    }
-    notify_done();
-
-    wait();
-    if (kill(target_pid, SIGCONT) != 0) {
-      ALOGE("debuggerd: failed to resume target '%d': %s", target_pid, strerror(errno));
-    }
-    notify_done();
-
-    exit(0);
-  } else {
-    close(input_pipe[0]);
-    close(output_pipe[1]);
-    *in_fd = input_pipe[1];
-    *out_fd = output_pipe[0];
-    *sender_pid = fork_pid;
-    return true;
-  }
-}
-
-static void handle_request(int fd) {
-  ALOGV("handle_request(%d)\n", fd);
-
-  ScopedFd closer(fd);
-  debugger_request_t request;
-  memset(&request, 0, sizeof(request));
-  int status = read_request(fd, &request);
-  if (status != 0) {
-    return;
-  }
-
-  ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid);
-
-#if defined(__LP64__)
-  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
-  // to the 64 bit debuggerd. If the process is a 32 bit executable,
-  // redirect the request to the 32 bit debuggerd.
-  if (is32bit(request.tid)) {
-    // Only dump backtrace and dump tombstone requests can be redirected.
-    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
-        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-      redirect_to_32(fd, &request);
-    } else {
-      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
-    }
-    return;
-  }
-#endif
-
-  // Fork a child to handle the rest of the request.
-  pid_t fork_pid = fork();
-  if (fork_pid == -1) {
-    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
-    return;
-  } else if (fork_pid != 0) {
-    waitpid(fork_pid, nullptr, 0);
-    return;
-  }
-
+static void worker_process(int fd, debugger_request_t& request) {
   // Open the tombstone file if we need it.
   std::string tombstone_path;
   int tombstone_fd = -1;
@@ -587,10 +487,7 @@
 
   // Don't attach to the sibling threads if we want to attach gdb.
   // Supposedly, it makes the process less reliable.
-  bool attach_gdb = should_attach_gdb(&request);
-  int signal_in_fd = -1;
-  int signal_out_fd = -1;
-  pid_t signal_pid = 0;
+  bool attach_gdb = should_attach_gdb(request);
   if (attach_gdb) {
     // Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
     if (init_getevent() != 0) {
@@ -598,21 +495,8 @@
       attach_gdb = false;
     }
 
-    // Fork a process that stays root, and listens on a pipe to pause and resume the target.
-    if (!fork_signal_sender(&signal_in_fd, &signal_out_fd, &signal_pid, request.pid)) {
-      attach_gdb = false;
-    }
   }
 
-  auto notify_signal_sender = [=]() {
-    char buf[1];
-    if (TEMP_FAILURE_RETRY(write(signal_in_fd, "", 1)) != 1) {
-      ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno));
-    } else if (TEMP_FAILURE_RETRY(read(signal_out_fd, buf, 1)) != 1) {
-      ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno));
-    }
-  };
-
   std::set<pid_t> siblings;
   if (!attach_gdb) {
     ptrace_siblings(request.pid, request.tid, siblings);
@@ -624,19 +508,26 @@
   bool succeeded = false;
 
   // Now that we've done everything that requires privileges, we can drop them.
-  if (drop_privileges()) {
-    succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings);
-    if (succeeded) {
-      if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
-        if (!tombstone_path.empty()) {
-          write(fd, tombstone_path.c_str(), tombstone_path.length());
-        }
+  if (!drop_privileges()) {
+    ALOGE("debuggerd: failed to drop privileges, exiting");
+    _exit(1);
+  }
+
+  int crash_signal = SIGKILL;
+  succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings, &crash_signal);
+  if (succeeded) {
+    if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+      if (!tombstone_path.empty()) {
+        write(fd, tombstone_path.c_str(), tombstone_path.length());
       }
     }
+  }
 
-    if (attach_gdb) {
-      // Tell the signal process to send SIGSTOP to the target.
-      notify_signal_sender();
+  if (attach_gdb) {
+    // Tell the signal process to send SIGSTOP to the target.
+    if (!send_signal(request.pid, 0, SIGSTOP)) {
+      ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
+      attach_gdb = false;
     }
   }
 
@@ -648,20 +539,137 @@
     ptrace(PTRACE_DETACH, sibling, 0, 0);
   }
 
+  // Send the signal back to the process if it crashed and we're not waiting for gdb.
+  if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
+    if (!send_signal(request.pid, request.tid, crash_signal)) {
+      ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
+    }
+  }
+
   // Wait for gdb, if requested.
   if (attach_gdb && succeeded) {
     wait_for_user_action(request);
 
     // Tell the signal process to send SIGCONT to the target.
-    notify_signal_sender();
+    if (!send_signal(request.pid, 0, SIGCONT)) {
+      ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
+    }
 
     uninit_getevent();
-    waitpid(signal_pid, nullptr, 0);
   }
 
   exit(!succeeded);
 }
 
+static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
+  struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
+  if (should_attach_gdb(request)) {
+    // If wait_for_gdb is enabled, set the timeout to something large.
+    timeout.tv_sec = INT_MAX;
+  }
+
+  sigset_t signal_set;
+  sigemptyset(&signal_set);
+  sigaddset(&signal_set, SIGCHLD);
+
+  bool kill_worker = false;
+  bool kill_target = false;
+  bool kill_self = false;
+
+  int status;
+  siginfo_t siginfo;
+  int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
+  if (signal == SIGCHLD) {
+    pid_t rc = waitpid(0, &status, WNOHANG | WUNTRACED);
+    if (rc != child_pid) {
+      ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
+      kill_worker = true;
+      kill_target = true;
+      kill_self = true;
+    }
+
+    if (WIFSIGNALED(status)) {
+      ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
+      kill_worker = false;
+      kill_target = true;
+    } else if (WIFSTOPPED(status)) {
+      ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
+      kill_worker = true;
+      kill_target = true;
+    }
+  } else {
+    ALOGE("debuggerd: worker process %d timed out", child_pid);
+    kill_worker = true;
+    kill_target = true;
+  }
+
+  if (kill_worker) {
+    // Something bad happened, kill the worker.
+    if (kill(child_pid, SIGKILL) != 0) {
+      ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
+    } else {
+      waitpid(child_pid, &status, 0);
+    }
+  }
+
+  if (kill_target) {
+    // Resume or kill the target, depending on what the initial request was.
+    if (request.action == DEBUGGER_ACTION_CRASH) {
+      ALOGE("debuggerd: killing target %d", request.pid);
+      kill(request.pid, SIGKILL);
+    } else {
+      ALOGE("debuggerd: resuming target %d", request.pid);
+      kill(request.pid, SIGCONT);
+    }
+  }
+
+  if (kill_self) {
+    stop_signal_sender();
+    _exit(1);
+  }
+}
+
+static void handle_request(int fd) {
+  ALOGV("handle_request(%d)\n", fd);
+
+  ScopedFd closer(fd);
+  debugger_request_t request;
+  memset(&request, 0, sizeof(request));
+  int status = read_request(fd, &request);
+  if (status != 0) {
+    return;
+  }
+
+  ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
+        request.gid, request.tid);
+
+#if defined(__LP64__)
+  // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
+  // to the 64 bit debuggerd. If the process is a 32 bit executable,
+  // redirect the request to the 32 bit debuggerd.
+  if (is32bit(request.tid)) {
+    // Only dump backtrace and dump tombstone requests can be redirected.
+    if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
+        request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+      redirect_to_32(fd, &request);
+    } else {
+      ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
+    }
+    return;
+  }
+#endif
+
+  // Fork a child to handle the rest of the request.
+  pid_t fork_pid = fork();
+  if (fork_pid == -1) {
+    ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
+  } else if (fork_pid == 0) {
+    worker_process(fd, request);
+  } else {
+    monitor_worker_process(fork_pid, request);
+  }
+}
+
 static int do_server() {
   // debuggerd crashes can't be reported to debuggerd.
   // Reset all of the crash handlers.
@@ -678,17 +686,22 @@
   // Ignore failed writes to closed sockets
   signal(SIGPIPE, SIG_IGN);
 
-  struct sigaction act;
-  act.sa_handler = SIG_DFL;
-  sigemptyset(&act.sa_mask);
-  sigaddset(&act.sa_mask,SIGCHLD);
-  act.sa_flags = SA_NOCLDWAIT;
-  sigaction(SIGCHLD, &act, 0);
+  // Block SIGCHLD so we can sigtimedwait for it.
+  sigset_t sigchld;
+  sigemptyset(&sigchld);
+  sigaddset(&sigchld, SIGCHLD);
+  sigprocmask(SIG_SETMASK, &sigchld, nullptr);
 
   int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
                               SOCK_STREAM | SOCK_CLOEXEC);
   if (s == -1) return 1;
 
+  // Fork a process that stays root, and listens on a pipe to pause and resume the target.
+  if (!start_signal_sender()) {
+    ALOGE("debuggerd: failed to fork signal sender");
+    return 1;
+  }
+
   ALOGI("debuggerd: starting\n");
 
   for (;;) {
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 3f0dbde..9959f2e 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -63,10 +63,10 @@
         if (nhdr.n_type == NT_GNU_BUILD_ID) {
           // Skip the name (which is the owner and should be "GNU").
           addr += NOTE_ALIGN(nhdr.n_namesz);
-          uint8_t build_id_data[128];
-          if (nhdr.n_namesz > sizeof(build_id_data)) {
-            ALOGE("Possible corrupted note, name size value is too large: %u",
-                  nhdr.n_namesz);
+          uint8_t build_id_data[160];
+          if (nhdr.n_descsz > sizeof(build_id_data)) {
+            ALOGE("Possible corrupted note, desc size value is too large: %u",
+                  nhdr.n_descsz);
             return false;
           }
           if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
new file mode 100644
index 0000000..1cfb704
--- /dev/null
+++ b/debuggerd/signal_sender.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+
+#include "signal_sender.h"
+
+static int signal_fd = -1;
+static pid_t signal_pid;
+struct signal_message {
+  pid_t pid;
+  pid_t tid;
+  int signal;
+};
+
+// Fork a process to send signals for the worker processes to use after they've dropped privileges.
+bool start_signal_sender() {
+  if (signal_pid != 0) {
+    ALOGE("debuggerd: attempted to start signal sender multiple times");
+    return false;
+  }
+
+  int sfd[2];
+  if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
+    ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
+    return false;
+  }
+
+  pid_t parent = getpid();
+  pid_t fork_pid = fork();
+  if (fork_pid == -1) {
+    ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
+    return false;
+  } else if (fork_pid == 0) {
+    close(sfd[1]);
+
+    while (true) {
+      signal_message msg;
+      int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
+      if (rc < 0) {
+        ALOGE("debuggerd: signal sender failed to read from socket");
+        break;
+      } else if (rc != sizeof(msg)) {
+        ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
+        break;
+      }
+
+      // Report success after sending a signal
+      int err = 0;
+      if (msg.tid > 0) {
+        if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
+          err = errno;
+        }
+      } else {
+        if (kill(msg.pid, msg.signal) != 0) {
+          err = errno;
+        }
+      }
+
+      if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
+        ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
+      }
+    }
+
+    // Our parent proably died, but if not, kill them.
+    if (getppid() == parent) {
+      kill(parent, SIGKILL);
+    }
+    _exit(1);
+  } else {
+    close(sfd[0]);
+    signal_fd = sfd[1];
+    signal_pid = fork_pid;
+    return true;
+  }
+}
+
+bool stop_signal_sender() {
+  if (signal_pid <= 0) {
+    return false;
+  }
+
+  if (kill(signal_pid, SIGKILL) != 0) {
+    ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
+    return false;
+  }
+
+  close(signal_fd);
+  signal_fd = -1;
+
+  int status;
+  waitpid(signal_pid, &status, 0);
+  signal_pid = 0;
+
+  return true;
+}
+
+bool send_signal(pid_t pid, pid_t tid, int signal) {
+  if (signal_fd == -1) {
+    ALOGE("debuggerd: attempted to send signal before signal sender was started");
+    errno = EHOSTUNREACH;
+    return false;
+  }
+
+  signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
+  if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
+    ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
+    errno = EHOSTUNREACH;
+    return false;
+  }
+
+  int response;
+  ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
+  if (rc == 0) {
+    ALOGE("debuggerd: received EOF from signal sender");
+    errno = EHOSTUNREACH;
+    return false;
+  } else if (rc < 0) {
+    ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
+    errno = EHOSTUNREACH;
+    return false;
+  }
+
+  if (response == 0) {
+    return true;
+  }
+
+  errno = response;
+  return false;
+}
diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h
new file mode 100644
index 0000000..0443272
--- /dev/null
+++ b/debuggerd/signal_sender.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_SIGNAL_SENDER_H
+#define _DEBUGGERD_SIGNAL_SENDER_H
+
+#include <sys/types.h>
+
+bool start_signal_sender();
+bool stop_signal_sender();
+
+// Sends a signal to a target process or thread.
+// If tid is greater than zero, this performs tgkill(pid, tid, signal).
+// Otherwise, it performs kill(pid, signal).
+bool send_signal(pid_t pid, pid_t tid, int signal);
+
+#endif
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index c47a585..02aff55 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -600,7 +600,7 @@
                   fstab->recs[top_idx].fs_type);
             if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
                 strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
-                int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644);
+                int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
                 if (fd >= 0) {
                     INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
                     wipe_block_device(fd, get_file_size(fd));
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index 853bf0b..c63ff67 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -36,7 +36,7 @@
     uint64_t dev_sz;
     int fd, rc = 0;
 
-    if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
+    if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
         ERROR("Cannot open block device.  %s\n", strerror(errno));
         return -1;
     }
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index b4fdab0..7254cf2 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -76,7 +76,7 @@
 
     void store_sid(uint32_t uid, uint64_t sid) {
         char filename[21];
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
         if (fd < 0) {
             ALOGE("could not open file: %s: %s", filename, strerror(errno));
@@ -102,7 +102,7 @@
 
     void maybe_store_sid(uint32_t uid, uint64_t sid) {
         char filename[21];
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         if (access(filename, F_OK) == -1) {
             store_sid(uid, sid);
         }
@@ -111,7 +111,7 @@
     uint64_t read_sid(uint32_t uid) {
         char filename[21];
         uint64_t sid;
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         int fd = open(filename, O_RDONLY);
         if (fd < 0) return 0;
         read(fd, &sid, sizeof(sid));
@@ -121,7 +121,7 @@
 
     void clear_sid(uint32_t uid) {
         char filename[21];
-        sprintf(filename, "%u", uid);
+        snprintf(filename, sizeof(filename), "%u", uid);
         if (remove(filename) < 0) {
             ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
             store_sid(uid, 0);
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b33dd28..69647de 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -613,19 +613,15 @@
         }
     }
 
-    // This indicates that there is no charger driver registered.
     // Typically the case for devices which do not have a battery and
     // and are always plugged into AC mains.
-    if (!mChargerNames.size()) {
-        KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
-        mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
-        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
-        mAlwaysPluggedDevice = true;
-    }
     if (!mBatteryDevicePresent) {
         KLOG_WARNING(LOG_TAG, "No battery devices found\n");
         hc->periodic_chores_interval_fast = -1;
         hc->periodic_chores_interval_slow = -1;
+        mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
+        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+        mAlwaysPluggedDevice = true;
     } else {
         if (mHealthdConfig->batteryStatusPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index f440bd2..c896ab8 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -34,6 +34,24 @@
 typedef uint32_t word_t;
 #endif
 
+enum BacktraceUnwindError : uint32_t {
+  BACKTRACE_UNWIND_NO_ERROR,
+  // Something failed while trying to perform the setup to begin the unwind.
+  BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
+  // There is no map information to use with the unwind.
+  BACKTRACE_UNWIND_ERROR_MAP_MISSING,
+  // An error occurred that indicates a programming error.
+  BACKTRACE_UNWIND_ERROR_INTERNAL,
+  // The thread to unwind has disappeared before the unwind can begin.
+  BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST,
+  // The thread to unwind has not responded to a signal in a timely manner.
+  BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT,
+  // Attempt to do an unsupported operation.
+  BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
+  // Attempt to do an offline unwind without a context.
+  BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+};
+
 struct backtrace_frame_data_t {
   size_t num;             // The current fame number.
   uintptr_t pc;           // The absolute pc.
@@ -127,6 +145,10 @@
 
   BacktraceMap* GetMap() { return map_; }
 
+  BacktraceUnwindError GetError() { return error_; }
+
+  std::string GetErrorString(BacktraceUnwindError error);
+
 protected:
   Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
 
@@ -145,6 +167,8 @@
   bool map_shared_;
 
   std::vector<backtrace_frame_data_t> frames_;
+
+  BacktraceUnwindError error_;
 };
 
 #endif // _BACKTRACE_BACKTRACE_H
diff --git a/init/init.cpp b/init/init.cpp
index 9e6143b..704597d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -74,8 +74,7 @@
 
 static char qemu[32];
 
-int have_console;
-std::string console_name = "/dev/console";
+std::vector<std::string> console_names;
 static time_t process_needs_restart;
 
 const char *ENV[32];
@@ -298,38 +297,23 @@
 
 static int console_init_action(const std::vector<std::string>& args)
 {
-    std::string console = property_get("ro.boot.console");
-    if (!console.empty()) {
-        console_name = "/dev/" + console;
+    std::vector<std::string> consoles;
+    std::string c_prop = property_get("ro.boot.console");
+    if (c_prop.empty()) {
+        // Property is missing, so check the system console by default.
+        consoles.emplace_back(DEFAULT_CONSOLE);
+    } else {
+        consoles = android::base::Split(c_prop, ":");
     }
 
-    int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
-    if (fd >= 0)
-        have_console = 1;
-    close(fd);
-
-    fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        const char *msg;
-            msg = "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"  // console is 40 cols x 30 lines
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "\n"
-        "             A N D R O I D ";
-        write(fd, msg, strlen(msg));
-        close(fd);
+    for (const auto& c : consoles) {
+        std::string console = "/dev/" + c;
+        int fd = open(console.c_str(), O_RDWR | O_CLOEXEC);
+        if (fd != -1) {
+            console_names.emplace_back(c);
+            close(fd);
+        }
     }
-
     return 0;
 }
 
@@ -460,24 +444,6 @@
     return true;
 }
 
-int selinux_reload_policy(void)
-{
-    INFO("SELinux: Attempting to reload policy files\n");
-
-    if (selinux_android_reload_policy() == -1) {
-        return -1;
-    }
-
-    if (sehandle)
-        selabel_close(sehandle);
-
-    if (sehandle_prop)
-        selabel_close(sehandle_prop);
-
-    selinux_init_all_handles();
-    return 0;
-}
-
 static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
 
     property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
diff --git a/init/init.h b/init/init.h
index 345d442..c844489 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,16 +18,17 @@
 #define _INIT_INIT_H
 
 #include <string>
+#include <vector>
 
 class Action;
 class Service;
 
 #define COMMAND_RETRY_TIMEOUT 5
+#define DEFAULT_CONSOLE "console"
 
 extern const char *ENV[32];
 extern bool waiting_for_exec;
-extern int have_console;
-extern std::string console_name;
+extern std::vector<std::string> console_names;
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 
@@ -35,8 +36,6 @@
 
 void property_changed(const char *name, const char *value);
 
-int selinux_reload_policy(void);
-
 void register_epoll_handler(int fd, void (*fn)());
 
 int add_environment(const char* key, const char* val);
diff --git a/init/parser.cpp b/init/parser.cpp
index 8193729..ae103ec 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -12,7 +12,7 @@
     char buf[128];
     int off;
 
-    snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+    snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
     buf[127] = 0;
     off = strlen(buf);
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5c1ae79..8c95f15 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -175,11 +175,7 @@
     if (!is_legal_property_name(name, namelen)) return -1;
     if (valuelen >= PROP_VALUE_MAX) return -1;
 
-    if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
-        if (selinux_reload_policy() != 0) {
-            ERROR("Failed to reload policy\n");
-        }
-    } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
+    if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
         if (restorecon_recursive(value) != 0) {
             ERROR("Failed to restorecon_recursive %s\n", value);
         }
diff --git a/init/readme.txt b/init/readme.txt
index ef85ccf..7d453c6 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -115,6 +115,11 @@
 Options are modifiers to services.  They affect how and when init
 runs the service.
 
+console [<console>]
+  This service needs a console. The optional second parameter chooses a
+  specific console instead of the default "/dev/console". The leading "/dev/"
+  should be omitted, so "/dev/tty0" would be specified as just "console tty0".
+
 critical
   This is a device-critical service. If it exits more than four times in
   four minutes, the device will reboot into recovery mode.
diff --git a/init/service.cpp b/init/service.cpp
index bdecc32..e7bbf7b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -172,6 +172,7 @@
 
 bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
     flags_ |= SVC_CONSOLE;
+    console_ = args.size() > 1 ? args[1] : DEFAULT_CONSOLE;
     return true;
 }
 
@@ -282,7 +283,7 @@
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     static const Map option_handlers = {
         {"class",       {1,     1,    &Service::HandleClass}},
-        {"console",     {0,     0,    &Service::HandleConsole}},
+        {"console",     {0,     1,    &Service::HandleConsole}},
         {"critical",    {0,     0,    &Service::HandleCritical}},
         {"disabled",    {0,     0,    &Service::HandleDisabled}},
         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
@@ -329,7 +330,7 @@
     }
 
     bool needs_console = (flags_ & SVC_CONSOLE);
-    if (needs_console && !have_console) {
+    if (needs_console && console_names.empty()) {
         ERROR("service '%s' requires console\n", name_.c_str());
         flags_ |= SVC_DISABLED;
         return false;
@@ -606,10 +607,12 @@
 }
 
 void Service::OpenConsole() const {
-    int fd;
-    if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
-        fd = open("/dev/null", O_RDWR);
+    int fd = -1;
+    if (std::find(console_names.begin(), console_names.end(), console_) != console_names.end()) {
+        std::string c_path = "/dev/" + console_;
+        fd = open(c_path.c_str(), O_RDWR);
     }
+    if (fd == -1) fd = open("/dev/null", O_RDWR);
     ioctl(fd, TIOCSCTTY, 0);
     dup2(fd, 0);
     dup2(fd, 1);
diff --git a/init/service.h b/init/service.h
index 35abde9..b003ca0 100644
--- a/init/service.h
+++ b/init/service.h
@@ -129,6 +129,7 @@
 
     std::string name_;
     std::string classname_;
+    std::string console_;
 
     unsigned flags_;
     pid_t pid_;
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index baa3d0f..995abc0 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -148,3 +148,24 @@
     return new UnwindPtrace(pid, tid, map);
   }
 }
+
+std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
+  switch (error) {
+  case BACKTRACE_UNWIND_NO_ERROR:
+    return "No error";
+  case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+    return "Setup failed";
+  case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+    return "No map found";
+  case BACKTRACE_UNWIND_ERROR_INTERNAL:
+    return "Internal libbacktrace error, please submit a bugreport";
+  case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+    return "Thread doesn't exist";
+  case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+    return "Thread has not repsonded to signal in time";
+  case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+    return "Attempt to use an unsupported feature";
+  case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+    return "Attempt to do an offline unwind without a context";
+  }
+}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 8e22366..5173e2c 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -24,6 +24,8 @@
 #include <ucontext.h>
 #include <unistd.h>
 
+#include <stdlib.h>
+
 #include <string>
 
 #include <backtrace/Backtrace.h>
@@ -65,9 +67,11 @@
 bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
+    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
+  error_ = BACKTRACE_UNWIND_NO_ERROR;
   if (ucontext) {
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
@@ -138,11 +142,19 @@
     BACK_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
+    error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
     return false;
   }
 
   if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
-    BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno));
+    // Do not emit an error message, this might be expected. Set the
+    // error and let the caller decide.
+    if (errno == ESRCH) {
+      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+    } else {
+      error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+    }
+
     sigaction(THREAD_SIGNAL, &oldact, nullptr);
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
@@ -183,7 +195,13 @@
       BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
     }
   } else {
-    BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+    // Check to see if the thread has disappeared.
+    if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
+      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+    } else {
+      error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+      BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+    }
   }
 
   ThreadEntry::Remove(entry);
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index c10634b..dc3ed67 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -129,9 +129,11 @@
 bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
   if (context == nullptr) {
     BACK_LOGW("The context is needed for offline backtracing.");
+    error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
     return false;
   }
   context_ = context;
+  error_ = BACKTRACE_UNWIND_NO_ERROR;
 
   unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
   unw_cursor_t cursor;
@@ -139,6 +141,7 @@
   if (ret != 0) {
     BACK_LOGW("unw_init_remote failed %d", ret);
     unw_destroy_addr_space(addr_space);
+    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
   size_t num_frames = 0;
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 67e583f..666c481 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -70,6 +70,7 @@
     int ret = unw_getcontext(&context_);
     if (ret < 0) {
       BACK_LOGW("unw_getcontext failed %d", ret);
+      error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
       return false;
     }
   } else {
@@ -81,6 +82,7 @@
   int ret = unw_init_local(cursor.get(), &context_);
   if (ret < 0) {
     BACK_LOGW("unw_init_local failed %d", ret);
+    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 07c2430..306d2ac 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -50,17 +50,22 @@
 bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
+    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
+  error_ = BACKTRACE_UNWIND_NO_ERROR;
+
   if (ucontext) {
     BACK_LOGW("Unwinding from a specified context not supported yet.");
+    error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
     return false;
   }
 
   addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
   if (!addr_space_) {
     BACK_LOGW("unw_create_addr_space failed.");
+    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
@@ -70,6 +75,7 @@
   upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
   if (!upt_info_) {
     BACK_LOGW("Failed to create upt info.");
+    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
@@ -77,6 +83,7 @@
   int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
   if (ret < 0) {
     BACK_LOGW("unw_init_remote failed %d", ret);
+    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index b975db9..df6c6c1 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -162,6 +162,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyLevelDump(backtrace.get());
 }
@@ -183,6 +184,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyMaxDump(backtrace.get());
 }
@@ -200,6 +202,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyFunc(backtrace.get());
 }
@@ -220,6 +223,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   ASSERT_TRUE(backtrace->NumFrames() != 0);
   for (const auto& frame : *backtrace ) {
@@ -267,16 +271,19 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
 
   std::unique_ptr<Backtrace> ign1(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
 
   std::unique_ptr<Backtrace> ign2(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
@@ -314,6 +321,7 @@
       std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
       ASSERT_TRUE(backtrace.get() != nullptr);
       ASSERT_TRUE(backtrace->Unwind(0));
+      ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
       if (ReadyFunc(backtrace.get())) {
         VerifyFunc(backtrace.get());
         verified = true;
@@ -372,10 +380,12 @@
   std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
 
   std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
 
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
@@ -462,6 +472,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyLevelDump(backtrace.get());
 }
@@ -474,6 +485,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyMaxDump(backtrace.get());
 }
@@ -515,6 +527,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyLevelDump(backtrace.get());
 
@@ -554,14 +567,17 @@
   std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
 
   std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
 
   std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
 
@@ -592,6 +608,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   VerifyMaxDump(backtrace.get());
 
@@ -718,18 +735,21 @@
   Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
   ASSERT_TRUE(back1 != nullptr);
   EXPECT_TRUE(back1->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError());
   delete back1;
   delete map1;
 
   Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
   ASSERT_TRUE(back2 != nullptr);
   EXPECT_TRUE(back2->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError());
   delete back2;
   delete map2;
 
   Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
   ASSERT_TRUE(back3 != nullptr);
   EXPECT_TRUE(back3->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError());
   delete back3;
   delete map3;
 }
@@ -1308,6 +1328,7 @@
                                                          BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
   size_t frame_num;
   ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
@@ -1364,6 +1385,7 @@
     std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
     ASSERT_TRUE(backtrace.get() != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
 
     size_t frame_num;
     if (FindFuncFrameInBacktrace(backtrace.get(),
@@ -1387,6 +1409,14 @@
   ASSERT_TRUE(done) << "Test function never found in unwind.";
 }
 
+TEST(libbacktrace, unwind_thread_doesnt_exist) {
+  std::unique_ptr<Backtrace> backtrace(
+      Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
+  ASSERT_TRUE(backtrace.get() != nullptr);
+  ASSERT_FALSE(backtrace->Unwind(0));
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
+}
+
 #if defined(ENABLE_PSS_TESTS)
 #include "GetPss.h"
 
@@ -1398,6 +1428,7 @@
     Backtrace* backtrace = Backtrace::Create(pid, tid);
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
     delete backtrace;
   }
   size_t stable_pss = GetPssBytes();
@@ -1408,13 +1439,14 @@
     Backtrace* backtrace = Backtrace::Create(pid, tid);
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
     delete backtrace;
   }
   size_t new_pss = GetPssBytes();
   ASSERT_TRUE(new_pss != 0);
-  size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
-  // As long as the new pss is within a certain amount, consider everything okay.
-  ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+  if (new_pss > stable_pss) {
+    ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+  }
 }
 
 TEST(libbacktrace, check_for_leak_local) {
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 51c6d9d..c0d4d76 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -121,6 +121,9 @@
 ifneq ($(ENABLE_CPUSETS),)
 LOCAL_CFLAGS += -DUSE_CPUSETS
 endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
 LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
 LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
@@ -135,6 +138,9 @@
 ifneq ($(ENABLE_CPUSETS),)
 LOCAL_CFLAGS += -DUSE_CPUSETS
 endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
 LOCAL_CFLAGS += -Werror -Wall -Wextra
 LOCAL_C_INCLUDES := $(libcutils_c_includes)
 LOCAL_CLANG := true
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 298e3da..6bba3a7 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -64,6 +64,8 @@
 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
 static int bg_cpuset_fd = -1;
 static int fg_cpuset_fd = -1;
+static int bg_schedboost_fd = -1;
+static int fg_schedboost_fd = -1;
 #endif
 
 /* Add tid to the scheduling group defined by the policy */
@@ -128,6 +130,12 @@
         fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
         filename = "/dev/cpuset/background/tasks";
         bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#ifdef USE_SCHEDBOOST
+        filename = "/sys/fs/cgroup/stune/foreground/tasks";
+        fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+        filename = "/sys/fs/cgroup/stune/tasks";
+        bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#endif
     }
 #endif
 
@@ -253,17 +261,20 @@
     pthread_once(&the_once, __initialize);
 
     int fd;
+    int boost_fd;
     switch (policy) {
     case SP_BACKGROUND:
         fd = bg_cpuset_fd;
+        boost_fd = bg_schedboost_fd;
         break;
     case SP_FOREGROUND:
     case SP_AUDIO_APP:
     case SP_AUDIO_SYS:
         fd = fg_cpuset_fd;
+        boost_fd = fg_schedboost_fd;
         break;
     default:
-        fd = -1;
+        boost_fd = fd = -1;
         break;
     }
 
@@ -272,6 +283,11 @@
             return -errno;
     }
 
+    if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+        if (errno != ESRCH && errno != ENOENT)
+            return -errno;
+    }
+
     return 0;
 #endif
 }
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
index 894f90e..d4d07c2 100644
--- a/libion/tests/Android.mk
+++ b/libion/tests/Android.mk
@@ -17,8 +17,9 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
+LOCAL_CLANG := true
 LOCAL_MODULE := ion-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
 LOCAL_SHARED_LIBRARIES += libion
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
 LOCAL_SRC_FILES := \
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index e26b302..3c4524e 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <memory>
 #include <sys/mman.h>
 
 #include <gtest/gtest.h>
@@ -90,7 +91,7 @@
 
 TEST_F(Allocate, Zeroed)
 {
-    void *zeroes = calloc(4096, 1);
+    auto zeroes_ptr = std::make_unique<char[]>(4096);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -125,14 +126,11 @@
         ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
-        ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+        ASSERT_EQ(0, memcmp(ptr, zeroes_ptr.get(), 4096));
 
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(zeroes);
-
 }
 
 TEST_F(Allocate, Large)
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
index 6f6e1bd..0be52bf 100644
--- a/libion/tests/device_test.cpp
+++ b/libion/tests/device_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <fcntl.h>
+#include <memory>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -133,8 +134,8 @@
 
 TEST_F(Device, KernelReadCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -161,14 +162,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelWriteCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -195,14 +194,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAReadCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -227,14 +224,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAWriteCached)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -261,14 +256,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelReadCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -295,14 +288,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelWriteCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -329,14 +320,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAReadCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -363,14 +352,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAWriteCachedNeedsSync)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -399,13 +386,11 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 TEST_F(Device, KernelRead)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -432,14 +417,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, KernelWrite)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -466,14 +449,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMARead)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -498,14 +479,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, DMAWrite)
 {
-    void *alloc = malloc(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
 
     for (int i = 0; i < 4096; i++)
         ((char *)buf)[i] = i;
@@ -532,13 +511,12 @@
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
     }
-
-    free(alloc);
 }
 
 TEST_F(Device, IsCached)
 {
-    void *buf = malloc(4096);
+    auto buf_ptr = std::make_unique<char[]>(4096);
+    void *buf = buf_ptr.get();
 
     for (unsigned int heapMask : m_allHeaps) {
         SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
diff --git a/liblog/Android.mk b/liblog/Android.mk
index dd5b518..6d53a4a 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -39,7 +39,7 @@
 LOCAL_SRC_FILES_darwin := event_tag_map.c
 LOCAL_SRC_FILES_linux := event_tag_map.c
 LOCAL_SRC_FILES_windows := uio.c
-LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
+LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror -fvisibility=hidden $(liblog_cflags)
 LOCAL_MULTILIB := both
 LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
@@ -59,7 +59,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_target_sources)
-LOCAL_CFLAGS := -Werror $(liblog_cflags)
+LOCAL_CFLAGS := -Werror -fvisibility=hidden $(liblog_cflags)
 # AddressSanitizer runtime library depends on liblog.
 LOCAL_SANITIZE := never
 include $(BUILD_STATIC_LIBRARY)
@@ -67,7 +67,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror $(liblog_cflags)
+LOCAL_CFLAGS := -Werror -fvisibility=hidden $(liblog_cflags)
 
 # TODO: This is to work around b/24465209. Remove after root cause is fixed
 LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index bea99aa..870c69a 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -24,6 +24,8 @@
 #include <log/event_tag_map.h>
 #include <log/log.h>
 
+#include "log_cdefs.h"
+
 #define OUT_TAG "EventTagMap"
 
 /*
@@ -61,7 +63,7 @@
  * We create a private mapping because we want to terminate the log tag
  * strings with '\0'.
  */
-EventTagMap* android_openEventTagMap(const char* fileName)
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
 {
     EventTagMap* newTagMap;
     off_t end;
@@ -109,7 +111,7 @@
 /*
  * Close the map.
  */
-void android_closeEventTagMap(EventTagMap* map)
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
 {
     if (map == NULL)
         return;
@@ -123,7 +125,8 @@
  *
  * The entries are sorted by tag number, so we can do a binary search.
  */
-const char* android_lookupEventTag(const EventTagMap* map, int tag)
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+                                                     int tag)
 {
     int hi, lo, mid;
 
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index aa88bed..c73e03e 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -19,23 +19,19 @@
  * passed on to the underlying (fake) log device.  When not in the
  * simulator, messages are printed to stderr.
  */
-#include "fake_log_device.h"
-
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
 
 #include <log/logd.h>
 
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
+#include "fake_log_device.h"
+#include "log_cdefs.h"
 
 #define kMaxTagLen  16      /* from the long-dead utils/Log.cpp */
 
@@ -512,7 +508,7 @@
         }
         numLines -= 1;
     }
-    
+
     /*
      * Write the entire message to the log file with a single writev() call.
      * We need to use this rather than a collection of printf()s on a FILE*
@@ -531,10 +527,10 @@
         int cc = writev(fileno(stderr), vec, v-vec);
 
         if (cc == totalLen) break;
-        
+
         if (cc < 0) {
             if(errno == EINTR) continue;
-            
+
                 /* can't really log the failure; for now, throw out a stderr */
             fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
             break;
@@ -683,7 +679,7 @@
     }
 }
 
-int fakeLogOpen(const char *pathName, int flags)
+LIBLOG_HIDDEN int fakeLogOpen(const char *pathName, int flags)
 {
     if (redirectOpen == NULL) {
         setRedirects();
@@ -702,19 +698,22 @@
  * call is in the exit handler. Logging can continue in the exit handler to
  * help debug HOST tools ...
  */
-int fakeLogClose(int fd)
+LIBLOG_HIDDEN int fakeLogClose(int fd)
 {
     /* Assume that open() was called first. */
     return redirectClose(fd);
 }
 
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd,
+                                    const struct iovec* vector, int count)
 {
     /* Assume that open() was called first. */
     return redirectWritev(fd, vector, count);
 }
 
-int __android_log_is_loggable(int prio, const char *tag __unused, int def)
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
+                                                const char *tag __unused,
+                                                int def)
 {
     int logLevel = def;
     return logLevel >= 0 && prio >= logLevel;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 9d168cd..672b446 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -19,10 +19,13 @@
 
 #include <sys/types.h>
 
+#include "log_cdefs.h"
+
 struct iovec;
 
-int fakeLogOpen(const char *pathName, int flags);
-int fakeLogClose(int fd);
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+LIBLOG_HIDDEN int fakeLogOpen(const char *pathName, int flags);
+LIBLOG_HIDDEN int fakeLogClose(int fd);
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd,
+                                    const struct iovec* vector, int count);
 
 #endif // _LIBLOG_FAKE_LOG_DEVICE_H
diff --git a/liblog/log_cdefs.h b/liblog/log_cdefs.h
new file mode 100644
index 0000000..3a52625
--- /dev/null
+++ b/liblog/log_cdefs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_CDEFS_H__
+#define _LIBLOG_CDEFS_H__
+
+#include <sys/cdefs.h>
+
+/* Declare this library function hidden and internal */
+#if defined(_WIN32)
+#define LIBLOG_HIDDEN
+#else
+#define LIBLOG_HIDDEN __attribute__((visibility("hidden")))
+#endif
+
+/* Declare this library function visible and external */
+#if defined(_WIN32)
+#define LIBLOG_ABI_PUBLIC
+#else
+#define LIBLOG_ABI_PUBLIC __attribute__((visibility("default")))
+#endif
+
+/* Declare this library function visible but private */
+#define LIBLOG_ABI_PRIVATE LIBLOG_ABI_PUBLIC
+
+/*
+ * Declare this library function as reimplementation.
+ * Prevent circular dependencies, but allow _real_ library to hijack
+ */
+#if defined(_WIN32)
+#define LIBLOG_WEAK static /* Accept that it is totally private */
+#else
+#define LIBLOG_WEAK __attribute__((weak,visibility("default")))
+#endif
+
+/* Unused argument. For C code only, remove symbol name for C++ */
+#ifndef __unused
+#define __unused        __attribute__((__unused__))
+#endif
+
+#endif /* _LIBLOG_CDEFS_H__ */
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index 2213f21..a77c56e 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -25,6 +25,8 @@
 #include <log/log.h>
 #include <log/logger.h>
 
+#include "log_cdefs.h"
+
 #define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
 
 typedef struct {
@@ -43,7 +45,7 @@
     uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
 } android_log_context_internal;
 
-android_log_context create_android_logger(uint32_t tag) {
+LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
     size_t needed, i;
     android_log_context_internal *context;
 
@@ -65,7 +67,9 @@
     return (android_log_context)context;
 }
 
-android_log_context create_android_log_parser(const char *msg, size_t len) {
+LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(
+        const char *msg,
+        size_t len) {
     android_log_context_internal *context;
     size_t i;
 
@@ -81,7 +85,7 @@
     return (android_log_context)context;
 }
 
-int android_log_destroy(android_log_context *ctx) {
+LIBLOG_ABI_PUBLIC int android_log_destroy(android_log_context *ctx) {
     android_log_context_internal *context;
 
     context = (android_log_context_internal *)*ctx;
@@ -94,7 +98,7 @@
     return 0;
 }
 
-int android_log_write_list_begin(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
     size_t needed;
     android_log_context_internal *context;
 
@@ -137,7 +141,8 @@
     buf[3] = (val >> 24) & 0xFF;
 }
 
-int android_log_write_int32(android_log_context ctx, int32_t value) {
+LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx,
+                                              int32_t value) {
     size_t needed;
     android_log_context_internal *context;
 
@@ -172,7 +177,8 @@
     buf[7] = (val >> 56) & 0xFF;
 }
 
-int android_log_write_int64(android_log_context ctx, int64_t value) {
+LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx,
+                                              int64_t value) {
     size_t needed;
     android_log_context_internal *context;
 
@@ -195,8 +201,9 @@
     return 0;
 }
 
-int android_log_write_string8_len(android_log_context ctx,
-                                  const char *value, size_t maxlen) {
+LIBLOG_ABI_PUBLIC 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;
@@ -231,11 +238,13 @@
     return len;
 }
 
-int android_log_write_string8(android_log_context ctx, const char *value) {
+LIBLOG_ABI_PUBLIC 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) {
+LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx,
+                                                float value) {
     size_t needed;
     uint32_t ivalue;
     android_log_context_internal *context;
@@ -260,7 +269,7 @@
     return 0;
 }
 
-int android_log_write_list_end(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC int android_log_write_list_end(android_log_context ctx) {
     android_log_context_internal *context;
 
     context = (android_log_context_internal *)ctx;
@@ -290,7 +299,8 @@
 /*
  * Logs the list of elements to the event log.
  */
-int android_log_write_list(android_log_context ctx, log_id_t id) {
+LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx,
+                                             log_id_t id) {
     android_log_context_internal *context;
     const char *msg;
     ssize_t len;
@@ -518,10 +528,12 @@
     }
 }
 
-android_log_list_element android_log_read_next(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC 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) {
+LIBLOG_ABI_PUBLIC 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 ad42edd..3535b94 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.c
@@ -18,10 +18,15 @@
 
 #include <log/log.h>
 
+#include "log_cdefs.h"
+
 #define MAX_SUBTAG_LEN 32
 
-int __android_log_error_write(int tag, const char *subTag, int32_t uid,
-                              const char *data, uint32_t dataLen)
+LIBLOG_ABI_PUBLIC int __android_log_error_write(
+        int tag,
+        const char *subTag,
+        int32_t uid,
+        const char *data, uint32_t dataLen)
 {
     int ret = -EINVAL;
 
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 2f8f886..47fde20 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,6 +23,8 @@
 
 #include <android/log.h>
 
+#include "log_cdefs.h"
+
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
 
 static int lock()
@@ -250,7 +252,8 @@
     return default_prio;
 }
 
-int __android_log_is_loggable(int prio, const char *tag, int default_prio)
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char *tag,
+                                                int default_prio)
 {
     int logLevel = __android_log_level(tag, default_prio);
     return logLevel >= 0 && prio >= logLevel;
@@ -315,7 +318,7 @@
  * Timestamp state generally remains constant, but can change at any time
  * to handle developer requirements.
  */
-clockid_t android_log_clockid()
+LIBLOG_ABI_PUBLIC clockid_t android_log_clockid()
 {
     static struct cache2 clockid = {
         PTHREAD_MUTEX_INITIALIZER,
@@ -343,7 +346,7 @@
     return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
 }
 
-int __android_log_security()
+LIBLOG_ABI_PUBLIC int __android_log_security()
 {
     static struct cache2 security = {
         PTHREAD_MUTEX_INITIALIZER,
diff --git a/liblog/log_read.c b/liblog/log_read.c
index fc63d2c..4b83944 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -24,7 +24,6 @@
 #define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
 #include <stdlib.h>
 #include <string.h>
-#include <sys/cdefs.h>
 #include <unistd.h>
 
 #include <cutils/list.h>
@@ -34,23 +33,16 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "log_cdefs.h"
+
 /* branchless on many architectures. */
 #define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
-#if defined(_WIN32)
-#define WEAK static
-#else
-#define WEAK __attribute__((weak))
-#endif
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
 /* Private copy of ../libcutils/socket_local_client.c prevent library loops */
 
 #if defined(_WIN32)
 
-int WEAK socket_local_client(const char *name, int namespaceId, int type)
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
 {
     errno = ENOSYS;
     return -ENOSYS;
@@ -71,8 +63,9 @@
 #define LISTEN_BACKLOG 4
 
 /* Documented in header file. */
-int WEAK socket_make_sockaddr_un(const char *name, int namespaceId,
-                                 struct sockaddr_un *p_addr, socklen_t *alen)
+LIBLOG_WEAK int socket_make_sockaddr_un(const char *name, int namespaceId,
+                                        struct sockaddr_un *p_addr,
+                                        socklen_t *alen)
 {
     memset (p_addr, 0, sizeof (*p_addr));
     size_t namelen;
@@ -151,8 +144,8 @@
  *
  * Used by AndroidSocketImpl
  */
-int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId,
-                                     int type __unused)
+LIBLOG_WEAK int socket_local_client_connect(int fd, const char *name,
+                                            int namespaceId, int type __unused)
 {
     struct sockaddr_un addr;
     socklen_t alen;
@@ -178,7 +171,7 @@
  * connect to peer named "name"
  * returns fd or -1 on error
  */
-int WEAK socket_local_client(const char *name, int namespaceId, int type)
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
 {
     int s;
 
@@ -212,7 +205,7 @@
     [LOG_ID_KERNEL] = "kernel",
 };
 
-const char *android_log_id_to_name(log_id_t log_id)
+LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
 {
     if (log_id >= LOG_ID_MAX) {
         log_id = LOG_ID_MAIN;
@@ -220,7 +213,7 @@
     return LOG_NAME[log_id];
 }
 
-log_id_t android_name_to_log_id(const char *logName)
+LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName)
 {
     const char *b;
     int ret;
@@ -275,7 +268,7 @@
 /* android_logger_alloc unimplemented, no use case */
 
 /* method for getting the associated sublog id */
-log_id_t android_logger_get_id(struct logger *logger)
+LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger)
 {
     return logger->id;
 }
@@ -409,7 +402,7 @@
     return last_uid = uid;
 }
 
-int android_logger_clear(struct logger *logger)
+LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger)
 {
     char buf[512];
 
@@ -425,7 +418,7 @@
 }
 
 /* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger *logger)
+LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger *logger)
 {
     char buf[512];
 
@@ -441,7 +434,8 @@
     return atol(buf);
 }
 
-int android_logger_set_log_size(struct logger *logger, unsigned long size)
+LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger,
+                                                  unsigned long size)
 {
     char buf[512];
 
@@ -455,7 +449,8 @@
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
  */
-long android_logger_get_log_readable_size(struct logger *logger)
+LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
+        struct logger *logger)
 {
     char buf[512];
 
@@ -474,16 +469,18 @@
 /*
  * returns the logger version
  */
-int android_logger_get_log_version(struct logger *logger __unused)
+LIBLOG_ABI_PUBLIC int android_logger_get_log_version(
+        struct logger *logger __unused)
 {
-    return 3;
+    return 4;
 }
 
 /*
  * returns statistics
  */
-ssize_t android_logger_get_statistics(struct logger_list *logger_list,
-                                      char *buf, size_t len)
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
+        struct logger_list *logger_list,
+        char *buf, size_t len)
 {
     struct logger *logger;
     char *cp = buf;
@@ -509,14 +506,16 @@
     return send_log_msg(NULL, NULL, buf, len);
 }
 
-ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
-                                      char *buf, size_t len)
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
+        struct logger_list *logger_list __unused,
+        char *buf, size_t len)
 {
     return send_log_msg(NULL, "getPruneList", buf, len);
 }
 
-int android_logger_set_prune_list(struct logger_list *logger_list __unused,
-                                  char *buf, size_t len)
+LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
+        struct logger_list *logger_list __unused,
+        char *buf, size_t len)
 {
     const char cmd[] = "setPruneList ";
     const size_t cmdlen = sizeof(cmd) - 1;
@@ -531,9 +530,10 @@
     return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
 }
 
-struct logger_list *android_logger_list_alloc(int mode,
-                                              unsigned int tail,
-                                              pid_t pid)
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
+        int mode,
+        unsigned int tail,
+        pid_t pid)
 {
     struct logger_list *logger_list;
 
@@ -553,9 +553,10 @@
     return logger_list;
 }
 
-struct logger_list *android_logger_list_alloc_time(int mode,
-                                                   log_time start,
-                                                   pid_t pid)
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc_time(
+        int mode,
+        log_time start,
+        pid_t pid)
 {
     struct logger_list *logger_list;
 
@@ -578,8 +579,9 @@
 /* android_logger_list_unregister unimplemented, no use case */
 
 /* Open the named log and add it to the logger list */
-struct logger *android_logger_open(struct logger_list *logger_list,
-                                   log_id_t id)
+LIBLOG_ABI_PUBLIC struct logger *android_logger_open(
+        struct logger_list *logger_list,
+        log_id_t id)
 {
     struct logger *logger;
 
@@ -610,10 +612,11 @@
 }
 
 /* Open the single named log and make it part of a new logger list */
-struct logger_list *android_logger_list_open(log_id_t id,
-                                             int mode,
-                                             unsigned int tail,
-                                             pid_t pid)
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open(
+        log_id_t id,
+        int mode,
+        unsigned int tail,
+        pid_t pid)
 {
     struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
     if (!logger_list) {
@@ -751,8 +754,9 @@
 }
 
 /* Read from the selected logs */
-int android_logger_list_read(struct logger_list *logger_list,
-                             struct log_msg *log_msg)
+LIBLOG_ABI_PUBLIC int android_logger_list_read(
+        struct logger_list *logger_list,
+        struct log_msg *log_msg)
 {
     int ret, e;
     struct logger *logger;
@@ -892,7 +896,8 @@
 }
 
 /* Close all the logs */
-void android_logger_list_free(struct logger_list *logger_list)
+LIBLOG_ABI_PUBLIC void android_logger_list_free(
+        struct logger_list *logger_list)
 {
     if (logger_list == NULL) {
         return;
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 9d5ea0e..b6af222 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -18,16 +18,17 @@
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/cdefs.h>
 
 #include <log/log_read.h>
 
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-const timespec log_time::EPOCH = { 0, 0 };
+#include "log_cdefs.h"
+
+LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 };
 
 // Add %#q for fractional seconds to standard strptime function
 
-char *log_time::strptime(const char *s, const char *format) {
+LIBLOG_ABI_PRIVATE char *log_time::strptime(const char *s, const char *format) {
     time_t now;
 #ifdef __linux__
     *this = log_time(CLOCK_REALTIME);
@@ -133,7 +134,7 @@
     return ret;
 }
 
-log_time log_time::operator-= (const timespec &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator-= (const timespec &T) {
     // No concept of negative time, clamp to EPOCH
     if (*this <= T) {
         return *this = EPOCH;
@@ -150,7 +151,7 @@
     return *this;
 }
 
-log_time log_time::operator+= (const timespec &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator+= (const timespec &T) {
     this->tv_nsec += (unsigned long int)T.tv_nsec;
     if (this->tv_nsec >= NS_PER_SEC) {
         this->tv_nsec -= NS_PER_SEC;
@@ -161,7 +162,7 @@
     return *this;
 }
 
-log_time log_time::operator-= (const log_time &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator-= (const log_time &T) {
     // No concept of negative time, clamp to EPOCH
     if (*this <= T) {
         return *this = EPOCH;
@@ -178,7 +179,7 @@
     return *this;
 }
 
-log_time log_time::operator+= (const log_time &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator+= (const log_time &T) {
     this->tv_nsec += T.tv_nsec;
     if (this->tv_nsec >= NS_PER_SEC) {
         this->tv_nsec -= NS_PER_SEC;
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index b173d1a..85a4aab 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -46,6 +46,8 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "log_cdefs.h"
+
 #define LOG_BUF_SIZE 1024
 
 #if FAKE_LOG_DEVICE
@@ -56,10 +58,6 @@
 static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
 static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
 
-#ifndef __unused
-#define __unused  __attribute__((__unused__))
-#endif
-
 #if !defined(_WIN32)
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
 
@@ -106,7 +104,7 @@
     kLogUninitialized, kLogNotAvailable, kLogAvailable
 } g_log_status = kLogUninitialized;
 
-int __android_log_dev_available(void)
+LIBLOG_ABI_PUBLIC int __android_log_dev_available()
 {
     if (g_log_status == kLogUninitialized) {
         if (access("/dev/socket/logdw", W_OK) == 0)
@@ -485,7 +483,7 @@
     [LOG_ID_KERNEL] = "kernel",
 };
 
-const char *android_log_id_to_name(log_id_t log_id)
+LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
 {
     if (log_id >= LOG_ID_MAX) {
         log_id = LOG_ID_MAIN;
@@ -520,12 +518,14 @@
     return write_to_log(log_id, vec, nr);
 }
 
-int __android_log_write(int prio, const char *tag, const char *msg)
+LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char *tag,
+                                          const char *msg)
 {
     return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
 }
 
-int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
+LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
+                                              const char *tag, const char *msg)
 {
     struct iovec vec[3];
     char tmp_tag[32];
@@ -566,7 +566,8 @@
     return write_to_log(bufID, vec, 3);
 }
 
-int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char *tag,
+                                           const char *fmt, va_list ap)
 {
     char buf[LOG_BUF_SIZE];
 
@@ -575,7 +576,8 @@
     return __android_log_write(prio, tag, buf);
 }
 
-int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char *tag,
+                                          const char *fmt, ...)
 {
     va_list ap;
     char buf[LOG_BUF_SIZE];
@@ -587,7 +589,9 @@
     return __android_log_write(prio, tag, buf);
 }
 
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
+                                              const char *tag,
+                                              const char *fmt, ...)
 {
     va_list ap;
     char buf[LOG_BUF_SIZE];
@@ -599,8 +603,10 @@
     return __android_log_buf_write(bufID, prio, tag, buf);
 }
 
-void __android_log_assert(const char *cond, const char *tag,
-                          const char *fmt, ...)
+LIBLOG_ABI_PUBLIC void __android_log_assert(
+        const char *cond,
+        const char *tag,
+        const char *fmt, ...)
 {
     char buf[LOG_BUF_SIZE];
 
@@ -625,7 +631,8 @@
     /* NOTREACHED */
 }
 
-int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag,
+                                           const void *payload, size_t len)
 {
     struct iovec vec[2];
 
@@ -637,7 +644,9 @@
     return write_to_log(LOG_ID_EVENTS, vec, 2);
 }
 
-int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len)
+LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
+                                                    const void *payload,
+                                                    size_t len)
 {
     struct iovec vec[2];
 
@@ -654,8 +663,8 @@
  * for the general case where we're generating lists of stuff, but very
  * handy if we just want to dump an integer into the log.
  */
-int __android_log_btwrite(int32_t tag, char type, const void *payload,
-                          size_t len)
+LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
+                                            const void *payload, size_t len)
 {
     struct iovec vec[3];
 
@@ -673,7 +682,7 @@
  * Like __android_log_bwrite, but used for writing strings to the
  * event log.
  */
-int __android_log_bswrite(int32_t tag, const char *payload)
+LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char *payload)
 {
     struct iovec vec[4];
     char type = EVENT_TYPE_STRING;
@@ -695,7 +704,8 @@
  * Like __android_log_security_bwrite, but used for writing strings to the
  * security log.
  */
-int __android_log_security_bswrite(int32_t tag, const char *payload)
+LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
+                                                     const char *payload)
 {
     struct iovec vec[4];
     char type = EVENT_TYPE_STRING;
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 4ef62a1..02df8dd 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -34,12 +34,11 @@
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 
+#include "log_cdefs.h"
+
 #define MS_PER_NSEC 1000000
 #define US_PER_NSEC 1000
 
-/* open coded fragment, prevent circular dependencies */
-#define WEAK static
-
 typedef struct FilterInfo_t {
     char *mTag;
     android_LogPriority mPri;
@@ -185,13 +184,15 @@
  * returns 1 if this log line should be printed based on its priority
  * and tag, and 0 if it should not
  */
-int android_log_shouldPrintLine (
-        AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
+LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine (
+        AndroidLogFormat *p_format,
+        const char *tag,
+        android_LogPriority pri)
 {
     return pri >= filterPriForTag(p_format, tag);
 }
 
-AndroidLogFormat *android_log_format_new()
+LIBLOG_ABI_PUBLIC AndroidLogFormat *android_log_format_new()
 {
     AndroidLogFormat *p_ret;
 
@@ -213,7 +214,7 @@
 
 static list_declare(convertHead);
 
-void android_log_format_free(AndroidLogFormat *p_format)
+LIBLOG_ABI_PUBLIC void android_log_format_free(AndroidLogFormat *p_format)
 {
     FilterInfo *p_info, *p_info_old;
 
@@ -236,7 +237,8 @@
     }
 }
 
-int android_log_setPrintFormat(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_setPrintFormat(
+        AndroidLogFormat *p_format,
         AndroidLogPrintFormat format)
 {
     switch (format) {
@@ -277,7 +279,8 @@
 /**
  * Returns FORMAT_OFF on invalid string
  */
-AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
+LIBLOG_ABI_PUBLIC AndroidLogPrintFormat android_log_formatFromString(
+        const char * formatString)
 {
     static AndroidLogPrintFormat format;
 
@@ -341,7 +344,8 @@
  * Assumes single threaded execution
  */
 
-int android_log_addFilterRule(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_addFilterRule(
+        AndroidLogFormat *p_format,
         const char *filterExpression)
 {
     size_t tagNameLength;
@@ -419,7 +423,8 @@
  *
  */
 
-int android_log_addFilterString(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_addFilterString(
+        AndroidLogFormat *p_format,
         const char *filterString)
 {
     char *filterStringCopy = strdup (filterString);
@@ -453,8 +458,9 @@
  * Returns 0 on success and -1 on invalid wire format (entry will be
  * in unspecified state)
  */
-int android_log_processLogBuffer(struct logger_entry *buf,
-                                 AndroidLogEntry *entry)
+LIBLOG_ABI_PUBLIC int android_log_processLogBuffer(
+        struct logger_entry *buf,
+        AndroidLogEntry *entry)
 {
     entry->tv_sec = buf->sec;
     entry->tv_nsec = buf->nsec;
@@ -734,9 +740,11 @@
  * it however we choose, which means we can't really use a fixed-size buffer
  * here.
  */
-int android_log_processBinaryLogBuffer(struct logger_entry *buf,
-    AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
-    int messageBufLen)
+LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
+        struct logger_entry *buf,
+        AndroidLogEntry *entry,
+        const EventTagMap *map,
+        char *messageBuf, int messageBufLen)
 {
     size_t inCount;
     unsigned int tagIndex;
@@ -852,7 +860,7 @@
  * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
  * propagate globally.
  */
-WEAK ssize_t utf8_character_length(const char *src, size_t len)
+LIBLOG_WEAK ssize_t utf8_character_length(const char *src, size_t len)
 {
     const char *cur = src;
     const char first_char = *cur++;
@@ -956,7 +964,7 @@
     return p - begin;
 }
 
-char *readSeconds(char *e, struct timespec *t)
+static char *readSeconds(char *e, struct timespec *t)
 {
     unsigned long multiplier;
     char *p;
@@ -1243,12 +1251,12 @@
  * Returns NULL on malloc error
  */
 
-char *android_log_formatLogLine (
-    AndroidLogFormat *p_format,
-    char *defaultBuffer,
-    size_t defaultBufferSize,
-    const AndroidLogEntry *entry,
-    size_t *p_outLength)
+LIBLOG_ABI_PUBLIC char *android_log_formatLogLine (
+        AndroidLogFormat *p_format,
+        char *defaultBuffer,
+        size_t defaultBufferSize,
+        const AndroidLogEntry *entry,
+        size_t *p_outLength)
 {
 #if !defined(_WIN32)
     struct tm tmBuf;
@@ -1421,8 +1429,16 @@
      * possibly causing heap corruption.  To avoid this we double check and
      * set the length at the maximum (size minus null byte)
      */
-    prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
-    suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+    prefixLen += len;
+    if (prefixLen >= sizeof(prefixBuf)) {
+        prefixLen = sizeof(prefixBuf) - 1;
+        prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+    }
+    if (suffixLen >= sizeof(suffixBuf)) {
+        suffixLen = sizeof(suffixBuf) - 1;
+        suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+        suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+    }
 
     /* the following code is tragically unreadable */
 
@@ -1526,10 +1542,10 @@
  * Returns count bytes written
  */
 
-int android_log_printLogLine(
-    AndroidLogFormat *p_format,
-    int fd,
-    const AndroidLogEntry *entry)
+LIBLOG_ABI_PUBLIC int android_log_printLogLine(
+        AndroidLogFormat *p_format,
+        int fd,
+        const AndroidLogEntry *entry)
 {
     int ret;
     char defaultBuffer[512];
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 50ecd2d..2767f73 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <semaphore.h>
 #include <signal.h>
 #include <string.h>
 #include <sys/types.h>
@@ -531,9 +532,15 @@
 }
 
 static unsigned signaled;
-log_time signal_time;
+static log_time signal_time;
 
-static void caught_blocking(int /*signum*/)
+/*
+ *  Strictly, we are not allowed to log messages in a signal context, but we
+ * do make an effort to keep the failure surface minimized, and this in-effect
+ * should catch any regressions in that effort. The odds of a logged message
+ * in a signal handler causing a lockup problem should be _very_ small.
+ */
+static void caught_blocking_signal(int /*signum*/)
 {
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -583,7 +590,7 @@
     }
 }
 
-TEST(liblog, android_logger_list_read__cpu) {
+TEST(liblog, android_logger_list_read__cpu_signal) {
     struct logger_list *logger_list;
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -606,7 +613,7 @@
 
     memset(&signal_time, 0, sizeof(signal_time));
 
-    signal(SIGALRM, caught_blocking);
+    signal(SIGALRM, caught_blocking_signal);
     alarm(alarm_time);
 
     signaled = 0;
@@ -651,7 +658,158 @@
     alarm(0);
     signal(SIGALRM, SIG_DFL);
 
-    EXPECT_LT(1, count);
+    EXPECT_LE(1, count);
+
+    EXPECT_EQ(1, signals);
+
+    android_logger_list_close(logger_list);
+
+    unsigned long long uticks_end;
+    unsigned long long sticks_end;
+    get_ticks(&uticks_end, &sticks_end);
+
+    // Less than 1% in either user or system time, or both
+    const unsigned long long one_percent_ticks = alarm_time;
+    unsigned long long user_ticks = uticks_end - uticks_start;
+    unsigned long long system_ticks = sticks_end - sticks_start;
+    EXPECT_GT(one_percent_ticks, user_ticks);
+    EXPECT_GT(one_percent_ticks, system_ticks);
+    EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+}
+
+/*
+ *  Strictly, we are not allowed to log messages in a signal context, the
+ * correct way to handle this is to ensure the messages are constructed in
+ * a thread; the signal handler should only unblock the thread.
+ */
+static sem_t thread_trigger;
+
+static void caught_blocking_thread(int /*signum*/)
+{
+    sem_post(&thread_trigger);
+}
+
+static void *running_thread(void *) {
+    unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+    v += getpid() & 0xFFFF;
+
+    struct timespec timeout;
+    clock_gettime(CLOCK_REALTIME, &timeout);
+    timeout.tv_sec += 55;
+    sem_timedwait(&thread_trigger, &timeout);
+
+    ++signaled;
+    if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+        signal_time = log_time(CLOCK_MONOTONIC);
+        signal_time.tv_sec += 2;
+    }
+
+    LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+    return NULL;
+}
+
+int start_thread()
+{
+    sem_init(&thread_trigger, 0, 0);
+
+    pthread_attr_t attr;
+    if (pthread_attr_init(&attr)) {
+        return -1;
+    }
+
+    struct sched_param param;
+
+    memset(&param, 0, sizeof(param));
+    pthread_attr_setschedparam(&attr, &param);
+    pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+
+    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+        pthread_attr_destroy(&attr);
+        return -1;
+    }
+
+    pthread_t thread;
+    if (pthread_create(&thread, &attr, running_thread, NULL)) {
+        pthread_attr_destroy(&attr);
+        return -1;
+    }
+
+    pthread_attr_destroy(&attr);
+    return 0;
+}
+
+TEST(liblog, android_logger_list_read__cpu_thread) {
+    struct logger_list *logger_list;
+    unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+    pid_t pid = getpid();
+
+    v += pid & 0xFFFF;
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+    int count = 0;
+
+    int signals = 0;
+
+    unsigned long long uticks_start;
+    unsigned long long sticks_start;
+    get_ticks(&uticks_start, &sticks_start);
+
+    const unsigned alarm_time = 10;
+
+    memset(&signal_time, 0, sizeof(signal_time));
+
+    signaled = 0;
+    EXPECT_EQ(0, start_thread());
+
+    signal(SIGALRM, caught_blocking_thread);
+    alarm(alarm_time);
+
+    do {
+        log_msg log_msg;
+        if (LOG_FAILURE_RETRY(android_logger_list_read(logger_list, &log_msg)) <= 0) {
+            break;
+        }
+
+        alarm(alarm_time);
+
+        ++count;
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.len != (4 + 1 + 8))
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        if (eventData[4] != EVENT_TYPE_LONG) {
+            continue;
+        }
+
+        unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
+        l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
+        l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
+        l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
+        l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
+        l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
+        l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
+        l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+
+        if (l == v) {
+            ++signals;
+            break;
+        }
+    } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+    alarm(0);
+    signal(SIGALRM, SIG_DFL);
+
+    EXPECT_LE(1, count);
 
     EXPECT_EQ(1, signals);
 
@@ -861,6 +1019,60 @@
     EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
 }
 
+TEST(liblog, __android_log_buf_print__maxtag) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    log_time ts(android_log_clockid());
+
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                         max_payload_buf, max_payload_buf));
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.sec < (ts.tv_sec - 1))
+         || ((ts.tv_sec + 1) < log_msg.entry.sec)
+         || ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD)
+         || (log_msg.id() != LOG_ID_MAIN)) {
+            continue;
+        }
+
+        ++count;
+
+        AndroidLogFormat *logformat = android_log_format_new();
+        EXPECT_TRUE(NULL != logformat);
+        AndroidLogEntry entry;
+        int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+                                                            &entry);
+        EXPECT_EQ(0, processLogBuffer);
+        if (processLogBuffer == 0) {
+            fflush(stderr);
+            int printLogLine =
+                    android_log_printLogLine(logformat, fileno(stderr), &entry);
+            EXPECT_LE(128, printLogLine);
+            EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD, printLogLine);
+        }
+        android_log_format_free(logformat);
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
 TEST(liblog, too_big_payload) {
     pid_t pid = getpid();
     static const char big_payload_tag[] = "TEST_big_payload_XXXX";
@@ -978,11 +1190,20 @@
         struct logger * logger;
         EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
         EXPECT_EQ(id, android_logger_get_id(logger));
-        EXPECT_LT(0, android_logger_get_log_size(logger));
-        /* crash buffer is allowed to be empty, that is actually healthy! */
-        if (android_logger_get_log_readable_size(logger) ||
-                (strcmp("crash", name) && strcmp("security", name))) {
-            EXPECT_LT(0, android_logger_get_log_readable_size(logger));
+        ssize_t get_log_size = android_logger_get_log_size(logger);
+        /* security buffer is allowed to be denied */
+        if (strcmp("security", name)) {
+            EXPECT_LT(0, get_log_size);
+            /* crash buffer is allowed to be empty, that is actually healthy! */
+            EXPECT_LE((strcmp("crash", name)) != 0,
+                      android_logger_get_log_readable_size(logger));
+        } else {
+            EXPECT_NE(0, get_log_size);
+            if (get_log_size < 0) {
+                EXPECT_GT(0, android_logger_get_log_readable_size(logger));
+            } else {
+                EXPECT_LE(0, android_logger_get_log_readable_size(logger));
+            }
         }
         EXPECT_LT(0, android_logger_get_log_version(logger));
     }
diff --git a/liblog/uio.c b/liblog/uio.c
index f77cc49..d0184dc 100644
--- a/liblog/uio.c
+++ b/liblog/uio.c
@@ -16,17 +16,20 @@
 
 #if defined(_WIN32)
 
-#include <log/uio.h>
 #include <unistd.h>
 
-int  readv( int  fd, struct iovec*  vecs, int  count )
+#include <log/uio.h>
+
+#include "log_cdefs.h"
+
+LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec *vecs, int count)
 {
     int   total = 0;
 
     for ( ; count > 0; count--, vecs++ ) {
         char*  buf = vecs->iov_base;
         int    len = vecs->iov_len;
-        
+
         while (len > 0) {
             int  ret = read( fd, buf, len );
             if (ret < 0) {
@@ -46,14 +49,14 @@
     return total;
 }
 
-int  writev( int  fd, const struct iovec*  vecs, int  count )
+LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec *vecs, int count)
 {
     int   total = 0;
 
     for ( ; count > 0; count--, vecs++ ) {
         const char*  buf = vecs->iov_base;
         int          len = vecs->iov_len;
-        
+
         while (len > 0) {
             int  ret = write( fd, buf, len );
             if (ret < 0) {
@@ -69,7 +72,7 @@
             len   -= ret;
         }
     }
-Exit:    
+Exit:
     return total;
 }
 
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index b75f1e5..68f654c 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -370,11 +370,11 @@
 }
 
 void* HeapImpl::AllocLocked(size_t size) {
-  if (__predict_false(size > kMaxBucketAllocationSize)) {
+  if (size > kMaxBucketAllocationSize) {
     return MapAlloc(size);
   }
   int bucket = size_to_bucket(size);
-  if (__predict_false(free_chunks_[bucket].empty())) {
+  if (free_chunks_[bucket].empty()) {
     Chunk *chunk = new Chunk(this, bucket);
     free_chunks_[bucket].insert(chunk->node_);
   }
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index b0a4d4c..a8f579e 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -24,6 +24,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 extern std::atomic<int> heap_count;
@@ -209,9 +210,12 @@
 template<class T>
 using list = std::list<T, Allocator<T>>;
 
-template<class T, class Key, class Compare = std::less<Key>>
+template<class Key, class T, class Compare = std::less<Key>>
 using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
 
+template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+
 template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
 using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
 
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
index 0df26a8..7b66d44 100644
--- a/libmemunreachable/Android.mk
+++ b/libmemunreachable/Android.mk
@@ -3,6 +3,7 @@
 memunreachable_srcs := \
    Allocator.cpp \
    HeapWalker.cpp \
+   LeakFolding.cpp \
    LeakPipe.cpp \
    LineBuffer.cpp \
    MemUnreachable.cpp \
@@ -12,7 +13,9 @@
 
 memunreachable_test_srcs := \
    tests/Allocator_test.cpp \
+   tests/DisableMalloc_test.cpp \
    tests/HeapWalker_test.cpp \
+   tests/LeakFolding_test.cpp \
    tests/MemUnreachable_test.cpp \
    tests/ThreadCapture_test.cpp \
 
@@ -41,3 +44,22 @@
 LOCAL_SHARED_LIBRARIES := libmemunreachable libbase liblog
 
 include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := memunreachable_test
+LOCAL_SRC_FILES := \
+   Allocator.cpp \
+   HeapWalker.cpp  \
+   LeakFolding.cpp \
+   tests/Allocator_test.cpp \
+   tests/HeapWalker_test.cpp \
+   tests/HostMallocStub.cpp \
+   tests/LeakFolding_test.cpp \
+
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libbase liblog
+LOCAL_MODULE_HOST_OS := linux
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 1a0c33d..19393ec 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -21,17 +21,19 @@
 
 #include "Allocator.h"
 #include "HeapWalker.h"
+#include "LeakFolding.h"
 #include "log.h"
 
 bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
   if (end == begin) {
     end = begin + 1;
   }
-  auto inserted = allocations_.insert(std::pair<Range, RangeInfo>(Range{begin, end}, RangeInfo{false, false}));
+  Range range{begin, end};
+  auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
   if (inserted.second) {
     valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
     valid_allocations_range_.end = std::max(valid_allocations_range_.end, end);
-    allocation_bytes_ += end - begin;
+    allocation_bytes_ += range.size();
     return true;
   } else {
     Range overlap = inserted.first->first;
@@ -44,27 +46,30 @@
   }
 }
 
-void HeapWalker::Walk(const Range& range, bool RangeInfo::*flag) {
-  allocator::vector<Range> to_do(1, range, allocator_);
+bool HeapWalker::IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info) {
+  if (ptr >= valid_allocations_range_.begin && ptr < valid_allocations_range_.end) {
+    AllocationMap::iterator it = allocations_.find(Range{ptr, ptr + 1});
+    if (it != allocations_.end()) {
+      *range = it->first;
+      *info = &it->second;
+      return true;
+    }
+  }
+  return false;
+}
+
+void HeapWalker::RecurseRoot(const Range& root) {
+  allocator::vector<Range> to_do(1, root, allocator_);
   while (!to_do.empty()) {
     Range range = to_do.back();
     to_do.pop_back();
-    uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
-    // TODO(ccross): we might need to consider a pointer to the end of a buffer
-    // to be inside the buffer, which means the common case of a pointer to the
-    // beginning of a buffer may keep two ranges live.
-    for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
-      uintptr_t val = *reinterpret_cast<uintptr_t*>(i);
-      if (val >= valid_allocations_range_.begin && val < valid_allocations_range_.end) {
-        RangeMap::iterator it = allocations_.find(Range{val, val + 1});
-        if (it != allocations_.end()) {
-          if (!(it->second.*flag)) {
-            to_do.push_back(it->first);
-            it->second.*flag = true;
-          }
-        }
+
+    ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
+      if (!ref_info->referenced_from_root) {
+        ref_info->referenced_from_root = true;
+        to_do.push_back(ref_range);
       }
-    }
+    });
   }
 }
 
@@ -85,27 +90,22 @@
 }
 
 bool HeapWalker::DetectLeaks() {
+  // Recursively walk pointers from roots to mark referenced allocations
   for (auto it = roots_.begin(); it != roots_.end(); it++) {
-    Walk(*it, &RangeInfo::referenced_from_root);
+    RecurseRoot(*it);
   }
 
   Range vals;
   vals.begin = reinterpret_cast<uintptr_t>(root_vals_.data());
   vals.end = vals.begin + root_vals_.size() * sizeof(uintptr_t);
-  Walk(vals, &RangeInfo::referenced_from_root);
 
-  for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
-    if (!it->second.referenced_from_root) {
-      Walk(it->first, &RangeInfo::referenced_from_leak);
-    }
-  }
+  RecurseRoot(vals);
 
   return true;
 }
 
 bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
     size_t* num_leaks_out, size_t* leak_bytes_out) {
-  DetectLeaks();
   leaked.clear();
 
   size_t num_leaks = 0;
@@ -120,7 +120,7 @@
   size_t n = 0;
   for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
     if (!it->second.referenced_from_root) {
-      if (n++ <= limit) {
+      if (n++ < limit) {
         leaked.push_back(it->first);
       }
     }
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 4be1934..7b851c4 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -20,11 +20,14 @@
 #include "android-base/macros.h"
 
 #include "Allocator.h"
+#include "Tarjan.h"
 
 // A range [begin, end)
 struct Range {
   uintptr_t begin;
   uintptr_t end;
+
+  size_t size() const { return end - begin; };
 };
 
 // Comparator for Ranges that returns equivalence for overlapping ranges
@@ -34,7 +37,6 @@
   }
 };
 
-
 class HeapWalker {
  public:
   HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
@@ -55,16 +57,25 @@
   size_t Allocations();
   size_t AllocationBytes();
 
- private:
-  struct RangeInfo {
+  template<class F>
+  void ForEachPtrInRange(const Range& range, F&& f);
+
+  template<class F>
+  void ForEachAllocation(F&& f);
+
+  struct AllocationInfo {
     bool referenced_from_root;
-    bool referenced_from_leak;
   };
-  void Walk(const Range& range, bool RangeInfo::* flag);
+
+ private:
+
+  void RecurseRoot(const Range& root);
+  bool IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
+
   DISALLOW_COPY_AND_ASSIGN(HeapWalker);
   Allocator<HeapWalker> allocator_;
-  using RangeMap = allocator::map<RangeInfo, Range, compare_range>;
-  RangeMap allocations_;
+  using AllocationMap = allocator::map<Range, AllocationInfo, compare_range>;
+  AllocationMap allocations_;
   size_t allocation_bytes_;
   Range valid_allocations_range_;
 
@@ -72,4 +83,28 @@
   allocator::vector<uintptr_t> root_vals_;
 };
 
+template<class F>
+inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
+  uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
+  // TODO(ccross): we might need to consider a pointer to the end of a buffer
+  // to be inside the buffer, which means the common case of a pointer to the
+  // beginning of a buffer may keep two ranges live.
+  for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
+    Range ref_range;
+    AllocationInfo* ref_info;
+    if (IsAllocationPtr(*reinterpret_cast<uintptr_t*>(i), &ref_range, &ref_info)) {
+      f(ref_range, ref_info);
+    }
+  }
+}
+
+template<class F>
+inline void HeapWalker::ForEachAllocation(F&& f) {
+  for (auto& it : allocations_) {
+    const Range& range = it.first;
+    HeapWalker::AllocationInfo& allocation = it.second;
+    f(range, allocation);
+  }
+}
+
 #endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
new file mode 100644
index 0000000..eaeeea7
--- /dev/null
+++ b/libmemunreachable/Leak.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_H_
+#define LIBMEMUNREACHABLE_LEAK_H_
+
+#include <functional>
+#include <vector>
+
+#include "memunreachable/memunreachable.h"
+
+// Custom std::hash specialization so that Leak::Backtrace can be used
+// as a key in std::unordered_map.
+namespace std {
+
+template<>
+struct hash<Leak::Backtrace> {
+  std::size_t operator()(const Leak::Backtrace& key) const {
+    std::size_t seed = 0;
+
+    hash_combine(seed, key.num_frames);
+    for (size_t i = 0; i < key.num_frames; i++) {
+      hash_combine(seed, key.frames[i]);
+    }
+
+    return seed;
+  }
+
+ private:
+  template<typename T>
+  inline void hash_combine(std::size_t& seed, const T& v) const {
+    std::hash<T> hasher;
+    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+  }
+};
+
+}  // namespace std
+
+static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
+  return (lhs.num_frames == rhs.num_frames) &&
+      memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
+
+#endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
new file mode 100644
index 0000000..be4d20c
--- /dev/null
+++ b/libmemunreachable/LeakFolding.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 <inttypes.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+#include "Tarjan.h"
+#include "log.h"
+
+// Converts possibly cyclic graph of leaks to a DAG by combining
+// strongly-connected components into a object, stored in the scc pointer
+// of each node in the component.
+void LeakFolding::ComputeDAG() {
+  SCCList<LeakInfo> scc_list{allocator_};
+  Tarjan(leak_graph_, scc_list);
+
+  Allocator<SCCInfo> scc_allocator = allocator_;
+
+  for (auto& scc_nodes: scc_list) {
+    Allocator<SCCInfo>::unique_ptr leak_scc;
+    leak_scc = scc_allocator.make_unique(scc_allocator);
+
+    for (auto& node: scc_nodes) {
+      node->ptr->scc = leak_scc.get();
+      leak_scc->count++;
+      leak_scc->size += node->ptr->range.size();
+    }
+
+    leak_scc_.emplace_back(std::move(leak_scc));
+  }
+
+  for (auto& it : leak_map_) {
+    LeakInfo& leak = it.second;
+    for (auto& ref: leak.node.references_out) {
+      if (leak.scc != ref->ptr->scc) {
+        leak.scc->node.Edge(&ref->ptr->scc->node);
+      }
+    }
+  }
+}
+
+void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
+  std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
+      [&](SCCInfo* scc) {
+        if (scc->accumulator != dominator) {
+          scc->accumulator = dominator;
+          dominator->cuumulative_size += scc->size;
+          dominator->cuumulative_count += scc->count;
+          scc->node.Foreach([&](SCCInfo* ref) {
+            walk(ref);
+          });
+        }
+      });
+  walk(dominator);
+}
+
+bool LeakFolding::FoldLeaks() {
+  Allocator<LeakInfo> leak_allocator = allocator_;
+
+  // Find all leaked allocations insert them into leak_map_ and leak_graph_
+  heap_walker_.ForEachAllocation(
+      [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+        if (!allocation.referenced_from_root) {
+          auto it = leak_map_.emplace(std::piecewise_construct,
+              std::forward_as_tuple(range),
+              std::forward_as_tuple(range, allocator_));
+          LeakInfo& leak = it.first->second;
+          leak_graph_.push_back(&leak.node);
+        }
+      });
+
+  // Find references between leaked allocations and connect them in leak_graph_
+  for (auto& it : leak_map_) {
+    LeakInfo& leak = it.second;
+    heap_walker_.ForEachPtrInRange(leak.range,
+        [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+          if (!ptr_info->referenced_from_root) {
+            LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+            leak.node.Edge(&ptr_leak->node);
+          }
+        });
+  }
+
+  // Convert the cyclic graph to a DAG by grouping strongly connected components
+  ComputeDAG();
+
+  // Compute dominators and cuumulative sizes
+  for (auto& scc : leak_scc_) {
+    if (scc->node.references_in.size() == 0) {
+      scc->dominator = true;
+      AccumulateLeaks(scc.get());
+    }
+  }
+
+  return true;
+}
+
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
+    size_t* num_leaks_out, size_t* leak_bytes_out) {
+  size_t num_leaks = 0;
+  size_t leak_bytes = 0;
+  for (auto& it : leak_map_) {
+    const LeakInfo& leak = it.second;
+    num_leaks++;
+    leak_bytes += leak.range.size();
+  }
+
+  for (auto& it : leak_map_) {
+    const LeakInfo& leak = it.second;
+    if (leak.scc->dominator) {
+      leaked.emplace_back(Leak{leak.range,
+        leak.scc->cuumulative_count - 1,
+        leak.scc->cuumulative_size - leak.range.size()});
+    }
+  }
+
+  if (num_leaks_out) {
+    *num_leaks_out = num_leaks;
+  }
+  if (leak_bytes_out) {
+    *leak_bytes_out = leak_bytes;
+  }
+
+  return true;
+}
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
new file mode 100644
index 0000000..732d3f2
--- /dev/null
+++ b/libmemunreachable/LeakFolding.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+#define LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+
+#include "HeapWalker.h"
+
+class LeakFolding {
+ public:
+  LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
+   : allocator_(allocator), heap_walker_(heap_walker),
+     leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+
+  bool FoldLeaks();
+
+  struct Leak {
+    const Range range;
+    size_t referenced_count;
+    size_t referenced_size;
+  };
+
+  bool Leaked(allocator::vector<Leak>& leaked,
+      size_t* num_leaks_out, size_t* leak_bytes_out);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LeakFolding);
+  Allocator<void> allocator_;
+  HeapWalker& heap_walker_;
+
+  struct SCCInfo {
+   public:
+    Node<SCCInfo> node;
+
+    size_t count;
+    size_t size;
+
+    size_t cuumulative_count;
+    size_t cuumulative_size;
+
+    bool dominator;
+    SCCInfo* accumulator;
+
+    SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
+        count(0), size(0), cuumulative_count(0), cuumulative_size(0),
+        dominator(false), accumulator(nullptr) {}
+   private:
+    SCCInfo(SCCInfo&&) = delete;
+    DISALLOW_COPY_AND_ASSIGN(SCCInfo);
+  };
+
+  struct LeakInfo {
+   public:
+    Node<LeakInfo> node;
+
+    const Range range;
+
+    SCCInfo* scc;
+
+    LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
+        : node(this, allocator), range(range),
+          scc(nullptr) {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(LeakInfo);
+  };
+
+  void ComputeDAG();
+  void AccumulateLeaks(SCCInfo* dominator);
+
+  allocator::map<Range, LeakInfo, compare_range> leak_map_;
+  Graph<LeakInfo> leak_graph_;
+  allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index eca26eb..ac19a66 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -21,12 +21,15 @@
 #include <mutex>
 #include <string>
 #include <sstream>
+#include <unordered_map>
 
 #include <backtrace.h>
 #include <android-base/macros.h>
 
 #include "Allocator.h"
 #include "HeapWalker.h"
+#include "Leak.h"
+#include "LeakFolding.h"
 #include "LeakPipe.h"
 #include "ProcessMappings.h"
 #include "PtracerThread.h"
@@ -117,32 +120,84 @@
   return true;
 }
 
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
-    size_t* num_leaks, size_t* leak_bytes) {
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
+    size_t limit, size_t* num_leaks, size_t* leak_bytes) {
   ALOGI("sweeping process %d for unreachable memory", pid_);
   leaks.clear();
 
-  allocator::vector<Range> leaked{allocator_};
-  if (!heap_walker_.Leaked(leaked, limit, num_leaks, leak_bytes)) {
+  if (!heap_walker_.DetectLeaks()) {
     return false;
   }
 
-  for (auto it = leaked.begin(); it != leaked.end(); it++) {
-    Leak leak{};
-    leak.begin = it->begin;
-    leak.size = it->end - it->begin;;
-    memcpy(leak.contents, reinterpret_cast<void*>(it->begin),
-        std::min(leak.size, Leak::contents_length));
-    ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it->begin),
-        leak.backtrace_frames, leak.backtrace_length);
-    if (num_backtrace_frames > 0) {
-      leak.num_backtrace_frames = num_backtrace_frames;
-    }
-    leaks.emplace_back(leak);
-  }
+
+  allocator::vector<Range> leaked1{allocator_};
+  heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
 
   ALOGI("sweeping done");
 
+  ALOGI("folding related leaks");
+
+  LeakFolding folding(allocator_, heap_walker_);
+  if (!folding.FoldLeaks()) {
+    return false;
+  }
+
+  allocator::vector<LeakFolding::Leak> leaked{allocator_};
+
+  if (!folding.Leaked(leaked, num_leaks, leak_bytes)) {
+    return false;
+  }
+
+  allocator::unordered_map<Leak::Backtrace, Leak*> backtrace_map{allocator_};
+
+  // Prevent reallocations of backing memory so we can store pointers into it
+  // in backtrace_map.
+  leaks.reserve(leaked.size());
+
+  for (auto& it: leaked) {
+    leaks.emplace_back();
+    Leak* leak = &leaks.back();
+
+    ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
+        leak->backtrace.frames, leak->backtrace.max_frames);
+    if (num_backtrace_frames > 0) {
+      leak->backtrace.num_frames = num_backtrace_frames;
+
+      auto inserted = backtrace_map.emplace(leak->backtrace, leak);
+      if (!inserted.second) {
+        // Leak with same backtrace already exists, drop this one and
+        // increment similar counts on the existing one.
+        leaks.pop_back();
+        Leak* similar_leak = inserted.first->second;
+        similar_leak->similar_count++;
+        similar_leak->similar_size += it.range.size();
+        similar_leak->similar_referenced_count += it.referenced_count;
+        similar_leak->similar_referenced_size += it.referenced_size;
+        similar_leak->total_size += it.range.size();
+        similar_leak->total_size += it.referenced_size;
+        continue;
+      }
+    }
+
+    leak->begin = it.range.begin;
+    leak->size = it.range.size();
+    leak->referenced_count = it.referenced_count;
+    leak->referenced_size = it.referenced_size;
+    leak->total_size = leak->size + leak->referenced_size;
+    memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
+        std::min(leak->size, Leak::contents_length));
+  }
+
+  ALOGI("folding done");
+
+  std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
+    return a.total_size > b.total_size;
+  });
+
+  if (leaks.size() > limit) {
+    leaks.resize(limit);
+  }
+
   return true;
 }
 
@@ -203,6 +258,11 @@
   return true;
 }
 
+template<typename T>
+static inline const char* plural(T val) {
+  return (val == 1) ? "" : "s";
+}
+
 bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
   int parent_pid = getpid();
   int parent_tid = gettid();
@@ -339,9 +399,8 @@
 
   ALOGI("unreachable memory detection done");
   ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
-      info.leak_bytes, info.num_leaks, info.num_leaks == 1 ? "" : "s",
-      info.allocation_bytes, info.num_allocations, info.num_allocations == 1 ? "" : "s");
-
+      info.leak_bytes, info.num_leaks, plural(info.num_leaks),
+      info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
   return true;
 }
 
@@ -353,6 +412,23 @@
   oss << " bytes unreachable at ";
   oss << std::hex << begin;
   oss << std::endl;
+  if (referenced_count > 0) {
+    oss << std::dec;
+    oss << "   referencing " << referenced_size << " unreachable bytes";
+    oss << " in " << referenced_count << " allocation" << plural(referenced_count);
+    oss << std::endl;
+  }
+  if (similar_count > 0) {
+    oss << std::dec;
+    oss << "   and " << similar_size << " similar unreachable bytes";
+    oss << " in " << similar_count << " allocation" << plural(similar_count);
+    oss << std::endl;
+    if (similar_referenced_count > 0) {
+      oss << "   referencing " << similar_referenced_size << " unreachable bytes";
+      oss << " in " << similar_referenced_count << " allocation" << plural(similar_referenced_count);
+      oss << std::endl;
+    }
+  }
 
   if (log_contents) {
     const int bytes_per_line = 16;
@@ -361,7 +437,7 @@
     if (bytes == size) {
       oss << "   contents:" << std::endl;
     } else {
-      oss << "  first " << bytes << " bytes of contents:" << std::endl;
+      oss << "   first " << bytes << " bytes of contents:" << std::endl;
     }
 
     for (size_t i = 0; i < bytes; i += bytes_per_line) {
@@ -385,21 +461,41 @@
       oss << std::endl;
     }
   }
-  if (num_backtrace_frames > 0) {
-    oss << backtrace_string(backtrace_frames, num_backtrace_frames);
+  if (backtrace.num_frames > 0) {
+    oss << backtrace_string(backtrace.frames, backtrace.num_frames);
   }
 
   return oss.str();
 }
 
+// Figure out the abi based on defined macros.
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
 std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
   std::ostringstream oss;
   oss << "  " << leak_bytes << " bytes in ";
-  oss << num_leaks << " unreachable allocation" << (num_leaks == 1 ? "" : "s");
+  oss << num_leaks << " unreachable allocation" << plural(num_leaks);
+  oss << std::endl;
+  oss << "  ABI: '" ABI_STRING "'" << std::endl;
   oss << std::endl;
 
   for (auto it = leaks.begin(); it != leaks.end(); it++) {
       oss << it->ToString(log_contents);
+      oss << std::endl;
   }
 
   return oss.str();
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 019deea..287f479 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -18,6 +18,7 @@
 #define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
 
 #include <signal.h>
+#include <sys/time.h>
 
 #include <chrono>
 #include <functional>
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
new file mode 100644
index 0000000..d7ecdb9
--- /dev/null
+++ b/libmemunreachable/Tarjan.h
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+// Based on system/update_engine/payload_generator/tarjan.cc
+
+#ifndef LIBMEMUNREACHABLE_TARJAN_H_
+#define LIBMEMUNREACHABLE_TARJAN_H_
+
+#include <algorithm>
+
+#include "Allocator.h"
+
+template<class T>
+class Node {
+ public:
+  allocator::set<Node<T>*> references_in;
+  allocator::set<Node<T>*> references_out;
+  size_t index;
+  size_t lowlink;
+
+  T* ptr;
+
+  Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
+      ptr(ptr) {};
+  Node(Node&& rhs) = default;
+  void Edge(Node<T>* ref) {
+    references_out.emplace(ref);
+    ref->references_in.emplace(this);
+  }
+  template<class F>
+  void Foreach(F&& f) {
+    for (auto& node: references_out) {
+      f(node->ptr);
+    }
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Node<T>);
+};
+
+template<class T>
+using Graph = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCC = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCCList = allocator::vector<SCC<T>>;
+
+template<class T>
+class TarjanAlgorithm {
+ public:
+  TarjanAlgorithm(Allocator<void> allocator) : index_(0),
+    stack_(allocator), components_(allocator) {}
+
+  void Execute(Graph<T>& graph, SCCList<T>& out);
+ private:
+  static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
+  void Tarjan(Node<T>* vertex, Graph<T>& graph);
+
+  size_t index_;
+  allocator::vector<Node<T>*> stack_;
+  SCCList<T> components_;
+};
+
+template<class T>
+void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
+  stack_.clear();
+  components_.clear();
+  index_ = 0;
+  for (auto& it: graph) {
+    it->index = UNDEFINED_INDEX;
+    it->lowlink = UNDEFINED_INDEX;
+  }
+
+  for (auto& it: graph) {
+    if (it->index == UNDEFINED_INDEX) {
+      Tarjan(it, graph);
+    }
+  }
+  out.swap(components_);
+}
+
+template<class T>
+void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
+  assert(vertex->index == UNDEFINED_INDEX);
+  vertex->index = index_;
+  vertex->lowlink = index_;
+  index_++;
+  stack_.push_back(vertex);
+  for (auto& it: vertex->references_out) {
+    Node<T>* vertex_next = it;
+    if (vertex_next->index == UNDEFINED_INDEX) {
+      Tarjan(vertex_next, graph);
+      vertex->lowlink = std::min(vertex->lowlink, vertex_next->lowlink);
+    } else if (std::find(stack_.begin(), stack_.end(), vertex_next) != stack_.end()) {
+      vertex->lowlink = std::min(vertex->lowlink, vertex_next->index);
+    }
+  }
+  if (vertex->lowlink == vertex->index) {
+    SCC<T> component{components_.get_allocator()};
+    Node<T>* other_vertex;
+    do {
+      other_vertex = stack_.back();
+      stack_.pop_back();
+      component.push_back(other_vertex);
+    } while (other_vertex != vertex && !stack_.empty());
+
+    components_.emplace_back(component);
+  }
+}
+
+template<class T>
+void Tarjan(Graph<T>& graph, SCCList<T>& out) {
+  TarjanAlgorithm<T> tarjan{graph.get_allocator()};
+  tarjan.Execute(graph, out);
+}
+
+#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 6357840..e8a8392 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -86,7 +86,7 @@
   void PtraceDetach(pid_t tid, unsigned int signal);
   bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
 
-  allocator::map<unsigned int, pid_t> captured_threads_;
+  allocator::map<pid_t, unsigned int> captured_threads_;
   Allocator<ThreadCaptureImpl> allocator_;
   pid_t pid_;
   std::function<void(pid_t)> inject_test_func_;
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 92de24a..83d07a8 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -18,6 +18,8 @@
 #define LIBMEMUNREACHABLE_BIONIC_H_
 
 #include <sys/cdefs.h>
+#include <stdint.h>
+#include <stdlib.h>
 
 __BEGIN_DECLS
 
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index f4f01ce..9b227fd 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -27,11 +27,26 @@
 struct Leak {
   uintptr_t begin;
   size_t size;
-  size_t num_backtrace_frames;
+
+  size_t referenced_count;
+  size_t referenced_size;
+
+  size_t similar_count;
+  size_t similar_size;
+  size_t similar_referenced_count;
+  size_t similar_referenced_size;
+
+  size_t total_size;
+
   static const size_t contents_length = 32;
   char contents[contents_length];
-  static const size_t backtrace_length = 16;
-  uintptr_t backtrace_frames[backtrace_length];
+
+  struct Backtrace {
+    size_t num_frames;
+
+    static const size_t max_frames = 16;
+    uintptr_t frames[max_frames];
+  } backtrace;
 
   std::string ToString(bool log_contents) const;
 };
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index d8e473e..fa76ae0 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -15,12 +15,6 @@
  */
 
 #include <Allocator.h>
-#include <sys/time.h>
-
-#include <chrono>
-#include <functional>
-#include <list>
-#include <vector>
 
 #include <gtest/gtest.h>
 #include <ScopedDisableMalloc.h>
@@ -28,8 +22,6 @@
 
 std::function<void()> ScopedAlarm::func_;
 
-using namespace std::chrono_literals;
-
 class AllocatorTest : public testing::Test {
  protected:
   AllocatorTest() : heap(), disable_malloc_() {}
@@ -180,94 +172,3 @@
 
   ASSERT_NE(ptr, nullptr);
 }
-
-class DisableMallocTest : public ::testing::Test {
- protected:
-  void alarm(std::chrono::microseconds us) {
-    std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
-    itimerval t = itimerval();
-    t.it_value.tv_sec = s.count();
-    t.it_value.tv_usec = (us - s).count();
-    setitimer(ITIMER_REAL, &t, NULL);
-  }
-};
-
-TEST_F(DisableMallocTest, reenable) {
-  ASSERT_EXIT({
-    alarm(100ms);
-    void *ptr1 = malloc(128);
-    ASSERT_NE(ptr1, nullptr);
-    free(ptr1);
-    {
-      ScopedDisableMalloc disable_malloc;
-    }
-    void *ptr2 = malloc(128);
-    ASSERT_NE(ptr2, nullptr);
-    free(ptr2);
-    _exit(1);
-  }, ::testing::ExitedWithCode(1), "");
-}
-
-TEST_F(DisableMallocTest, deadlock_allocate) {
-  ASSERT_DEATH({
-    void *ptr = malloc(128);
-    ASSERT_NE(ptr, nullptr);
-    free(ptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      void* ptr = malloc(128);
-      ASSERT_NE(ptr, nullptr);
-      free(ptr);
-    }
-  }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_new) {
-  ASSERT_DEATH({
-    char* ptr = new(char);
-    ASSERT_NE(ptr, nullptr);
-    delete(ptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      char* ptr = new(char);
-      ASSERT_NE(ptr, nullptr);
-      delete(ptr);
-    }
-  }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_delete) {
-  ASSERT_DEATH({
-    char* ptr = new(char);
-    ASSERT_NE(ptr, nullptr);
-    {
-      alarm(250ms);
-      ScopedDisableMalloc disable_malloc;
-      delete(ptr);
-    }
-  }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_free) {
-  ASSERT_DEATH({
-    void *ptr = malloc(128);
-    ASSERT_NE(ptr, nullptr);
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      free(ptr);
-    }
-  }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_fork) {
-  ASSERT_DEATH({
-    {
-      alarm(100ms);
-      ScopedDisableMalloc disable_malloc;
-      fork();
-    }
-  }, "");
-}
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
new file mode 100644
index 0000000..ea5c22c
--- /dev/null
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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/time.h>
+
+#include <chrono>
+#include <functional>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+using namespace std::chrono_literals;
+
+class DisableMallocTest : public ::testing::Test {
+ protected:
+  void alarm(std::chrono::microseconds us) {
+    std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+    itimerval t = itimerval();
+    t.it_value.tv_sec = s.count();
+    t.it_value.tv_usec = (us - s).count();
+    setitimer(ITIMER_REAL, &t, NULL);
+  }
+};
+
+TEST_F(DisableMallocTest, reenable) {
+  ASSERT_EXIT({
+    alarm(100ms);
+    void *ptr1 = malloc(128);
+    ASSERT_NE(ptr1, nullptr);
+    free(ptr1);
+    {
+      ScopedDisableMalloc disable_malloc;
+    }
+    void *ptr2 = malloc(128);
+    ASSERT_NE(ptr2, nullptr);
+    free(ptr2);
+    _exit(1);
+  }, ::testing::ExitedWithCode(1), "");
+}
+
+TEST_F(DisableMallocTest, deadlock_allocate) {
+  ASSERT_DEATH({
+    void *ptr = malloc(128);
+    ASSERT_NE(ptr, nullptr);
+    free(ptr);
+    {
+      alarm(100ms);
+      ScopedDisableMalloc disable_malloc;
+      void* ptr = malloc(128);
+      ASSERT_NE(ptr, nullptr);
+      free(ptr);
+    }
+  }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_new) {
+  ASSERT_DEATH({
+    char* ptr = new(char);
+    ASSERT_NE(ptr, nullptr);
+    delete(ptr);
+    {
+      alarm(100ms);
+      ScopedDisableMalloc disable_malloc;
+      char* ptr = new(char);
+      ASSERT_NE(ptr, nullptr);
+      delete(ptr);
+    }
+  }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_delete) {
+  ASSERT_DEATH({
+    char* ptr = new(char);
+    ASSERT_NE(ptr, nullptr);
+    {
+      alarm(250ms);
+      ScopedDisableMalloc disable_malloc;
+      delete(ptr);
+    }
+  }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_free) {
+  ASSERT_DEATH({
+    void *ptr = malloc(128);
+    ASSERT_NE(ptr, nullptr);
+    {
+      alarm(100ms);
+      ScopedDisableMalloc disable_malloc;
+      free(ptr);
+    }
+  }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_fork) {
+  ASSERT_DEATH({
+    {
+      alarm(100ms);
+      ScopedDisableMalloc disable_malloc;
+      fork();
+    }
+  }, "");
+}
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 9921eb6..c3e1c4d 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -80,6 +80,8 @@
   HeapWalker heap_walker(heap_);
   heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
 
+  ASSERT_EQ(true, heap_walker.DetectLeaks());
+
   allocator::vector<Range> leaked(heap_);
   size_t num_leaks = 0;
   size_t leaked_bytes = 0;
@@ -106,9 +108,11 @@
       heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
       heap_walker.Root(buffer_begin(buffer1), buffer_end(buffer1));
 
+      ASSERT_EQ(true, heap_walker.DetectLeaks());
+
       allocator::vector<Range> leaked(heap_);
-      size_t num_leaks = SIZE_T_MAX;
-      size_t leaked_bytes = SIZE_T_MAX;
+      size_t num_leaks = SIZE_MAX;
+      size_t leaked_bytes = SIZE_MAX;
       ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
 
       EXPECT_EQ(0U, num_leaks);
@@ -132,9 +136,11 @@
       heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
       heap_walker.Root(buffer_begin(buffer1) + i, buffer_end(buffer1) - j);
 
+      ASSERT_EQ(true, heap_walker.DetectLeaks());
+
       allocator::vector<Range> leaked(heap_);
-      size_t num_leaks = SIZE_T_MAX;
-      size_t leaked_bytes = SIZE_T_MAX;
+      size_t num_leaks = SIZE_MAX;
+      size_t leaked_bytes = SIZE_MAX;
       ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
 
       EXPECT_EQ(0U, num_leaks);
@@ -143,3 +149,26 @@
     }
   }
 }
+
+TEST_F(HeapWalkerTest, cycle) {
+  void* buffer1;
+  void* buffer2;
+
+  buffer1 = &buffer2;
+  buffer2 = &buffer1;
+
+  HeapWalker heap_walker(heap_);
+  heap_walker.Allocation(buffer_begin(buffer1), buffer_end(buffer1));
+  heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+  ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+  allocator::vector<Range> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(2U, num_leaks);
+  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(2U, leaked.size());
+}
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
new file mode 100644
index 0000000..a7e3f07
--- /dev/null
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 "bionic.h"
+
+void malloc_disable() {
+}
+
+void malloc_enable() {
+}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
new file mode 100644
index 0000000..879a3a0
--- /dev/null
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 "HeapWalker.h"
+#include "LeakFolding.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class LeakFoldingTest : public ::testing::Test {
+ public:
+  LeakFoldingTest() : disable_malloc_(), heap_() {}
+
+  void TearDown() {
+    ASSERT_TRUE(heap_.empty());
+    if (!HasFailure()) {
+      ASSERT_FALSE(disable_malloc_.timed_out());
+    }
+  }
+
+ protected:
+  ScopedDisableMallocTimeout disable_malloc_;
+  Heap heap_;
+};
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(&buffer[0])
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(&buffer[0]) + sizeof(buffer))
+#define ALLOCATION(heap_walker, buffer) \
+  ASSERT_EQ(true, heap_walker.Allocation(buffer_begin(buffer), buffer_end(buffer)))
+
+TEST_F(LeakFoldingTest, one) {
+  void* buffer1[1] = {nullptr};
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(1U, num_leaks);
+  EXPECT_EQ(sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(1U, leaked.size());
+  EXPECT_EQ(0U, leaked[0].referenced_count);
+  EXPECT_EQ(0U, leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two) {
+  void* buffer1[1] = {nullptr};
+  void* buffer2[1] = {nullptr};
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(2U, num_leaks);
+  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(2U, leaked.size());
+  EXPECT_EQ(0U, leaked[0].referenced_count);
+  EXPECT_EQ(0U, leaked[0].referenced_size);
+  EXPECT_EQ(0U, leaked[1].referenced_count);
+  EXPECT_EQ(0U, leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator) {
+  void* buffer1[1];
+  void* buffer2[1] = {nullptr};
+
+  buffer1[0] = buffer2;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(2U, num_leaks);
+  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(1U, leaked.size());
+  EXPECT_EQ(1U, leaked[0].referenced_count);
+  EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, cycle) {
+  void* buffer1[1];
+  void* buffer2[1];
+  void* buffer3[1];
+
+  buffer1[0] = buffer2;
+  buffer2[0] = buffer3;
+  buffer3[0] = buffer2;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+  ALLOCATION(heap_walker, buffer3);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(3U, num_leaks);
+  EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(1U, leaked.size());
+  EXPECT_EQ(2U, leaked[0].referenced_count);
+  EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator_cycle) {
+  void* buffer1[2] = {nullptr, nullptr};
+  void* buffer2[2];
+  void* buffer3[1] = {nullptr};
+
+  buffer1[0] = &buffer2;
+  buffer2[0] = &buffer1;
+  buffer2[1] = &buffer3;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+  ALLOCATION(heap_walker, buffer3);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(3U, num_leaks);
+  EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(2U, leaked.size());
+
+  EXPECT_EQ(2U, leaked[0].referenced_count);
+  EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(2U, leaked[1].referenced_count);
+  EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_cycles) {
+  void* buffer1[1];
+  void* buffer2[1];
+  void* buffer3[1];
+  void* buffer4[1];
+  void* buffer5[1];
+  void* buffer6[1];
+
+  buffer1[0] = buffer3;
+  buffer2[0] = buffer5;
+  buffer3[0] = buffer4;
+  buffer4[0] = buffer3;
+  buffer5[0] = buffer6;
+  buffer6[0] = buffer5;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+  ALLOCATION(heap_walker, buffer3);
+  ALLOCATION(heap_walker, buffer4);
+  ALLOCATION(heap_walker, buffer5);
+  ALLOCATION(heap_walker, buffer6);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(6U, num_leaks);
+  EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(2U, leaked.size());
+  EXPECT_EQ(2U, leaked[0].referenced_count);
+  EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(2U, leaked[1].referenced_count);
+  EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_dominator_cycles) {
+  void* buffer1[1];
+  void* buffer2[1];
+  void* buffer3[1];
+  void* buffer4[1];
+
+  buffer1[0] = buffer2;
+  buffer2[0] = buffer1;
+  buffer3[0] = buffer4;
+  buffer4[0] = buffer3;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+  ALLOCATION(heap_walker, buffer3);
+  ALLOCATION(heap_walker, buffer4);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(4U, num_leaks);
+  EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(4U, leaked.size());
+  EXPECT_EQ(1U, leaked[0].referenced_count);
+  EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(1U, leaked[1].referenced_count);
+  EXPECT_EQ(sizeof(uintptr_t), leaked[1].referenced_size);
+  EXPECT_EQ(1U, leaked[2].referenced_count);
+  EXPECT_EQ(sizeof(uintptr_t), leaked[2].referenced_size);
+  EXPECT_EQ(1U, leaked[3].referenced_count);
+  EXPECT_EQ(sizeof(uintptr_t), leaked[3].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_dominator_cycle) {
+  const size_t n = 1000;
+  void* buffer[n];
+
+  HeapWalker heap_walker(heap_);
+
+  for (size_t i = 0; i < n; i ++) {
+    ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+        reinterpret_cast<uintptr_t>(&buffer[i+1])));
+  }
+
+  for (size_t i = 0; i < n - 1; i++) {
+    buffer[i] = &buffer[i+1];
+  }
+  buffer[n - 1] = &buffer[0];
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(n, num_leaks);
+  EXPECT_EQ(n * sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(1000U, leaked.size());
+  EXPECT_EQ(n - 1, leaked[0].referenced_count);
+  EXPECT_EQ((n - 1) * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_cycle) {
+  const size_t n = 1000;
+  void* buffer[n];
+  void* buffer1[1];
+
+  HeapWalker heap_walker(heap_);
+
+  for (size_t i = 0; i < n - 1; i++) {
+    buffer[i] = &buffer[i+1];
+  }
+  buffer[n - 1] = &buffer[0];
+
+  buffer1[0] = &buffer[0];
+
+  for (size_t i = 0; i < n; i ++) {
+    ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+        reinterpret_cast<uintptr_t>(&buffer[i+1])));
+  }
+
+  ALLOCATION(heap_walker, buffer1);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(n + 1, num_leaks);
+  EXPECT_EQ((n + 1) * sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(1U, leaked.size());
+  EXPECT_EQ(n, leaked[0].referenced_count);
+  EXPECT_EQ(n * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multipath) {
+  void* buffer1[2];
+  void* buffer2[1];
+  void* buffer3[1];
+  void* buffer4[1] = {nullptr};
+
+  //    1
+  //   / \
+  //  v   v
+  //  2   3
+  //   \ /
+  //    v
+  //    4
+
+  buffer1[0] = &buffer2;
+  buffer1[1] = &buffer3;
+  buffer2[0] = &buffer4;
+  buffer3[0] = &buffer4;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+  ALLOCATION(heap_walker, buffer3);
+  ALLOCATION(heap_walker, buffer4);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(4U, num_leaks);
+  EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(1U, leaked.size());
+  EXPECT_EQ(3U, leaked[0].referenced_count);
+  EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multicycle) {
+  void* buffer1[2]{};
+  void* buffer2[2]{};
+  void* buffer3[2]{};
+  void* buffer4[2]{};
+
+  //    1
+  //   / ^
+  //  v   \
+  //  2 -> 3
+  //   \   ^
+  //    v /
+  //     4
+
+  buffer1[0] = &buffer2;
+  buffer2[0] = &buffer3;
+  buffer2[1] = &buffer4;
+  buffer3[0] = &buffer1;
+  buffer4[0] = &buffer3;
+
+  HeapWalker heap_walker(heap_);
+
+  ALLOCATION(heap_walker, buffer1);
+  ALLOCATION(heap_walker, buffer2);
+  ALLOCATION(heap_walker, buffer3);
+  ALLOCATION(heap_walker, buffer4);
+
+  LeakFolding folding(heap_, heap_walker);
+
+  ASSERT_TRUE(folding.FoldLeaks());
+
+  allocator::vector<LeakFolding::Leak> leaked(heap_);
+  size_t num_leaks = 0;
+  size_t leaked_bytes = 0;
+  ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+  EXPECT_EQ(4U, num_leaks);
+  EXPECT_EQ(8 * sizeof(uintptr_t), leaked_bytes);
+  ASSERT_EQ(4U, leaked.size());
+  EXPECT_EQ(3U, leaked[0].referenced_count);
+  EXPECT_EQ(6 * sizeof(uintptr_t), leaked[0].referenced_size);
+  EXPECT_EQ(3U, leaked[1].referenced_count);
+  EXPECT_EQ(6 * sizeof(uintptr_t), leaked[1].referenced_size);
+  EXPECT_EQ(3U, leaked[2].referenced_count);
+  EXPECT_EQ(6 * sizeof(uintptr_t), leaked[2].referenced_size);
+  EXPECT_EQ(3U, leaked[3].referenced_count);
+  EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
+}
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 32a65ea..ecfd719 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -231,8 +231,10 @@
 static const char* kRuntimeISA = "arm";
 #elif defined(__aarch64__)
 static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
 static const char* kRuntimeISA = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const char* kRuntimeISA = "mips64";
 #elif defined(__i386__)
 static const char* kRuntimeISA = "x86";
 #elif defined(__x86_64__)
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index e1c0876..2067ed2 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -18,15 +18,17 @@
 
 namespace android {
 
-static const char* kISAs[] = { "arm", "arm64", "mips", "x86", "x86_64", "random", "64arm", "64_x86",
-                               "64_x86_64", "", "reallylongstringabcd", nullptr };
+static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
+                               "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
 
 #if defined(__arm__)
 static const char* kRuntimeISA = "arm";
 #elif defined(__aarch64__)
 static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
 static const char* kRuntimeISA = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const char* kRuntimeISA = "mips64";
 #elif defined(__i386__)
 static const char* kRuntimeISA = "x86";
 #elif defined(__x86_64__)
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index b16c0e6..1bd3b8f 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -26,7 +26,7 @@
 namespace android {
 
 __attribute__((visibility("default")))
-void PreloadPublicNativeLibraries();
+void InitializeNativeLoader();
 
 __attribute__((visibility("default")))
 jstring CreateClassLoaderNamespace(JNIEnv* env,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 86d9d77..899c98c 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -29,32 +29,14 @@
 #include <string>
 #include <mutex>
 
+#include "android-base/file.h"
 #include "android-base/macros.h"
 #include "android-base/strings.h"
 
 namespace android {
 
-#ifdef __ANDROID__
-// TODO(dimitry): move this to system properties.
-static const char* kPublicNativeLibraries = "libandroid.so:"
-                                            "libc.so:"
-                                            "libdl.so:"
-                                            "libEGL.so:"
-                                            "libGLESv1_CM.so:"
-                                            "libGLESv2.so:"
-                                            "libGLESv3.so:"
-                                            "libicui18n.so:"
-                                            "libicuuc.so:"
-                                            "libjnigraphics.so:"
-                                            "liblog.so:"
-                                            "libmediandk.so:"
-                                            "libm.so:"
-                                            "libOpenMAXAL.so:"
-                                            "libOpenSLES.so:"
-                                            "libRS.so:"
-                                            "libstdc++.so:"
-                                            "libwebviewchromium_plat_support.so:"
-                                            "libz.so";
+#if defined(__ANDROID__)
+static constexpr const char* kPublicNativeLibrariesConfig = "/system/etc/public.libraries.txt";
 
 class LibraryNamespaces {
  public:
@@ -79,7 +61,8 @@
 
     android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
 
-    LOG_FATAL_IF(ns != nullptr, "There is already a namespace associated with this classloader");
+    LOG_ALWAYS_FATAL_IF(ns != nullptr,
+                        "There is already a namespace associated with this classloader");
 
     uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
     if (is_shared) {
@@ -109,10 +92,30 @@
     return it != namespaces_.end() ? it->second : nullptr;
   }
 
-  void PreloadPublicLibraries() {
+  void Initialize() {
+    // Read list of public native libraries from the config file.
+    std::string file_content;
+    LOG_ALWAYS_FATAL_IF(!base::ReadFileToString(kPublicNativeLibrariesConfig, &file_content),
+                        "Error reading public native library list from \"%s\": %s",
+                        kPublicNativeLibrariesConfig, strerror(errno));
+
+    std::vector<std::string> lines = base::Split(file_content, "\n");
+
+    std::vector<std::string> sonames;
+
+    for (const auto& line : lines) {
+      auto trimmed_line = base::Trim(line);
+      if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+        continue;
+      }
+
+      sonames.push_back(trimmed_line);
+    }
+
+    public_libraries_ = base::Join(sonames, ':');
+
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
-    std::vector<std::string> sonames = android::base::Split(kPublicNativeLibraries, ":");
     for (const auto& soname : sonames) {
       dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
     }
@@ -120,15 +123,19 @@
 
  private:
   bool InitPublicNamespace(const char* library_path) {
-    // Some apps call dlopen from generated code unknown to linker in which
-    // case linker uses anonymous namespace. See b/25844435 for details.
-    initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
+    // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+    // code is one example) unknown to linker in which  case linker uses anonymous
+    // namespace. The second argument specifies the search path for the anonymous
+    // namespace which is the library_path of the classloader.
+    initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
 
     return initialized_;
   }
 
   bool initialized_;
   std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
+  std::string public_libraries_;
+
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
@@ -141,10 +148,10 @@
 }
 #endif
 
-void PreloadPublicNativeLibraries() {
+void InitializeNativeLoader() {
 #if defined(__ANDROID__)
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  g_namespaces->PreloadPublicLibraries();
+  g_namespaces->Initialize();
 #endif
 }
 
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index ee9ea3c..9a5f7d8 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -92,7 +92,7 @@
 
         for (j = 0; j < 2; j++) {
             unsigned val = i + j * 3 + 1;
-            sprintf(str, "test_fence%d-%d", i, j);
+            snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
             int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
             if (fd < 0) {
                 printf("can't create sync pt %d: %s", val, strerror(errno));
@@ -106,7 +106,7 @@
 
     sync_data[3].thread_no = 3;
     for (j = 0; j < 2; j++) {
-        sprintf(str, "merged_fence%d", j);
+        snprintf(str, sizeof(str), "merged_fence%d", j);
         sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
         if (sync_data[3].fd[j] < 0) {
             printf("can't merge sync pts %d and %d: %s\n",
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 02907ad..ea1e4db 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -190,17 +190,22 @@
         {
             Mutex::Autolock _l(mMutex);
             char buf[128];
-            sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+            snprintf(buf, sizeof(buf),
+                     "Strong references on RefBase %p (weakref_type %p):\n",
+                     mBase, this);
             text.append(buf);
             printRefsLocked(&text, mStrongRefs);
-            sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+            snprintf(buf, sizeof(buf),
+                     "Weak references on RefBase %p (weakref_type %p):\n",
+                     mBase, this);
             text.append(buf);
             printRefsLocked(&text, mWeakRefs);
         }
 
         {
             char name[100];
-            snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+            snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
+                     this);
             int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
             if (rc >= 0) {
                 write(rc, text.string(), text.length());
@@ -293,8 +298,8 @@
         char buf[128];
         while (refs) {
             char inc = refs->ref >= 0 ? '+' : '-';
-            sprintf(buf, "\t%c ID %p (ref %d):\n", 
-                    inc, refs->id, refs->ref);
+            snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
+                     inc, refs->id, refs->ref);
             out->append(buf);
 #if DEBUG_REFS_CALLSTACK_ENABLED
             out->append(refs->stack.toString("\t\t"));
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 5ab6195..60834fe 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -43,7 +43,14 @@
 
     uint32_t tags_addr;    /* physical addr for kernel tags */
     uint32_t page_size;    /* flash page size we assume */
-    uint32_t unused[2];    /* future expansion: should be 0 */
+    uint32_t unused;       /* reserved for future expansion: MUST be 0 */
+
+    /* operating system version and security patch level; for
+     * version "A.B.C" and patch level "Y-M-D":
+     * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C)
+     * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M)
+     * os_version = ver << 11 | lvl */
+    uint32_t os_version;
 
     uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
 
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index f95d703..7b04bcc 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -20,6 +20,7 @@
 from struct import pack
 from hashlib import sha1
 import sys
+import re
 
 def filesize(f):
     if f is None:
@@ -47,7 +48,7 @@
 def write_header(args):
     BOOT_MAGIC = 'ANDROID!'.encode()
     args.output.write(pack('8s', BOOT_MAGIC))
-    args.output.write(pack('8I',
+    args.output.write(pack('10I',
         filesize(args.kernel),                          # size in bytes
         args.base + args.kernel_offset,                 # physical load addr
         filesize(args.ramdisk),                         # size in bytes
@@ -55,8 +56,9 @@
         filesize(args.second),                          # size in bytes
         args.base + args.second_offset,                 # physical load addr
         args.base + args.tags_offset,                   # physical addr for kernel tags
-        args.pagesize))                                 # flash page size we assume
-    args.output.write(pack('8x'))                       # future expansion: should be 0
+        args.pagesize,                                  # flash page size we assume
+        0,                                              # future expansion: MUST be 0
+        (args.os_version << 11) | args.os_patch_level)) # os version and patch level
     args.output.write(pack('16s', args.board.encode())) # asciiz product name
     args.output.write(pack('512s', args.cmdline[:512].encode()))
 
@@ -97,6 +99,32 @@
 def parse_int(x):
     return int(x, 0)
 
+def parse_os_version(x):
+    match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
+    if match:
+        a = parse_int(match.group(1))
+        b = c = 0
+        if match.lastindex >= 2:
+            b = parse_int(match.group(2))
+        if match.lastindex == 3:
+            c = parse_int(match.group(3))
+        # 7 bits allocated for each field
+        assert a < 128
+        assert b < 128
+        assert c < 128
+        return (a << 14) | (b << 7) | c
+    return 0
+
+def parse_os_patch_level(x):
+    match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
+    if match:
+        y = parse_int(match.group(1)) - 2000
+        m = parse_int(match.group(2))
+        # 7 bits allocated for the year, 4 bits for the month
+        assert y >= 0 and y < 128
+        assert m > 0 and m <= 12
+        return (y << 4) | m
+    return 0
 
 def parse_cmdline():
     parser = ArgumentParser()
@@ -111,6 +139,10 @@
     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
                         default=0x00f00000)
+    parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
+                        default=0)
+    parser.add_argument('--os_patch_level', help='operating system patch level',
+                        type=parse_os_patch_level, default=0)
     parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
     parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
                         maxlen=16)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d90f988..d53af2f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -70,6 +70,11 @@
     ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
     ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
 endif
+# The A/B updater uses a top-level /postinstall directory to mount the new
+# system before reboot.
+ifeq ($(AB_OTA_UPDATER),true)
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
+endif
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
new file mode 100644
index 0000000..8db2ba8
--- /dev/null
+++ b/rootdir/etc/public.libraries.android.txt
@@ -0,0 +1,19 @@
+libandroid.so
+libc.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libwebviewchromium_plat_support.so
+libz.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
new file mode 100644
index 0000000..673e115
--- /dev/null
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -0,0 +1,18 @@
+libandroid.so
+libc.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libz.so
diff --git a/rootdir/init-debug.rc b/rootdir/init-debug.rc
index 435d4cb..44d34d8 100644
--- a/rootdir/init-debug.rc
+++ b/rootdir/init-debug.rc
@@ -6,3 +6,6 @@
 
 on property:persist.mmc.cache_size=*
     write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}
+
+on early-init
+    mount debugfs debugfs /sys/kernel/debug
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1f63fcf..aa32343 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -23,6 +23,9 @@
     # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
     mkdir /mnt 0775 root system
 
+    # Set the security context of /postinstall if present.
+    restorecon /postinstall
+
     start ueventd
 
 on init
@@ -56,6 +59,17 @@
     chown root system /sys/fs/cgroup/memory/sw/tasks
     chmod 0660 /sys/fs/cgroup/memory/sw/tasks
 
+    # Create energy-aware scheduler tuning nodes
+    mkdir /sys/fs/cgroup/stune
+    mount cgroup none /sys/fs/cgroup/stune schedtune
+    mkdir /sys/fs/cgroup/stune/foreground
+    chown system system /sys/fs/cgroup/stune
+    chown system system /sys/fs/cgroup/stune/foreground
+    chown system system /sys/fs/cgroup/stune/tasks
+    chown system system /sys/fs/cgroup/stune/foreground/tasks
+    chmod 0664 /sys/fs/cgroup/stune/tasks
+    chmod 0664 /sys/fs/cgroup/stune/foreground/tasks
+
     # Mount staging areas for devices managed by vold
     # See storage config details at http://source.android.com/tech/storage/
     mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
@@ -71,7 +85,6 @@
     mkdir /mnt/expand 0771 system system
 
     # Storage views to support runtime permissions
-    mkdir /storage 0755 root root
     mkdir /mnt/runtime 0700 root root
     mkdir /mnt/runtime/default 0755 root root
     mkdir /mnt/runtime/default/self 0755 root root
@@ -167,13 +180,16 @@
     chown system system /dev/cpuset/foreground
     chown system system /dev/cpuset/foreground/boost
     chown system system /dev/cpuset/background
+    chown system system /dev/cpuset/system-background
     chown system system /dev/cpuset/tasks
     chown system system /dev/cpuset/foreground/tasks
     chown system system /dev/cpuset/foreground/boost/tasks
     chown system system /dev/cpuset/background/tasks
+    chown system system /dev/cpuset/system-background/tasks
     chmod 0664 /dev/cpuset/foreground/tasks
     chmod 0664 /dev/cpuset/foreground/boost/tasks
     chmod 0664 /dev/cpuset/background/tasks
+    chmod 0664 /dev/cpuset/system-background/tasks
     chmod 0664 /dev/cpuset/tasks
 
 
@@ -383,9 +399,6 @@
     # symlink to bugreport storage location
     symlink /data/data/com.android.shell/files/bugreports /data/bugreports
 
-    # Separate location for storing security policy files on data
-    mkdir /data/security 0711 system system
-
     # Create all remaining /data root dirs so that they are made through init
     # and get proper encryption policy installed
     mkdir /data/backup 0700 system system
@@ -397,9 +410,6 @@
 
     setusercryptopolicies /data/user
 
-    # Reload policy from /data/security if present.
-    setprop selinux.reload_policy 1
-
     # Set SELinux security contexts on upgrade or policy update.
     restorecon_recursive /data
 
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ff25ac2..0ca38b9 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,4 +5,4 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
-    writepid /dev/cpuset/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 4e702a1..1646c0f 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,10 +5,10 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
-    writepid /dev/cpuset/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
 
 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
-    writepid /dev/cpuset/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5497524..b477c8e 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,4 +5,4 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
-    writepid /dev/cpuset/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 692af99..633a981 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,10 +5,10 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
-    writepid /dev/cpuset/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
 
 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
-    writepid /dev/cpuset/foreground/tasks
+    writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 45efe36..f862561 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -1214,7 +1214,13 @@
     }
     out.fh = ptr_to_id(h);
     out.open_flags = 0;
+
+#ifdef FUSE_STACKED_IO
+    out.lower_fd = h->fd;
+#else
     out.padding = 0;
+#endif
+
     fuse_reply(fuse, hdr->unique, &out, sizeof(out));
     return NO_STATUS;
 }
@@ -1378,7 +1384,13 @@
     }
     out.fh = ptr_to_id(h);
     out.open_flags = 0;
+
+#ifdef FUSE_STACKED_IO
+    out.lower_fd = -1;
+#else
     out.padding = 0;
+#endif
+
     fuse_reply(fuse, hdr->unique, &out, sizeof(out));
     return NO_STATUS;
 }
@@ -1460,6 +1472,11 @@
     out.major = FUSE_KERNEL_VERSION;
     out.max_readahead = req->max_readahead;
     out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+
+#ifdef FUSE_STACKED_IO
+    out.flags |= FUSE_STACKED_IO;
+#endif
+
     out.max_background = 32;
     out.congestion_threshold = 32;
     out.max_write = MAX_WRITE;
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index fc2898e..b1cdb60 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -38,7 +38,6 @@
     nandread \
     newfs_msdos \
     ps \
-    prlimit \
     sendevent \
     start \
     stop \
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 5b98a01..27ea9e8 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -695,7 +695,7 @@
                                                     (u_int)tm->tm_min));
                 mk4(bsx->volid, x);
                 mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
-                sprintf(buf, "FAT%u", fat);
+                snprintf(buf, sizeof(buf), "FAT%u", fat);
                 setstr(bsx->type, buf, sizeof(bsx->type));
                 if (!opt_B) {
                     x1 += sizeof(struct bsx);
diff --git a/toolbox/prlimit.c b/toolbox/prlimit.c
deleted file mode 100644
index 8cf202a..0000000
--- a/toolbox/prlimit.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-static void
-usage(const char *s)
-{
-    fprintf(stderr, "usage: %s pid resource cur max\n", s);
-    exit(EXIT_FAILURE);
-}
-
-int prlimit_main(int argc, char *argv[])
-{
-    pid_t pid;
-    struct rlimit64 rl;
-    int resource;
-    int rc;
-
-    if (argc != 5)
-        usage(*argv);
-
-    if (sscanf(argv[1], "%d", &pid) != 1)
-        usage(*argv);
-
-    if (sscanf(argv[2], "%d", &resource) != 1)
-        usage(*argv);
-
-    if (sscanf(argv[3], "%llu", &rl.rlim_cur) != 1)
-        usage(*argv);
-
-    if (sscanf(argv[4], "%llu", &rl.rlim_max) != 1)
-        usage(*argv);
-
-    printf("setting resource %d of pid %d to [%llu,%llu]\n", resource, pid,
-            rl.rlim_cur, rl.rlim_max);
-    rc = prlimit64(pid, resource, &rl, NULL);
-    if (rc < 0) {
-        perror("prlimit");
-        exit(EXIT_FAILURE);
-    }
-
-    return 0;
-}
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 7e70c71..d366f3e 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -57,16 +57,16 @@
     int prio, nice, rtprio, sched, psr;
     struct passwd *pw;
 
-    sprintf(statline, "/proc/%d", tid ? tid : pid);
+    snprintf(statline, sizeof(statline), "/proc/%d", tid ? tid : pid);
     stat(statline, &stats);
 
     if(tid) {
-        sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+        snprintf(statline, sizeof(statline), "/proc/%d/task/%d/stat", pid, tid);
         cmdline[0] = 0;
         snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
     } else {
-        sprintf(statline, "/proc/%d/stat", pid);
-        sprintf(cmdline, "/proc/%d/cmdline", pid);
+        snprintf(statline, sizeof(statline), "/proc/%d/stat", pid);
+        snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid);
         snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
         int fd = open(cmdline, O_RDONLY);
         if(fd == 0) {
@@ -149,7 +149,7 @@
 
     pw = getpwuid(stats.st_uid);
     if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
-        sprintf(user,"%d",(int)stats.st_uid);
+        snprintf(user,sizeof(user),"%d",(int)stats.st_uid);
     } else {
         strcpy(user,pw->pw_name);
     }
@@ -208,7 +208,7 @@
     int fd, r;
     char exeline[1024];
 
-    sprintf(exeline, "/proc/%d/exe", pid);
+    snprintf(exeline, sizeof(exeline), "/proc/%d/exe", pid);
     fd = open(exeline, O_RDONLY);
     if(fd == 0) {
         printf("    ");
@@ -243,7 +243,7 @@
     DIR *d;
     struct dirent *de;
 
-    sprintf(tmp,"/proc/%d/task",pid);
+    snprintf(tmp,sizeof(tmp),"/proc/%d/task",pid);
     d = opendir(tmp);
     if(d == 0) return;
 
diff --git a/toolbox/top.c b/toolbox/top.c
index 6fda132..003f4c9 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -258,29 +258,29 @@
 
             proc->pid = proc->tid = pid;
 
-            sprintf(filename, "/proc/%d/stat", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
             read_stat(filename, proc);
 
-            sprintf(filename, "/proc/%d/cmdline", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
             read_cmdline(filename, proc);
 
-            sprintf(filename, "/proc/%d/status", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
             read_status(filename, proc);
 
             read_policy(pid, proc);
 
             proc->num_threads = 0;
         } else {
-            sprintf(filename, "/proc/%d/cmdline", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
             read_cmdline(filename, &cur_proc);
 
-            sprintf(filename, "/proc/%d/status", pid);
+            snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
             read_status(filename, &cur_proc);
 
             proc = NULL;
         }
 
-        sprintf(filename, "/proc/%d/task", pid);
+        snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
         task_dir = opendir(filename);
         if (!task_dir) continue;
 
@@ -295,7 +295,7 @@
 
                 proc->pid = pid; proc->tid = tid;
 
-                sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+                snprintf(filename, sizeof(filename), "/proc/%d/task/%d/stat", pid, tid);
                 read_stat(filename, proc);
 
                 read_policy(tid, proc);
@@ -484,7 +484,7 @@
         if (user && user->pw_name) {
             user_str = user->pw_name;
         } else {
-            snprintf(user_buf, 20, "%d", proc->uid);
+            snprintf(user_buf, sizeof(user_buf), "%d", proc->uid);
             user_str = user_buf;
         }
         if (!threads) {
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 55d5ee6..1fb34c9 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <sys/uio.h>
 
 #include <trusty/tipc.h>
 
@@ -80,6 +81,8 @@
 "   ta2ta-ipc    - execute TA to TA unittest\n"
 "   dev-uuid     - print device uuid\n"
 "   ta-access    - test ta-access flags\n"
+"   writev       - writev test\n"
+"   readv        - readv test\n"
 "\n"
 ;
 
@@ -93,7 +96,7 @@
 {
 	fprintf (stderr, usage, prog);
 	if (verbose)
-		fprintf (stderr, usage_long);
+		fprintf (stderr, "%s", usage_long);
 	exit(code);
 }
 
@@ -692,6 +695,171 @@
 }
 
 
+static int writev_test(uint repeat, uint msgsz, bool var)
+{
+	uint i;
+	ssize_t rc;
+	size_t  msg_len;
+	int  echo_fd = -1;
+	char tx0_buf[msgsz];
+	char tx1_buf[msgsz];
+	char rx_buf [msgsz];
+	struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+
+	if (!opt_silent) {
+		printf("%s: repeat %u: msgsz %u: variable %s\n",
+			__func__, repeat, msgsz, var ? "true" : "false");
+	}
+
+	echo_fd = tipc_connect(dev_name, echo_name);
+	if (echo_fd < 0) {
+		fprintf(stderr, "Failed to connect to service\n");
+		return echo_fd;
+	}
+
+	for (i = 0; i < repeat; i++) {
+
+		msg_len = msgsz;
+		if (opt_variable && msgsz) {
+			msg_len = rand() % msgsz;
+		}
+
+		iovs[0].iov_len = msg_len / 3;
+		iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+		memset(tx0_buf, i + 1, iovs[0].iov_len);
+		memset(tx1_buf, i + 2, iovs[1].iov_len);
+		memset(rx_buf,  i + 3, sizeof(rx_buf));
+
+		rc = writev(echo_fd, iovs, 2);
+		if (rc < 0) {
+			perror("writev_test: writev");
+			break;
+		}
+
+		if ((size_t)rc != msg_len) {
+			fprintf(stderr,
+				"%s: %s: data size mismatch (%zd vs. %zd)\n",
+				__func__, "writev", (size_t)rc, msg_len);
+			break;
+		}
+
+		rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+		if (rc < 0) {
+			perror("writev_test: read");
+			break;
+		}
+
+		if ((size_t)rc != msg_len) {
+			fprintf(stderr,
+				"%s: %s: data size mismatch (%zd vs. %zd)\n",
+				__func__, "read", (size_t)rc, msg_len);
+			break;
+		}
+
+		if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+			fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+			break;
+		}
+
+		if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+			fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+			break;
+		}
+	}
+
+	tipc_close(echo_fd);
+
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	return 0;
+}
+
+static int readv_test(uint repeat, uint msgsz, bool var)
+{
+	uint i;
+	ssize_t rc;
+	size_t  msg_len;
+	int  echo_fd = -1;
+	char tx_buf [msgsz];
+	char rx0_buf[msgsz];
+	char rx1_buf[msgsz];
+	struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+
+	if (!opt_silent) {
+		printf("%s: repeat %u: msgsz %u: variable %s\n",
+			__func__, repeat, msgsz, var ? "true" : "false");
+	}
+
+	echo_fd = tipc_connect(dev_name, echo_name);
+	if (echo_fd < 0) {
+		fprintf(stderr, "Failed to connect to service\n");
+		return echo_fd;
+	}
+
+	for (i = 0; i < repeat; i++) {
+
+		msg_len = msgsz;
+		if (opt_variable && msgsz) {
+			msg_len = rand() % msgsz;
+		}
+
+		iovs[0].iov_len = msg_len / 3;
+		iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+		memset(tx_buf,  i + 1, sizeof(tx_buf));
+		memset(rx0_buf, i + 2, iovs[0].iov_len);
+		memset(rx1_buf, i + 3, iovs[1].iov_len);
+
+		rc = write(echo_fd, tx_buf, msg_len);
+		if (rc < 0) {
+			perror("readv_test: write");
+			break;
+		}
+
+		if ((size_t)rc != msg_len) {
+			fprintf(stderr,
+				"%s: %s: data size mismatch (%zd vs. %zd)\n",
+				__func__, "write", (size_t)rc, msg_len);
+			break;
+		}
+
+		rc = readv(echo_fd, iovs, 2);
+		if (rc < 0) {
+			perror("readv_test: readv");
+			break;
+		}
+
+		if ((size_t)rc != msg_len) {
+			fprintf(stderr,
+				"%s: %s: data size mismatch (%zd vs. %zd)\n",
+				__func__, "write", (size_t)rc, msg_len);
+			break;
+		}
+
+		if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+			fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+			break;
+		}
+
+		if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+			fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+			break;
+		}
+	}
+
+	tipc_close(echo_fd);
+
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	return 0;
+}
+
+
 int main(int argc, char **argv)
 {
 	int rc = 0;
@@ -735,6 +903,10 @@
 		rc = dev_uuid_test();
 	} else if (strcmp(test_name, "ta-access") == 0) {
 		rc = ta_access_test();
+	} else if (strcmp(test_name, "writev") == 0) {
+		rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+	} else if (strcmp(test_name, "readv") == 0) {
+		rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
 	} else {
 		fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
 		print_usage_and_exit(argv[0], EXIT_FAILURE, true);
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
new file mode 100644
index 0000000..18c54d5
--- /dev/null
+++ b/trusty/nvram/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2016 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# nvram.trusty is the Trusty NVRAM HAL module.
+include $(CLEAR_VARS)
+LOCAL_MODULE := nvram.trusty
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+	module.c \
+	trusty_nvram_implementation.cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
+LOCAL_STATIC_LIBRARIES := libnvram-hal
+LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c
new file mode 100644
index 0000000..06819c0
--- /dev/null
+++ b/trusty/nvram/module.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/nvram.h>
+
+// This function is defined in trusty_nvram_implementation.cpp.
+int trusty_nvram_open(const hw_module_t* module,
+                      const char* device_id,
+                      hw_device_t** device_ptr);
+
+static struct hw_module_methods_t nvram_module_methods = {
+    .open = trusty_nvram_open,
+};
+
+struct nvram_module HAL_MODULE_INFO_SYM
+    __attribute__((visibility("default"))) = {
+        .common = {.tag = HARDWARE_MODULE_TAG,
+                   .module_api_version = NVRAM_MODULE_API_VERSION_0_1,
+                   .hal_api_version = HARDWARE_HAL_API_VERSION,
+                   .id = NVRAM_HARDWARE_MODULE_ID,
+                   .name = "Trusty NVRAM HAL",
+                   .author = "The Android Open Source Project",
+                   .methods = &nvram_module_methods,
+                   .dso = 0,
+                   .reserved = {}},
+};
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
new file mode 100644
index 0000000..39496b4
--- /dev/null
+++ b/trusty/nvram/trusty_nvram_implementation.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <hardware/nvram.h>
+#include <trusty/tipc.h>
+
+#define LOG_TAG "TrustyNVRAM"
+#include <log/log.h>
+
+#include <nvram/hal/nvram_device_adapter.h>
+#include <nvram/messages/blob.h>
+#include <nvram/messages/nvram_messages.h>
+
+namespace {
+
+// Character device to open for Trusty IPC connections.
+const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+
+// App identifier of the NVRAM app.
+const char kTrustyNvramAppId[] = "com.android.trusty.nvram";
+
+// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It
+// serializes the request objects, sends it to the Trusty app and finally reads
+// back the result and decodes it.
+class TrustyNvramImplementation : public nvram::NvramImplementation {
+ public:
+  ~TrustyNvramImplementation() override;
+
+  void Execute(const nvram::Request& request,
+               nvram::Response* response) override;
+
+ private:
+  // Connects the IPC channel to the Trusty app if it is not already open.
+  // Returns true if the channel is open, false on errors.
+  bool Connect();
+
+  // Dispatches a command to the trust app. Returns true if successful (note
+  // that the response may still indicate an error on the Trusty side), false if
+  // there are any I/O or encoding/decoding errors.
+  bool SendRequest(const nvram::Request& request,
+                   nvram::Response* response);
+
+  // The file descriptor for the IPC connection to the Trusty app.
+  int tipc_nvram_fd_ = -1;
+
+  // Response buffer. This puts a hard size limit on the responses from the
+  // Trusty app. 4096 matches the maximum IPC message size currently supported
+  // by Trusty.
+  uint8_t response_buffer_[4096];
+};
+
+TrustyNvramImplementation::~TrustyNvramImplementation() {
+  if (tipc_nvram_fd_ != -1) {
+    tipc_close(tipc_nvram_fd_);
+    tipc_nvram_fd_ = -1;
+  }
+}
+
+void TrustyNvramImplementation::Execute(const nvram::Request& request,
+                                        nvram::Response* response) {
+  if (!SendRequest(request, response)) {
+    response->result = NV_RESULT_INTERNAL_ERROR;
+  }
+}
+
+bool TrustyNvramImplementation::Connect() {
+  if (tipc_nvram_fd_ != -1) {
+    return true;
+  }
+
+  int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId);
+  if (rc < 0) {
+    ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc));
+    return false;
+  }
+
+  tipc_nvram_fd_ = rc;
+  return true;
+}
+
+bool TrustyNvramImplementation::SendRequest(const nvram::Request& request,
+                                            nvram::Response* response) {
+  if (!Connect()) {
+    return false;
+  }
+
+  nvram::Blob request_buffer;
+  if (!nvram::Encode(request, &request_buffer)) {
+    ALOGE("Failed to encode NVRAM request.\n");
+    return false;
+  }
+
+  ssize_t rc =
+      write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size());
+  if (rc < 0) {
+    ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc));
+    return false;
+  }
+  if (static_cast<size_t>(rc) != request_buffer.size()) {
+    ALOGE("Failed to send full request buffer: %zd\n", rc);
+    return false;
+  }
+
+  rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_));
+  if (rc < 0) {
+    ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc));
+    return false;
+  }
+
+  if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) {
+    ALOGE("NVRAM response exceeds response buffer size.\n");
+    return false;
+  }
+
+  if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) {
+    ALOGE("Failed to decode NVRAM response.\n");
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+extern "C" int trusty_nvram_open(const hw_module_t* module,
+                                 const char* device_id,
+                                 hw_device_t** device_ptr) {
+  if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) {
+    return -EINVAL;
+  }
+
+  nvram::NvramDeviceAdapter* adapter =
+      new nvram::NvramDeviceAdapter(module, new TrustyNvramImplementation);
+  *device_ptr = adapter->as_device();
+  return 0;
+}