Merge changes I4a8aa840,I9708f2a3

* changes:
  adb: check for an error response from adbd between each write.
  adbd: restore the old error handling behavior.
diff --git a/adb/.clang-format b/adb/.clang-format
index 6737535..fc4eb1b 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,7 @@
 AllowShortBlocksOnASingleLine: false
 AllowShortFunctionsOnASingleLine: false
 
+AccessModifierOffset: -2
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/adb/Android.mk b/adb/Android.mk
index 5b1e3bb..e0997ea 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -151,7 +151,7 @@
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := libadbd
-LOCAL_SHARED_LIBRARIES := libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
 # libdiagnose_usb
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 337ba7c..635af6c 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -16,7 +16,6 @@
 
 #include "android-base/logging.h"
 #include "android-base/test_utils.h"
-#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
 
 #include <fcntl.h>
 #include <stdio.h>
@@ -27,6 +26,9 @@
 #if defined(_WIN32)
 #include <windows.h>
 #include <direct.h>
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
 #endif
 
 #include <string>
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 22a67bb..8282b40 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -50,7 +50,7 @@
   SetStorePath(BOOTSTAT_DATA_DIR);
 }
 
-void BootEventRecordStore::AddBootEvent(const std::string& name) {
+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";
@@ -58,15 +58,15 @@
 
   // Cast intentionally rounds down.
   int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-  AddBootEventWithValue(name, uptime);
+  AddBootEventWithValue(event, uptime);
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
 // attribute to store the value associated with a boot event in order to
 // optimize on-disk size requirements and small-file thrashing.
 void BootEventRecordStore::AddBootEventWithValue(
-    const std::string& name, int32_t value) {
-  std::string record_path = GetBootEventPath(name);
+    const std::string& event, int32_t value) {
+  std::string record_path = GetBootEventPath(event);
   if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
     PLOG(ERROR) << "Failed to create " << record_path;
   }
@@ -86,6 +86,22 @@
   }
 }
 
+bool BootEventRecordStore::GetBootEvent(
+    const std::string& event, BootEventRecord* record) const {
+  CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
+  CHECK(!event.empty());
+
+  const std::string record_path = GetBootEventPath(event);
+  int32_t uptime;
+  if (!ParseRecordEventTime(record_path, &uptime)) {
+    LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+    return false;
+  }
+
+  *record = std::make_pair(event, uptime);
+  return true;
+}
+
 std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
     GetAllBootEvents() const {
   std::vector<BootEventRecord> events;
@@ -104,14 +120,13 @@
     }
 
     const std::string event = entry->d_name;
-    const std::string record_path = GetBootEventPath(event);
-    int32_t uptime;
-    if (!ParseRecordEventTime(record_path, &uptime)) {
-      LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+    BootEventRecord record;
+    if (!GetBootEvent(event, &record)) {
+      LOG(ERROR) << "Failed to parse boot time event: " << event;
       continue;
     }
 
-    events.push_back(std::make_pair(event, uptime));
+    events.push_back(record);
   }
 
   return events;
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index d1b7835..4d5deee 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -34,12 +34,16 @@
 
   BootEventRecordStore();
 
-  // Persists the boot event named |name| in the record store.
-  void AddBootEvent(const std::string& name);
+  // Persists the boot |event| in the record store.
+  void AddBootEvent(const std::string& event);
 
-  // Persists the boot event named |name| with the associated |value| in the
-  // record store.
-  void AddBootEventWithValue(const std::string& name, int32_t value);
+  // Persists the boot |event| with the associated |value| in the record store.
+  void AddBootEventWithValue(const std::string& event, int32_t value);
+
+  // Queries the named boot |event|. |record| must be non-null. |record|
+  // contains the boot event data on success. Returns true iff the query is
+  // successful.
+  bool GetBootEvent(const std::string& event, BootEventRecord* record) const;
 
   // Returns a list of all of the boot events persisted in the record store.
   std::vector<BootEventRecord> GetAllBootEvents() const;
@@ -50,6 +54,7 @@
   FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
   FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
   FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
+  FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
 
   // Sets the filesystem path of the record store.
   void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 3e6d706..0d7bbb0 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -165,4 +165,27 @@
   ASSERT_EQ(1U, events.size());
   EXPECT_EQ("permian", events[0].first);
   EXPECT_EQ(42, events[0].second);
+}
+
+TEST_F(BootEventRecordStoreTest, GetBootEvent) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  // Event does not exist.
+  BootEventRecordStore::BootEventRecord record;
+  bool result = store.GetBootEvent("nonexistent", &record);
+  EXPECT_EQ(false, result);
+
+  // Empty path.
+  EXPECT_DEATH(store.GetBootEvent(std::string(), &record), std::string());
+
+  // Success case.
+  store.AddBootEventWithValue("carboniferous", 314);
+  result = store.GetBootEvent("carboniferous", &record);
+  EXPECT_EQ(true, result);
+  EXPECT_EQ("carboniferous", record.first);
+  EXPECT_EQ(314, record.second);
+
+  // Null |record|.
+  EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
 }
\ No newline at end of file
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 31c84c7..0c49f82 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include <cstddef>
 #include <cstdio>
+#include <ctime>
 #include <map>
 #include <memory>
 #include <string>
@@ -105,33 +106,39 @@
   return std::string(&temp[0], len);
 }
 
+constexpr int32_t kUnknownBootReason = 1;
+
 // A mapping from boot reason string, as read from the ro.boot.bootreason
 // system property, to a unique integer ID. Viewers of log data dashboards for
 // the boot_reason metric may refer to this mapping to discern the histogram
 // values.
-const std::map<std::string, int> kBootReasonMap = {
-  {"normal", 0},
-  {"recovery", 1},
-  {"reboot", 2},
-  {"PowerKey", 3},
-  {"hard_reset", 4},
-  {"kernel_panic", 5},
-  {"rpm_err", 6},
-  {"hw_reset", 7},
-  {"tz_err", 8},
-  {"adsp_err", 9},
-  {"modem_err", 10},
-  {"mba_err", 11},
-  {"Watchdog", 12},
-  {"Panic", 13},
+const std::map<std::string, int32_t> kBootReasonMap = {
+  {"unknown", kUnknownBootReason},
+  {"normal", 2},
+  {"recovery", 3},
+  {"reboot", 4},
+  {"PowerKey", 5},
+  {"hard_reset", 6},
+  {"kernel_panic", 7},
+  {"rpm_err", 8},
+  {"hw_reset", 9},
+  {"tz_err", 10},
+  {"adsp_err", 11},
+  {"modem_err", 12},
+  {"mba_err", 13},
+  {"Watchdog", 14},
+  {"Panic", 15},
+  {"power_key", 16},
+  {"power_on", 17},
+  {"Reboot", 18},
+  {"rtc", 19},
+  {"edl", 20},
 };
 
 // Converts a string value representing the reason the system booted to an
 // integer representation. This is necessary for logging the boot_reason metric
 // via Tron, which does not accept non-integer buckets in histograms.
 int32_t BootReasonStrToEnum(const std::string& boot_reason) {
-  static const int32_t kUnknownBootReason = -1;
-
   auto mapping = kBootReasonMap.find(boot_reason);
   if (mapping != kBootReasonMap.end()) {
     return mapping->second;
@@ -149,6 +156,32 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 }
 
+// Records two metrics related to the user resetting a device: the time at
+// which the device is reset, and the time since the user last reset the
+// device.  The former is only set once per-factory reset.
+void RecordFactoryReset() {
+  BootEventRecordStore boot_event_store;
+  BootEventRecordStore::BootEventRecord record;
+
+  time_t current_time_utc = time(nullptr);
+
+  // 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);
+    return;
+  }
+
+  // Calculate and record the difference in time between now and the
+  // factory_reset time.
+  time_t factory_reset_utc = record.second;
+  time_t time_since_factory_reset = difftime(current_time_utc,
+                                             factory_reset_utc);
+  boot_event_store.AddBootEventWithValue("time_since_factory_reset",
+                                         time_since_factory_reset);
+}
+
 }  // namespace
 
 int main(int argc, char **argv) {
@@ -159,12 +192,14 @@
 
   int option_index = 0;
   static const char boot_reason_str[] = "record_boot_reason";
+  static const char factory_reset_str[] = "record_factory_reset";
   static const struct option long_options[] = {
     { "help",            no_argument,       NULL,   'h' },
     { "log",             no_argument,       NULL,   'l' },
     { "print",           no_argument,       NULL,   'p' },
     { "record",          required_argument, NULL,   'r' },
     { boot_reason_str,   no_argument,       NULL,   0 },
+    { factory_reset_str, no_argument,       NULL,   0 },
     { NULL,              0,                 NULL,   0 }
   };
 
@@ -176,6 +211,8 @@
         const std::string option_name = long_options[option_index].name;
         if (option_name == boot_reason_str) {
           RecordBootReason();
+        } else if (option_name == factory_reset_str) {
+          RecordFactoryReset();
         } else {
           LOG(ERROR) << "Invalid option: " << option_name;
         }
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 8efbacc..294bda9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -678,13 +678,6 @@
   // Ignore failed writes to closed sockets
   signal(SIGPIPE, SIG_IGN);
 
-  int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
-  if (logsocket < 0) {
-    logsocket = -1;
-  } else {
-    fcntl(logsocket, F_SETFD, FD_CLOEXEC);
-  }
-
   struct sigaction act;
   act.sa_handler = SIG_DFL;
   sigemptyset(&act.sa_mask);
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
index 751c4fb..e5acd17 100644
--- a/debuggerd/getevent.cpp
+++ b/debuggerd/getevent.cpp
@@ -26,6 +26,7 @@
 #include <sys/poll.h>
 #include <linux/input.h>
 #include <errno.h>
+#include <memory>
 #include <cutils/log.h>
 
 static struct pollfd* ufds;
@@ -143,22 +144,20 @@
 static int scan_dir(const char* dirname) {
   char devname[PATH_MAX];
   char* filename;
-  DIR* dir;
   struct dirent* de;
-  dir = opendir(dirname);
+  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
   if (dir == NULL)
     return -1;
   strcpy(devname, dirname);
   filename = devname + strlen(devname);
   *filename++ = '/';
-  while ((de = readdir(dir))) {
+  while ((de = readdir(dir.get()))) {
     if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
         (de->d_name[1] == '.' && de->d_name[2] == '\0'))
       continue;
     strcpy(filename, de->d_name);
     open_device(devname);
   }
-  closedir(dir);
   return 0;
 }
 
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index da2880a..e42c4e1 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -28,6 +28,7 @@
 
 #include "tcp.h"
 
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 
 namespace tcp {
@@ -98,7 +99,8 @@
         return false;
     }
 
-    char buffer[kHandshakeLength];
+    char buffer[kHandshakeLength + 1];
+    buffer[kHandshakeLength] = '\0';
     if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {
         *error = android::base::StringPrintf(
                 "No initialization message received (%s). Target may not support TCP fastboot",
@@ -111,9 +113,10 @@
         return false;
     }
 
-    if (memcmp(buffer + 2, "01", 2) != 0) {
+    int version = 0;
+    if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
         *error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)",
-                                             std::string(buffer + 2, 2).c_str(), kProtocolVersion);
+                                             buffer + 2, kProtocolVersion);
         return false;
     }
 
diff --git a/fastboot/tcp_test.cpp b/fastboot/tcp_test.cpp
index 7d80d76..6e867ae 100644
--- a/fastboot/tcp_test.cpp
+++ b/fastboot/tcp_test.cpp
@@ -42,6 +42,16 @@
     EXPECT_EQ("", error);
 }
 
+TEST(TcpConnectTest, TestNewerVersionSuccess) {
+    std::unique_ptr<SocketMock> mock(new SocketMock);
+    mock->ExpectSend("FB01");
+    mock->AddReceive("FB99");
+
+    std::string error;
+    EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));
+    EXPECT_EQ("", error);
+}
+
 TEST(TcpConnectTest, TestSendFailure) {
     std::unique_ptr<SocketMock> mock(new SocketMock);
     mock->ExpectSendFailure("FB01");
@@ -74,11 +84,11 @@
 TEST(TcpConnectTest, TestUnknownVersionFailure) {
     std::unique_ptr<SocketMock> mock(new SocketMock);
     mock->ExpectSend("FB01");
-    mock->AddReceive("FB02");
+    mock->AddReceive("FB00");
 
     std::string error;
     EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
-    EXPECT_EQ("Unknown TCP protocol version 02 (host version 01)", error);
+    EXPECT_EQ("Unknown TCP protocol version 00 (host version 01)", error);
 }
 
 // Fixture to configure a SocketMock for a successful TCP connection.
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 02ffcd9..d4824fb 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -145,7 +145,7 @@
     int in, out;
     unsigned i;
     unsigned e;
-    
+
     if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
         return -1;
     dev = (struct usb_device_descriptor *)ptr;
@@ -333,15 +333,14 @@
     char desc[1024];
     int n, in, out, ifc;
 
-    DIR *busdir;
     struct dirent *de;
     int fd;
     int writable;
 
-    busdir = opendir(base);
+    std::unique_ptr<DIR, decltype(&closedir)> busdir(opendir(base), closedir);
     if (busdir == 0) return 0;
 
-    while ((de = readdir(busdir)) && (usb == nullptr)) {
+    while ((de = readdir(busdir.get())) && (usb == nullptr)) {
         if (badname(de->d_name)) continue;
 
         if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
@@ -377,7 +376,6 @@
             }
         }
     }
-    closedir(busdir);
 
     return usb;
 }
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index c73045d..853bf0b 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -33,7 +33,7 @@
 
 static int format_ext4(char *fs_blkdev, char *fs_mnt_point)
 {
-    unsigned int nr_sec;
+    uint64_t dev_sz;
     int fd, rc = 0;
 
     if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
@@ -41,7 +41,7 @@
         return -1;
     }
 
-    if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+    if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
         ERROR("Cannot get block device size.  %s\n", strerror(errno));
         close(fd);
         return -1;
@@ -49,7 +49,7 @@
 
     /* Format the partition using the calculated length */
     reset_ext4fs_info();
-    info.len = ((off64_t)nr_sec * 512);
+    info.len = (off64_t)dev_sz;
 
     /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
     rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 27c985c..ddd9f1f 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -6,17 +6,36 @@
 LOCAL_SRC_FILES := healthd_board_default.cpp
 LOCAL_MODULE := libhealthd.default
 LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := BatteryMonitor.cpp
+LOCAL_MODULE := libbatterymonitor
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libutils
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 
+ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
+LOCAL_CHARGER_NO_UI := true
+endif
+ifdef BRILLO
+LOCAL_CHARGER_NO_UI := true
+endif
+
 LOCAL_SRC_FILES := \
 	healthd.cpp \
 	healthd_mode_android.cpp \
-	healthd_mode_charger.cpp \
-	BatteryMonitor.cpp \
 	BatteryPropertiesRegistrar.cpp
 
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_SRC_FILES += healthd_mode_charger.cpp
+endif
+
 LOCAL_MODULE := healthd
 LOCAL_MODULE_TAGS := optional
 LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -33,9 +52,19 @@
 LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
 endif
 
-LOCAL_C_INCLUDES := bootable/recovery
+ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+endif
 
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
+LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_STATIC_LIBRARIES += libminui libpng libz
+endif
+
+LOCAL_STATIC_LIBRARIES += libutils libcutils liblog libm libc
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
 LOCAL_STATIC_LIBRARIES += libsuspend
@@ -50,6 +79,7 @@
 include $(BUILD_EXECUTABLE)
 
 
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
 include $$(CLEAR_VARS)
 LOCAL_MODULE := system_core_charger_$(notdir $(1))
@@ -75,3 +105,4 @@
 
 _add-charger-image :=
 _img_modules :=
+endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 8f98f17..2022ae7 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -16,8 +16,8 @@
 
 #define LOG_TAG "healthd"
 
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
 
 #include <dirent.h>
 #include <errno.h>
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <memory>
 
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
@@ -56,6 +57,28 @@
     return -1;
 }
 
+static void initBatteryProperties(BatteryProperties* props) {
+    props->chargerAcOnline = false;
+    props->chargerUsbOnline = false;
+    props->chargerWirelessOnline = false;
+    props->maxChargingCurrent = 0;
+    props->batteryStatus = BATTERY_STATUS_UNKNOWN;
+    props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
+    props->batteryPresent = false;
+    props->batteryLevel = 0;
+    props->batteryVoltage = 0;
+    props->batteryTemperature = 0;
+    props->batteryCurrent = 0;
+    props->batteryCycleCount = 0;
+    props->batteryFullCharge = 0;
+    props->batteryTechnology.clear();
+}
+
+BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
+    mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
+    initBatteryProperties(&props);
+}
+
 int BatteryMonitor::getBatteryStatus(const char* status) {
     int ret;
     struct sysfsStringEnumMap batteryStatusMap[] = {
@@ -184,12 +207,7 @@
 bool BatteryMonitor::update(void) {
     bool logthis;
 
-    props.chargerAcOnline = false;
-    props.chargerUsbOnline = false;
-    props.chargerWirelessOnline = false;
-    props.batteryStatus = BATTERY_STATUS_UNKNOWN;
-    props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
-    props.maxChargingCurrent = 0;
+    initBatteryProperties(&props);
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -439,13 +457,13 @@
     char pval[PROPERTY_VALUE_MAX];
 
     mHealthdConfig = hc;
-    DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
     if (dir == NULL) {
         KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
     } else {
         struct dirent* entry;
 
-        while ((entry = readdir(dir))) {
+        while ((entry = readdir(dir.get()))) {
             const char* name = entry->d_name;
 
             if (!strcmp(name, ".") || !strcmp(name, ".."))
@@ -583,7 +601,6 @@
                 break;
             }
         }
-        closedir(dir);
     }
 
     // This indicates that there is no charger driver registered.
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index d3a89d7..5d1fa52 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -26,7 +26,7 @@
 #include <utils/Mutex.h>
 #include <utils/String16.h>
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 
 namespace android {
 
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 85888c3..20a6bf6 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -17,8 +17,8 @@
 #define LOG_TAG "healthd"
 #define KLOG_LEVEL 6
 
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
 
 #include <errno.h>
 #include <libgen.h>
@@ -109,10 +109,17 @@
 };
 
 static struct healthd_mode_ops charger_ops = {
+#ifdef CHARGER_NO_UI
+    .init = healthd_mode_nop_init,
+    .preparetowait = healthd_mode_nop_preparetowait,
+    .heartbeat = healthd_mode_nop_heartbeat,
+    .battery_update = healthd_mode_nop_battery_update,
+#else
     .init = healthd_mode_charger_init,
     .preparetowait = healthd_mode_charger_preparetowait,
     .heartbeat = healthd_mode_charger_heartbeat,
     .battery_update = healthd_mode_charger_battery_update,
+#endif
 };
 
 static struct healthd_mode_ops recovery_ops = {
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
index ed4ddb4..eb55773 100644
--- a/healthd/healthd_board_default.cpp
+++ b/healthd/healthd_board_default.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <healthd.h>
+#include <healthd/healthd.h>
 
 void healthd_board_init(struct healthd_config*)
 {
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index 0a64099..323ef52 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "healthd-android"
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 #include "BatteryPropertiesRegistrar.h"
 
 #include <binder/IPCThreadState.h>
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 46bad4e..5846626 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -46,7 +46,7 @@
 
 #include "minui/minui.h"
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 
 char *locale;
 
diff --git a/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
similarity index 97%
rename from healthd/BatteryMonitor.h
rename to healthd/include/healthd/BatteryMonitor.h
index a61171f..31c4e09 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -22,7 +22,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 
 namespace android {
 
@@ -37,6 +37,7 @@
         ANDROID_POWER_SUPPLY_TYPE_BATTERY
     };
 
+    BatteryMonitor();
     void init(struct healthd_config *hc);
     bool update(void);
     status_t getProperty(int id, struct BatteryProperty *val);
diff --git a/healthd/healthd.h b/healthd/include/healthd/healthd.h
similarity index 100%
rename from healthd/healthd.h
rename to healthd/include/healthd/healthd.h
diff --git a/include/utils/String16.h b/include/utils/String16.h
index 9a67c7a..4a5874a 100644
--- a/include/utils/String16.h
+++ b/include/utils/String16.h
@@ -17,8 +17,10 @@
 #ifndef ANDROID_STRING16_H
 #define ANDROID_STRING16_H
 
+#include <string> // for std::string
+
 #include <utils/Errors.h>
-#include <utils/Unicode.h>
+#include <utils/String8.h>
 #include <utils/TypeHelpers.h>
 
 // ---------------------------------------------------------------------------
@@ -62,9 +64,10 @@
     explicit                    String16(const char* o, size_t len);
 
                                 ~String16();
-    
+
     inline  const char16_t*     string() const;
-    
+
+    static inline std::string   std_string(const String16& str);
             size_t              size() const;
             void                setTo(const String16& other);
             status_t            setTo(const char16_t* other);
@@ -72,12 +75,12 @@
             status_t            setTo(const String16& other,
                                       size_t len,
                                       size_t begin=0);
-    
+
             status_t            append(const String16& other);
             status_t            append(const char16_t* other, size_t len);
-            
+
     inline  String16&           operator=(const String16& other);
-    
+
     inline  String16&           operator+=(const String16& other);
     inline  String16            operator+(const String16& other) const;
 
@@ -90,7 +93,7 @@
 
             bool                startsWith(const String16& prefix) const;
             bool                startsWith(const char16_t* prefix) const;
-            
+
             status_t            makeLower();
 
             status_t            replaceAll(char16_t replaceThis,
@@ -106,16 +109,16 @@
     inline  bool                operator!=(const String16& other) const;
     inline  bool                operator>=(const String16& other) const;
     inline  bool                operator>(const String16& other) const;
-    
+
     inline  bool                operator<(const char16_t* other) const;
     inline  bool                operator<=(const char16_t* other) const;
     inline  bool                operator==(const char16_t* other) const;
     inline  bool                operator!=(const char16_t* other) const;
     inline  bool                operator>=(const char16_t* other) const;
     inline  bool                operator>(const char16_t* other) const;
-    
+
     inline                      operator const char16_t*() const;
-    
+
 private:
             const char16_t*     mString;
 };
@@ -142,6 +145,11 @@
     return mString;
 }
 
+inline std::string String16::std_string(const String16& str)
+{
+    return std::string(String8(str).string());
+}
+
 inline String16& String16::operator=(const String16& other)
 {
     setTo(other);
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 2a75b98..1d12994 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_STRING8_H
 #define ANDROID_STRING8_H
 
+#include <string> // for std::string
+
 #include <utils/Errors.h>
 #include <utils/Unicode.h>
 #include <utils/TypeHelpers.h>
@@ -49,7 +51,7 @@
                                 String8(const String8& o);
     explicit                    String8(const char* o);
     explicit                    String8(const char* o, size_t numChars);
-    
+
     explicit                    String8(const String16& o);
     explicit                    String8(const char16_t* o);
     explicit                    String8(const char16_t* o, size_t numChars);
@@ -63,12 +65,13 @@
     static String8              formatV(const char* fmt, va_list args);
 
     inline  const char*         string() const;
+    static inline std::string   std_string(const String8& str);
     inline  size_t              size() const;
     inline  size_t              bytes() const;
     inline  bool                isEmpty() const;
-    
+
             size_t              length() const;
-    
+
             void                clear();
 
             void                setTo(const String8& other);
@@ -95,10 +98,10 @@
 
     inline  String8&            operator=(const String8& other);
     inline  String8&            operator=(const char* other);
-    
+
     inline  String8&            operator+=(const String8& other);
     inline  String8             operator+(const String8& other) const;
-    
+
     inline  String8&            operator+=(const char* other);
     inline  String8             operator+(const char* other) const;
 
@@ -110,20 +113,20 @@
     inline  bool                operator!=(const String8& other) const;
     inline  bool                operator>=(const String8& other) const;
     inline  bool                operator>(const String8& other) const;
-    
+
     inline  bool                operator<(const char* other) const;
     inline  bool                operator<=(const char* other) const;
     inline  bool                operator==(const char* other) const;
     inline  bool                operator!=(const char* other) const;
     inline  bool                operator>=(const char* other) const;
     inline  bool                operator>(const char* other) const;
-    
+
     inline                      operator const char*() const;
-    
+
             char*               lockBuffer(size_t size);
             void                unlockBuffer();
             status_t            unlockBuffer(size_t size);
-            
+
             // return the index of the first byte of other in this at or after
             // start, or -1 if not found
             ssize_t             find(const char* other, size_t start = 0) const;
@@ -261,6 +264,11 @@
     return mString;
 }
 
+inline std::string String8::std_string(const String8& str)
+{
+    return std::string(str.string());
+}
+
 inline size_t String8::size() const
 {
     return length();
diff --git a/init/devices.cpp b/init/devices.cpp
index 39cd706..557a6ac 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -31,6 +31,8 @@
 #include <sys/un.h>
 #include <linux/netlink.h>
 
+#include <memory>
+
 #include <selinux/selinux.h>
 #include <selinux/label.h>
 #include <selinux/android.h>
@@ -957,10 +959,9 @@
 
 static void coldboot(const char *path)
 {
-    DIR *d = opendir(path);
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
     if(d) {
-        do_coldboot(d);
-        closedir(d);
+        do_coldboot(d.get());
     }
 }
 
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 7d829fe..b975db9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -404,17 +404,16 @@
   char task_path[128];
   snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
 
-  DIR* tasks_dir = opendir(task_path);
+  std::unique_ptr<DIR, decltype(&closedir)> tasks_dir(opendir(task_path), closedir);
   ASSERT_TRUE(tasks_dir != nullptr);
   struct dirent* entry;
-  while ((entry = readdir(tasks_dir)) != nullptr) {
+  while ((entry = readdir(tasks_dir.get())) != nullptr) {
     char* end;
     pid_t tid = strtoul(entry->d_name, &end, 10);
     if (*end == '\0') {
       threads->push_back(tid);
     }
   }
-  closedir(tasks_dir);
 }
 
 TEST(libbacktrace, ptrace_threads) {
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index 659821c..22ca706 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -15,7 +15,7 @@
  */
 
 #define LOG_TAG "Properties_test"
-#include <utils/Log.h>
+#include <cutils/log.h>
 #include <gtest/gtest.h>
 
 #include <cutils/properties.h>
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
index 624e385..a49ce58 100644
--- a/libdiskconfig/Android.mk
+++ b/libdiskconfig/Android.mk
@@ -13,6 +13,8 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
 LOCAL_CFLAGS := -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_SHARED_LIBRARY)
 
 ifeq ($(HOST_OS),linux)
@@ -21,5 +23,7 @@
 LOCAL_MODULE := libdiskconfig_host
 LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_HOST_STATIC_LIBRARY)
 endif # HOST_OS == linux
diff --git a/include/diskconfig/diskconfig.h b/libdiskconfig/include/diskconfig/diskconfig.h
similarity index 100%
rename from include/diskconfig/diskconfig.h
rename to libdiskconfig/include/diskconfig/diskconfig.h
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 5f7a078..4942c08 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -69,7 +69,7 @@
     int     fakeFd;
 
     /* a printable name for this fake device */
-    char   *debugName;
+    char   debugName[sizeof("/dev/log/security")];
 
     /* nonzero if this is a binary log */
     int     isBinary;
@@ -123,8 +123,8 @@
  * File descriptor management.
  */
 #define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 16
-static LogState *openLogTable[MAX_OPEN_LOGS];
+#define MAX_OPEN_LOGS 8
+static LogState openLogTable[MAX_OPEN_LOGS];
 
 /*
  * Allocate an fd and associate a new LogState with it.
@@ -134,11 +134,10 @@
 {
     size_t i;
 
-    for (i = 0; i < sizeof(openLogTable); i++) {
-        if (openLogTable[i] == NULL) {
-            openLogTable[i] = calloc(1, sizeof(LogState));
-            openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
-            return openLogTable[i];
+    for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
+        if (openLogTable[i].fakeFd == 0) {
+            openLogTable[i].fakeFd = FAKE_FD_BASE + i;
+            return &openLogTable[i];
         }
     }
     return NULL;
@@ -150,7 +149,7 @@
 static LogState *fdToLogState(int fd)
 {
     if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
-        return openLogTable[fd - FAKE_FD_BASE];
+        return &openLogTable[fd - FAKE_FD_BASE];
     }
     return NULL;
 }
@@ -166,9 +165,7 @@
 
     ls = fdToLogState(fd);
     if (ls != NULL) {
-        openLogTable[fd - FAKE_FD_BASE] = NULL;
-        free(ls->debugName);
-        free(ls);
+        memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
     }
 
     unlock();
@@ -191,10 +188,12 @@
 {
     static const int kDevLogLen = sizeof("/dev/log/") - 1;
 
-    logState->debugName = strdup(pathName);
+    strncpy(logState->debugName, pathName, sizeof(logState->debugName));
+    logState->debugName[sizeof(logState->debugName) - 1] = '\0';
 
     /* identify binary logs */
-    if (strcmp(pathName + kDevLogLen, "events") == 0) {
+    if (!strcmp(pathName + kDevLogLen, "events") ||
+            !strcmp(pathName + kDevLogLen, "security")) {
         logState->isBinary = 1;
     }
 
@@ -218,8 +217,7 @@
 
             i = 0;
             while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
-                i < kMaxTagLen)
-            {
+                    i < kMaxTagLen) {
                 tagName[i++] = *tags++;
             }
             if (i == kMaxTagLen) {
@@ -320,9 +318,9 @@
     };
     int idx;
 
-    idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+    idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
     if (idx < 0 ||
-        idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+            idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
         return "?unknown?";
     return priorityStrings[idx];
 }
@@ -454,13 +452,15 @@
     while (p < end) {
         if (*p++ == '\n') numLines++;
     }
-    if (p > msg && *(p-1) != '\n') numLines++;
+    if (p > msg && *(p-1) != '\n') {
+        numLines++;
+    }
 
     /*
      * Create an array of iovecs large enough to write all of
      * the lines with a prefix and a suffix.
      */
-    const size_t INLINE_VECS = 6;
+    const size_t INLINE_VECS = 64;
     const size_t MAX_LINES   = ((size_t)~0)/(3*sizeof(struct iovec*));
     struct iovec stackVec[INLINE_VECS];
     struct iovec* vec = stackVec;
@@ -494,7 +494,9 @@
             v++;
         }
         const char* start = p;
-        while (p < end && *p != '\n') p++;
+        while (p < end && *p != '\n') {
+            p++;
+        }
         if ((p-start) > 0) {
             v->iov_base = (void*)start;
             v->iov_len = p-start;
@@ -689,6 +691,17 @@
     return redirectOpen(pathName, flags);
 }
 
+/*
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
+ */
 int fakeLogClose(int fd)
 {
     /* Assume that open() was called first. */
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 4946073..b173d1a 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -125,7 +125,7 @@
 
 #if FAKE_LOG_DEVICE
     for (i = 0; i < LOG_ID_MAX; i++) {
-        char buf[sizeof("/dev/log_system")];
+        char buf[sizeof("/dev/log_security")];
         snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
         log_fds[i] = fakeLogOpen(buf, O_WRONLY);
     }
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
index 7b170f5..ffc7244 100644
--- a/libmemtrack/Android.mk
+++ b/libmemtrack/Android.mk
@@ -5,6 +5,8 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := memtrack.c
 LOCAL_MODULE := libmemtrack
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES += hardware/libhardware/include
 LOCAL_SHARED_LIBRARIES := libhardware liblog
 LOCAL_CFLAGS := -Wall -Werror
diff --git a/include/memtrack/memtrack.h b/libmemtrack/include/memtrack/memtrack.h
similarity index 98%
rename from include/memtrack/memtrack.h
rename to libmemtrack/include/memtrack/memtrack.h
index 3917300..8c0ab89 100644
--- a/include/memtrack/memtrack.h
+++ b/libmemtrack/include/memtrack/memtrack.h
@@ -19,7 +19,6 @@
 
 #include <sys/types.h>
 #include <stddef.h>
-#include <cutils/compiler.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
new file mode 100644
index 0000000..b75f1e5
--- /dev/null
+++ b/libmemunreachable/Allocator.cpp
@@ -0,0 +1,480 @@
+/*
+ * 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.
+ */
+
+// Header page:
+//
+// For minimum allocation size (8 bytes), bitmap can store used allocations for
+// up to 4032*8*8=258048, which is 256KiB minus the header page
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "Allocator.h"
+#include "LinkedList.h"
+
+// runtime interfaces used:
+// abort
+// assert - fprintf + mmap
+// mmap
+// munmap
+// prctl
+
+constexpr size_t const_log2(size_t n, size_t p = 0) {
+  return (n <= 1) ? p : const_log2(n / 2, p + 1);
+}
+
+constexpr unsigned int div_round_up(unsigned int x, unsigned int y) {
+  return (x + y - 1) / y;
+}
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+static constexpr size_t kPageSize = 4096;
+static constexpr size_t kChunkSize = 256 * 1024;
+static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
+static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
+static constexpr size_t kMinBucketAllocationSize = 8;
+static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
+    - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
+    / kPageSize;
+
+std::atomic<int> heap_count;
+
+class Chunk;
+
+class HeapImpl {
+ public:
+  HeapImpl();
+  ~HeapImpl();
+  void* operator new(std::size_t count) noexcept;
+  void operator delete(void* ptr);
+
+  void* Alloc(size_t size);
+  void Free(void* ptr);
+  bool Empty();
+
+  void MoveToFullList(Chunk* chunk, int bucket_);
+  void MoveToFreeList(Chunk* chunk, int bucket_);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HeapImpl);
+
+  LinkedList<Chunk*> free_chunks_[kNumBuckets];
+  LinkedList<Chunk*> full_chunks_[kNumBuckets];
+
+  void MoveToList(Chunk* chunk, LinkedList<Chunk*>* head);
+  void* MapAlloc(size_t size);
+  void MapFree(void* ptr);
+  void* AllocLocked(size_t size);
+  void FreeLocked(void* ptr);
+
+  struct MapAllocation {
+    void *ptr;
+    size_t size;
+    MapAllocation* next;
+  };
+  MapAllocation* map_allocation_list_;
+  std::mutex m_;
+};
+
+// Integer log 2, rounds down
+static inline unsigned int log2(size_t n) {
+  return 8 * sizeof(unsigned long long) - __builtin_clzll(n) - 1;
+}
+
+static inline unsigned int size_to_bucket(size_t size) {
+  if (size < kMinBucketAllocationSize)
+    return kMinBucketAllocationSize;
+  return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
+}
+
+static inline size_t bucket_to_size(unsigned int bucket) {
+  return kMinBucketAllocationSize << bucket;
+}
+
+static void* MapAligned(size_t size, size_t align) {
+  const int prot = PROT_READ | PROT_WRITE;
+  const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+
+  size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+  // Over-allocate enough to align
+  size_t map_size = size + align - kPageSize;
+  if (map_size < size) {
+    return nullptr;
+  }
+
+  void* ptr = mmap(NULL, map_size, prot, flags, -1, 0);
+  if (ptr == MAP_FAILED) {
+    return nullptr;
+  }
+
+  size_t aligned_size = map_size;
+  void* aligned_ptr = ptr;
+
+  std::align(align, size, aligned_ptr, aligned_size);
+
+  // Trim beginning
+  if (aligned_ptr != ptr) {
+    ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
+        - reinterpret_cast<uintptr_t>(ptr);
+    munmap(ptr, extra);
+    map_size -= extra;
+    ptr = aligned_ptr;
+  }
+
+  // Trim end
+  if (map_size != size) {
+    assert(map_size > size);
+    assert(ptr != NULL);
+    munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
+        map_size - size);
+  }
+
+#define PR_SET_VMA   0x53564d41
+#define PR_SET_VMA_ANON_NAME    0
+  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
+      reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+
+  return ptr;
+}
+
+class Chunk {
+ public:
+  static void* operator new(std::size_t count) noexcept;
+  static void operator delete(void* ptr);
+  Chunk(HeapImpl* heap, int bucket);
+  ~Chunk() {}
+
+  void *Alloc();
+  void Free(void* ptr);
+  void Purge();
+  bool Empty();
+
+  static Chunk* ptr_to_chunk(void* ptr) {
+    return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
+        & ~(kChunkSize - 1));
+  }
+  static bool is_chunk(void* ptr) {
+    return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
+  }
+
+  unsigned int free_count() {
+    return free_count_;
+  }
+  HeapImpl* heap() {
+    return heap_;
+  }
+  LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Chunk);
+  HeapImpl* heap_;
+  unsigned int bucket_;
+  unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
+  unsigned int max_allocations_; // maximum number of allocations in the chunk
+  unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
+  unsigned int free_count_; // number of available allocations
+  unsigned int frees_since_purge_; // number of calls to Free since last Purge
+
+  // bitmap of pages that have been dirtied
+  uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
+
+  // bitmap of free allocations.
+  uint32_t free_bitmap_[kUsableChunkSize / kMinBucketAllocationSize / 32];
+
+  char data_[0];
+
+  unsigned int ptr_to_n(void* ptr) {
+    ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
+        - reinterpret_cast<uintptr_t>(data_);
+    return offset / allocation_size_;
+  }
+  void* n_to_ptr(unsigned int n) {
+    return data_ + n * allocation_size_;
+  }
+};
+static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void* Chunk::operator new(std::size_t count __attribute__((unused))) noexcept {
+  assert(count == sizeof(Chunk));
+  void* mem = MapAligned(kChunkSize, kChunkSize);
+  if (!mem) {
+    abort(); //throw std::bad_alloc;
+  }
+
+  return mem;
+}
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void Chunk::operator delete(void *ptr) {
+  assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
+  munmap(ptr, kChunkSize);
+}
+
+Chunk::Chunk(HeapImpl* heap, int bucket) :
+    node_(this), heap_(heap), bucket_(bucket), allocation_size_(
+        bucket_to_size(bucket)), max_allocations_(
+        kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
+        max_allocations_), frees_since_purge_(0) {
+  memset(dirty_pages_, 0, sizeof(dirty_pages_));
+  memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
+}
+
+bool Chunk::Empty() {
+  return free_count_ == max_allocations_;
+}
+
+void* Chunk::Alloc() {
+  assert(free_count_ > 0);
+
+  unsigned int i = first_free_bitmap_;
+  while (free_bitmap_[i] == 0)
+    i++;
+  assert(i < ARRAY_SIZE(free_bitmap_));
+  unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
+  assert(free_bitmap_[i] & (1U << bit));
+  free_bitmap_[i] &= ~(1U << bit);
+  unsigned int n = i * 32 + bit;
+  assert(n < max_allocations_);
+
+  unsigned int page = n * allocation_size_ / kPageSize;
+  assert(page / 32 < ARRAY_SIZE(dirty_pages_));
+  dirty_pages_[page / 32] |= 1U << (page % 32);
+
+  free_count_--;
+  if (free_count_ == 0) {
+    heap_->MoveToFullList(this, bucket_);
+  }
+
+  return n_to_ptr(n);
+}
+
+void Chunk::Free(void* ptr) {
+  assert(is_chunk(ptr));
+  assert(ptr_to_chunk(ptr) == this);
+
+  unsigned int n = ptr_to_n(ptr);
+  unsigned int i = n / 32;
+  unsigned int bit = n % 32;
+
+  assert(i < ARRAY_SIZE(free_bitmap_));
+  assert(!(free_bitmap_[i] & (1U << bit)));
+  free_bitmap_[i] |= 1U << bit;
+  free_count_++;
+
+  if (i < first_free_bitmap_) {
+    first_free_bitmap_ = i;
+  }
+
+  if (free_count_ == 1) {
+    heap_->MoveToFreeList(this, bucket_);
+  } else {
+    // TODO(ccross): move down free list if necessary
+  }
+
+  if (frees_since_purge_++ * allocation_size_ > 16 * kPageSize) {
+    Purge();
+  }
+}
+
+void Chunk::Purge() {
+  frees_since_purge_ = 0;
+
+  //unsigned int allocsPerPage = kPageSize / allocation_size_;
+}
+
+// Override new operator on HeapImpl to use mmap to allocate a page
+void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
+    noexcept {
+  assert(count == sizeof(HeapImpl));
+  void* mem = MapAligned(kPageSize, kPageSize);
+  if (!mem) {
+    abort(); //throw std::bad_alloc;
+  }
+
+  heap_count++;
+  return mem;
+}
+
+void HeapImpl::operator delete(void *ptr) {
+  munmap(ptr, kPageSize);
+}
+
+HeapImpl::HeapImpl() :
+    free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
+}
+
+bool HeapImpl::Empty() {
+  for (unsigned int i = 0; i < kNumBuckets; i++) {
+    for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+      if (!it->data()->Empty()) {
+        return false;
+      }
+    }
+    for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+      if (!it->data()->Empty()) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+HeapImpl::~HeapImpl() {
+  for (unsigned int i = 0; i < kNumBuckets; i++) {
+    while (!free_chunks_[i].empty()) {
+      Chunk *chunk = free_chunks_[i].next()->data();
+      chunk->node_.remove();
+      delete chunk;
+    }
+    while (!full_chunks_[i].empty()) {
+      Chunk *chunk = full_chunks_[i].next()->data();
+      chunk->node_.remove();
+      delete chunk;
+    }
+  }
+}
+
+void* HeapImpl::Alloc(size_t size) {
+  std::lock_guard<std::mutex> lk(m_);
+  return AllocLocked(size);
+}
+
+void* HeapImpl::AllocLocked(size_t size) {
+  if (__predict_false(size > kMaxBucketAllocationSize)) {
+    return MapAlloc(size);
+  }
+  int bucket = size_to_bucket(size);
+  if (__predict_false(free_chunks_[bucket].empty())) {
+    Chunk *chunk = new Chunk(this, bucket);
+    free_chunks_[bucket].insert(chunk->node_);
+  }
+  return free_chunks_[bucket].next()->data()->Alloc();
+}
+
+void HeapImpl::Free(void *ptr) {
+  std::lock_guard<std::mutex> lk(m_);
+  FreeLocked(ptr);
+}
+
+void HeapImpl::FreeLocked(void *ptr) {
+  if (!Chunk::is_chunk(ptr)) {
+    HeapImpl::MapFree(ptr);
+  } else {
+    Chunk* chunk = Chunk::ptr_to_chunk(ptr);
+    assert(chunk->heap() == this);
+    chunk->Free(ptr);
+  }
+}
+
+void* HeapImpl::MapAlloc(size_t size) {
+  size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+  MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
+      sizeof(MapAllocation)));
+  void* ptr = MapAligned(size, kChunkSize);
+  if (!ptr) {
+    FreeLocked(allocation);
+    abort(); //throw std::bad_alloc;
+  }
+  allocation->ptr = ptr;
+  allocation->size = size;
+  allocation->next = map_allocation_list_;
+  map_allocation_list_ = allocation;
+
+  return ptr;
+}
+
+void HeapImpl::MapFree(void *ptr) {
+  MapAllocation **allocation = &map_allocation_list_;
+  while (*allocation && (*allocation)->ptr != ptr)
+    allocation = &(*allocation)->next;
+
+  assert(*allocation != nullptr);
+
+  munmap((*allocation)->ptr, (*allocation)->size);
+  FreeLocked(*allocation);
+
+  *allocation = (*allocation)->next;
+}
+
+void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+  MoveToList(chunk, &free_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+  MoveToList(chunk, &full_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+  // Remove from old list
+  chunk->node_.remove();
+
+  LinkedList<Chunk*> *node = head;
+  // Insert into new list, sorted by lowest free count
+  while (node->next() != head && node->data() != nullptr
+      && node->data()->free_count() < chunk->free_count())
+    node = node->next();
+
+  node->insert(chunk->node_);
+}
+
+Heap::Heap() {
+  // HeapImpl overloads the operator new in order to mmap itself instead of
+  // allocating with new.
+  // Can't use a shared_ptr to store the result because shared_ptr needs to
+  // allocate, and Allocator<T> is still being constructed.
+  impl_ = new HeapImpl();
+  owns_impl_ = true;
+}
+
+Heap::~Heap() {
+  if (owns_impl_) {
+    delete impl_;
+  }
+}
+
+void* Heap::allocate(size_t size) {
+  return impl_->Alloc(size);
+}
+
+void Heap::deallocate(void* ptr) {
+  impl_->Free(ptr);
+}
+
+void Heap::deallocate(HeapImpl*impl, void* ptr) {
+  impl->Free(ptr);
+}
+
+bool Heap::empty() {
+  return impl_->Empty();
+}
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
new file mode 100644
index 0000000..b0a4d4c
--- /dev/null
+++ b/libmemunreachable/Allocator.h
@@ -0,0 +1,224 @@
+/*
+ * 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_ALLOCATOR_H_
+#define LIBMEMUNREACHABLE_ALLOCATOR_H_
+
+#include <atomic>
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_set>
+#include <vector>
+extern std::atomic<int> heap_count;
+
+class HeapImpl;
+
+template<typename T>
+class Allocator;
+
+
+// Non-templated class that implements wraps HeapImpl to keep
+// implementation out of the header file
+class Heap {
+public:
+  Heap();
+  ~Heap();
+
+  // Copy constructor that does not take ownership of impl_
+  Heap(const Heap& other) : impl_(other.impl_), owns_impl_(false) {}
+
+  // Assignment disabled
+  Heap& operator=(const Heap&) = delete;
+
+  // Allocate size bytes
+  void* allocate(size_t size);
+
+  // Deallocate allocation returned by allocate
+  void deallocate(void*);
+
+  bool empty();
+
+  static void deallocate(HeapImpl* impl, void* ptr);
+
+  // Allocate a class of type T
+  template<class T>
+  T* allocate() {
+    return reinterpret_cast<T*>(allocate(sizeof(T)));
+  }
+
+  // Comparators, copied objects will be equal
+  bool operator ==(const Heap& other) const {
+    return impl_ == other.impl_;
+  }
+  bool operator !=(const Heap& other) const {
+    return !(*this == other);
+  }
+
+  // std::unique_ptr wrapper that allocates using allocate and deletes using
+  // deallocate
+  template<class T>
+  using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
+
+  template<class T, class... Args>
+  unique_ptr<T> make_unique(Args&&... args) {
+    HeapImpl* impl = impl_;
+    return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
+        [impl](void* ptr) {
+          reinterpret_cast<T*>(ptr)->~T();
+          deallocate(impl, ptr);
+        });
+  }
+
+  // std::unique_ptr wrapper that allocates using allocate and deletes using
+  // deallocate
+  template<class T>
+  using shared_ptr = std::shared_ptr<T>;
+
+  template<class T, class... Args>
+  shared_ptr<T> make_shared(Args&&... args);
+
+protected:
+  HeapImpl* impl_;
+  bool owns_impl_;
+};
+
+// STLAllocator implements the std allocator interface on top of a Heap
+template<typename T>
+class STLAllocator {
+public:
+  using value_type = T;
+  ~STLAllocator() {
+  }
+
+  // Construct an STLAllocator on top of a Heap
+  STLAllocator(const Heap& heap) :
+      heap_(heap) {
+  }
+
+  // Rebind an STLAllocator from an another STLAllocator
+  template<typename U>
+  STLAllocator(const STLAllocator<U>& other) :
+      heap_(other.heap_) {
+  }
+
+  STLAllocator(const STLAllocator&) = default;
+  STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
+
+  T* allocate(std::size_t n) {
+    return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
+  }
+
+  void deallocate(T* ptr, std::size_t) {
+    heap_.deallocate(ptr);
+  }
+
+  template<typename U>
+  bool operator ==(const STLAllocator<U>& other) const {
+    return heap_ == other.heap_;
+  }
+  template<typename U>
+  inline bool operator !=(const STLAllocator<U>& other) const {
+    return !(this == other);
+  }
+
+  template<typename U>
+  friend class STLAllocator;
+
+protected:
+  Heap heap_;
+};
+
+
+// Allocator extends STLAllocator with some convenience methods for allocating
+// a single object and for constructing unique_ptr and shared_ptr objects with
+// appropriate deleters.
+template<class T>
+class Allocator : public STLAllocator<T> {
+ public:
+  ~Allocator() {}
+
+  Allocator(const Heap& other) :
+      STLAllocator<T>(other) {
+  }
+
+  template<typename U>
+  Allocator(const STLAllocator<U>& other) :
+      STLAllocator<T>(other) {
+  }
+
+  Allocator(const Allocator&) = default;
+  Allocator<T>& operator=(const Allocator<T>&) = default;
+
+  using STLAllocator<T>::allocate;
+  using STLAllocator<T>::deallocate;
+  using STLAllocator<T>::heap_;
+
+  T* allocate() {
+    return STLAllocator<T>::allocate(1);
+  }
+  void deallocate(void* ptr) {
+    heap_.deallocate(ptr);
+  }
+
+  using shared_ptr = Heap::shared_ptr<T>;
+
+  template<class... Args>
+  shared_ptr make_shared(Args&& ...args) {
+    return heap_.template make_shared<T>(std::forward<Args>(args)...);
+  }
+
+  using unique_ptr = Heap::unique_ptr<T>;
+
+  template<class... Args>
+  unique_ptr make_unique(Args&& ...args) {
+    return heap_.template make_unique<T>(std::forward<Args>(args)...);
+  }
+};
+
+// std::unique_ptr wrapper that allocates using allocate and deletes using
+// deallocate.  Implemented outside class definition in order to pass
+// Allocator<T> to shared_ptr.
+template<class T, class... Args>
+inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
+  return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
+      std::forward<Args>(args)...);
+}
+
+namespace allocator {
+
+template<class T>
+using vector = std::vector<T, Allocator<T>>;
+
+template<class T>
+using list = std::list<T, Allocator<T>>;
+
+template<class T, class Key, class Compare = std::less<Key>>
+using map = std::map<Key, T, Compare, 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>>;
+
+template<class Key, class Compare = std::less<Key>>
+using set = std::set<Key, Compare, Allocator<Key>>;
+
+using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+}
+
+#endif
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
new file mode 100644
index 0000000..0df26a8
--- /dev/null
+++ b/libmemunreachable/Android.mk
@@ -0,0 +1,43 @@
+LOCAL_PATH := $(call my-dir)
+
+memunreachable_srcs := \
+   Allocator.cpp \
+   HeapWalker.cpp \
+   LeakPipe.cpp \
+   LineBuffer.cpp \
+   MemUnreachable.cpp \
+   ProcessMappings.cpp \
+   PtracerThread.cpp \
+   ThreadCapture.cpp \
+
+memunreachable_test_srcs := \
+   tests/Allocator_test.cpp \
+   tests/HeapWalker_test.cpp \
+   tests/MemUnreachable_test.cpp \
+   tests/ThreadCapture_test.cpp \
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmemunreachable
+LOCAL_SRC_FILES := $(memunreachable_srcs)
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_SHARED_LIBRARIES := libbase liblog
+LOCAL_STATIC_LIBRARIES := libc_malloc_debug_backtrace libc_logging
+# Only need this for arm since libc++ uses its own unwind code that
+# doesn't mix with the other default unwind code.
+LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CLANG := true
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := memunreachable_test
+LOCAL_SRC_FILES := $(memunreachable_test_srcs)
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libmemunreachable libbase liblog
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
new file mode 100644
index 0000000..1a0c33d
--- /dev/null
+++ b/libmemunreachable/HeapWalker.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 <map>
+#include <utility>
+
+#include "Allocator.h"
+#include "HeapWalker.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}));
+  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;
+    return true;
+  } else {
+    Range overlap = inserted.first->first;
+    ALOGE("range %p-%p overlaps with existing range %p-%p",
+        reinterpret_cast<void*>(begin),
+        reinterpret_cast<void*>(end),
+        reinterpret_cast<void*>(overlap.begin),
+        reinterpret_cast<void*>(overlap.end));
+    return false;
+  }
+}
+
+void HeapWalker::Walk(const Range& range, bool RangeInfo::*flag) {
+  allocator::vector<Range> to_do(1, range, 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;
+          }
+        }
+      }
+    }
+  }
+}
+
+void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
+  roots_.push_back(Range{begin, end});
+}
+
+void HeapWalker::Root(const allocator::vector<uintptr_t>& vals) {
+  root_vals_.insert(root_vals_.end(), vals.begin(), vals.end());
+}
+
+size_t HeapWalker::Allocations() {
+  return allocations_.size();
+}
+
+size_t HeapWalker::AllocationBytes() {
+  return allocation_bytes_;
+}
+
+bool HeapWalker::DetectLeaks() {
+  for (auto it = roots_.begin(); it != roots_.end(); it++) {
+    Walk(*it, &RangeInfo::referenced_from_root);
+  }
+
+  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);
+    }
+  }
+
+  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;
+  size_t leak_bytes = 0;
+  for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+    if (!it->second.referenced_from_root) {
+      num_leaks++;
+      leak_bytes += it->first.end - it->first.begin;
+    }
+  }
+
+  size_t n = 0;
+  for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+    if (!it->second.referenced_from_root) {
+      if (n++ <= limit) {
+        leaked.push_back(it->first);
+      }
+    }
+  }
+
+  if (num_leaks_out) {
+    *num_leaks_out = num_leaks;
+  }
+  if (leak_bytes_out) {
+    *leak_bytes_out = leak_bytes;
+  }
+
+  return true;
+}
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
new file mode 100644
index 0000000..4be1934
--- /dev/null
+++ b/libmemunreachable/HeapWalker.h
@@ -0,0 +1,75 @@
+/*
+ * 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_HEAP_WALKER_H_
+#define LIBMEMUNREACHABLE_HEAP_WALKER_H_
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+
+// A range [begin, end)
+struct Range {
+  uintptr_t begin;
+  uintptr_t end;
+};
+
+// Comparator for Ranges that returns equivalence for overlapping ranges
+struct compare_range {
+  bool operator()(const Range& a, const Range& b) const {
+    return a.end <= b.begin;
+  }
+};
+
+
+class HeapWalker {
+ public:
+  HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
+    allocations_(allocator), allocation_bytes_(0),
+	roots_(allocator), root_vals_(allocator) {
+    valid_allocations_range_.end = 0;
+    valid_allocations_range_.begin = ~valid_allocations_range_.end;
+  }
+  ~HeapWalker() {}
+  bool Allocation(uintptr_t begin, uintptr_t end);
+  void Root(uintptr_t begin, uintptr_t end);
+  void Root(const allocator::vector<uintptr_t>& vals);
+
+  bool DetectLeaks();
+
+  bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
+      size_t* leak_bytes);
+  size_t Allocations();
+  size_t AllocationBytes();
+
+ private:
+  struct RangeInfo {
+    bool referenced_from_root;
+    bool referenced_from_leak;
+  };
+  void Walk(const Range& range, bool RangeInfo::* flag);
+  DISALLOW_COPY_AND_ASSIGN(HeapWalker);
+  Allocator<HeapWalker> allocator_;
+  using RangeMap = allocator::map<RangeInfo, Range, compare_range>;
+  RangeMap allocations_;
+  size_t allocation_bytes_;
+  Range valid_allocations_range_;
+
+  allocator::vector<Range> roots_;
+  allocator::vector<uintptr_t> root_vals_;
+};
+
+#endif
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
new file mode 100644
index 0000000..080f8a7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.cpp
@@ -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.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "LeakPipe.h"
+
+#include "log.h"
+
+bool LeakPipe::SendFd(int sock, int fd) {
+  struct msghdr hdr{};
+  struct iovec iov{};
+  unsigned int data = 0xfdfdfdfd;
+  alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  iov.iov_base = &data;
+  iov.iov_len = sizeof(data);
+
+  hdr.msg_control = cmsgbuf;
+  hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+
+  *(int*)CMSG_DATA(cmsg) = fd;
+
+  int ret = sendmsg(sock, &hdr, 0);
+  if (ret < 0) {
+    ALOGE("failed to send fd: %s", strerror(errno));
+    return false;
+  }
+  if (ret == 0) {
+    ALOGE("eof when sending fd");
+    return false;
+  }
+
+  return true;
+}
+
+int LeakPipe::ReceiveFd(int sock) {
+  struct msghdr hdr{};
+  struct iovec iov{};
+  unsigned int data;
+  alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+  hdr.msg_iov = &iov;
+  hdr.msg_iovlen = 1;
+  iov.iov_base = &data;
+  iov.iov_len = sizeof(data);
+
+  hdr.msg_control = cmsgbuf;
+  hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+  int ret = recvmsg(sock, &hdr, 0);
+  if (ret < 0) {
+    ALOGE("failed to receive fd: %s", strerror(errno));
+    return -1;
+  }
+  if (ret == 0) {
+    ALOGE("eof when receiving fd");
+    return -1;
+  }
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+  if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+    ALOGE("missing fd while receiving fd");
+    return -1;
+  }
+
+  return *(int*)CMSG_DATA(cmsg);
+}
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
new file mode 100644
index 0000000..3f4e0b7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.h
@@ -0,0 +1,201 @@
+/*
+ * 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_PIPE_H_
+#define LIBMEMUNREACHABLE_LEAK_PIPE_H_
+
+#include <sys/socket.h>
+
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "ScopedPipe.h"
+#include "log.h"
+
+// LeakPipe implements a pipe that can transfer vectors of simple objects
+// between processes.  The pipe is created in the sending process and
+// transferred over a socketpair that was created before forking.  This ensures
+// that only the sending process can have the send side of the pipe open, so if
+// the sending process dies the pipe will close.
+class LeakPipe {
+ public:
+  LeakPipe() {
+    int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+    if (ret < 0) {
+      LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+    }
+  }
+
+  ~LeakPipe() {
+    Close();
+  }
+
+  void Close() {
+    close(sv_[0]);
+    close(sv_[1]);
+    sv_[0] = -1;
+    sv_[1] = -1;
+  }
+
+  bool OpenReceiver() {
+    int fd = ReceiveFd(sv_[0]);
+    if (fd < 0) {
+      return false;
+    }
+
+    receiver_.SetFd(fd);
+    return true;
+  }
+
+  bool OpenSender() {
+    ScopedPipe pipe;
+
+    if (!SendFd(sv_[1], pipe.Receiver())) {
+      return false;
+    }
+    pipe.ReleaseReceiver();
+
+    sender_.SetFd(pipe.ReleaseSender());
+    return true;
+  }
+
+  class LeakPipeBase {
+   public:
+    LeakPipeBase() : fd_(-1) {}
+
+    ~LeakPipeBase() {
+      Close();
+    }
+
+    void SetFd(int fd) {
+      fd_ = fd;
+    }
+
+    void Close() {
+      close(fd_);
+      fd_ = -1;
+    }
+
+   protected:
+    int fd_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(LeakPipeBase);
+  };
+
+  class LeakPipeSender : public LeakPipeBase {
+   public:
+    using LeakPipeBase::LeakPipeBase;
+
+    template<typename T>
+    bool Send(const T& value) {
+      ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
+      if (ret < 0) {
+        ALOGE("failed to send value: %s", strerror(errno));
+        return false;
+      } else if (static_cast<size_t>(ret) != sizeof(T)) {
+        ALOGE("eof while writing value");
+        return false;
+      }
+
+      return true;
+    }
+
+    template<class T, class Alloc = std::allocator<T>>
+    bool SendVector(const std::vector<T, Alloc>& vector) {
+      size_t size = vector.size() * sizeof(T);
+      if (!Send(size)) {
+        return false;
+      }
+
+      ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
+      if (ret < 0) {
+        ALOGE("failed to send vector: %s", strerror(errno));
+        return false;
+      } else if (static_cast<size_t>(ret) != size) {
+        ALOGE("eof while writing vector");
+        return false;
+      }
+
+      return true;
+    }
+  };
+
+  class LeakPipeReceiver : public LeakPipeBase {
+   public:
+    using LeakPipeBase::LeakPipeBase;
+
+    template<typename T>
+    bool Receive(T* value) {
+      ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
+      if (ret < 0) {
+        ALOGE("failed to receive value: %s", strerror(errno));
+        return false;
+      } else if (static_cast<size_t>(ret) != sizeof(T)) {
+        ALOGE("eof while receiving value");
+        return false;
+      }
+
+      return true;
+    }
+
+    template<class T, class Alloc = std::allocator<T>>
+    bool ReceiveVector(std::vector<T, Alloc>& vector) {
+      size_t size = 0;
+      if (!Receive(&size)) {
+        return false;
+      }
+
+      vector.resize(size / sizeof(T));
+
+      char* ptr = reinterpret_cast<char*>(vector.data());
+      while (size > 0) {
+        ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
+        if (ret < 0) {
+          ALOGE("failed to send vector: %s", strerror(errno));
+          return false;
+        } else if (ret == 0) {
+          ALOGE("eof while reading vector");
+          return false;
+        }
+        size -= ret;
+        ptr += ret;
+      }
+
+      return true;
+    }
+
+  };
+
+  LeakPipeReceiver& Receiver() {
+    return receiver_;
+  }
+
+  LeakPipeSender& Sender() {
+    return sender_;
+  }
+
+ private:
+  LeakPipeReceiver receiver_;
+  LeakPipeSender sender_;
+  bool SendFd(int sock, int fd);
+  int ReceiveFd(int sock);
+  DISALLOW_COPY_AND_ASSIGN(LeakPipe);
+  int sv_[2];
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
new file mode 100644
index 0000000..d3580c0
--- /dev/null
+++ b/libmemunreachable/LineBuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// Copied from system/extras/memory_replay/LineBuffer.cpp
+// TODO(ccross): find a way to share between libmemunreachable and memory_replay?
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "LineBuffer.h"
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
+}
+
+bool LineBuffer::GetLine(char** line, size_t* line_len) {
+  while (true) {
+    if (bytes_ > 0) {
+      char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
+      if (newline != nullptr) {
+        *newline = '\0';
+        *line = buffer_ + start_;
+        start_ = newline - buffer_ + 1;
+        bytes_ -= newline - *line + 1;
+        *line_len = newline - *line;
+        return true;
+      }
+    }
+    if (start_ > 0) {
+      // Didn't find anything, copy the current to the front of the buffer.
+      memmove(buffer_, buffer_ + start_, bytes_);
+      start_ = 0;
+    }
+    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
+    if (bytes <= 0) {
+      if (bytes_ > 0) {
+        // The read data might not contain a nul terminator, so add one.
+        buffer_[bytes_] = '\0';
+        *line = buffer_ + start_;
+        *line_len = bytes_;
+        bytes_ = 0;
+        start_ = 0;
+        return true;
+      }
+      return false;
+    }
+    bytes_ += bytes;
+  }
+}
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
new file mode 100644
index 0000000..a015c46
--- /dev/null
+++ b/libmemunreachable/LineBuffer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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_LINE_BUFFER_H
+#define _LIBMEMUNREACHABLE_LINE_BUFFER_H
+
+#include <stdint.h>
+
+class LineBuffer {
+ public:
+  LineBuffer(int fd, char* buffer, size_t buffer_len);
+
+  bool GetLine(char** line, size_t* line_len);
+
+ private:
+  int fd_;
+  char* buffer_ = nullptr;
+  size_t buffer_len_ = 0;
+  size_t start_ = 0;
+  size_t bytes_ = 0;
+};
+
+#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
new file mode 100644
index 0000000..3e44035
--- /dev/null
+++ b/libmemunreachable/LinkedList.h
@@ -0,0 +1,60 @@
+/*
+ * 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_LINKED_LIST_H_
+#define LIBMEMUNREACHABLE_LINKED_LIST_H_
+
+template<class T>
+class LinkedList {
+public:
+    LinkedList() : next_(this), prev_(this), data_() {}
+    LinkedList(T data) : LinkedList() {
+        data_ = data;
+    }
+    ~LinkedList() {}
+    void insert(LinkedList<T>& node) {
+        assert(node.empty());
+        node.next_ = this->next_;
+        node.next_->prev_ = &node;
+        this->next_ = &node;
+        node.prev_ = this;
+    }
+    void remove() {
+        this->next_->prev_ = this->prev_;
+        this->prev_->next_ = this->next_;
+        this->next_ = this;
+        this->prev_ = this;
+    }
+    T data() { return data_; }
+    bool empty() { return next_ == this && prev_ == this; }
+    LinkedList<T> *next() { return next_; }
+private:
+    LinkedList<T> *next_;
+    LinkedList<T> *prev_;
+    T data_;
+};
+
+template<class T>
+class LinkedListHead {
+public:
+    LinkedListHead() : node_() {}
+    ~LinkedListHead() {}
+
+private:
+    LinkedList<T> node_;
+};
+
+#endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
new file mode 100644
index 0000000..eca26eb
--- /dev/null
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -0,0 +1,437 @@
+/*
+ * 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 <functional>
+#include <iomanip>
+#include <mutex>
+#include <string>
+#include <sstream>
+
+#include <backtrace.h>
+#include <android-base/macros.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakPipe.h"
+#include "ProcessMappings.h"
+#include "PtracerThread.h"
+#include "ScopedDisableMalloc.h"
+#include "Semaphore.h"
+#include "ThreadCapture.h"
+
+#include "memunreachable/memunreachable.h"
+#include "bionic.h"
+#include "log.h"
+
+const size_t Leak::contents_length;
+
+using namespace std::chrono_literals;
+
+class MemUnreachable {
+ public:
+  MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
+      heap_walker_(allocator_) {}
+  bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+      const allocator::vector<Mapping>& mappings);
+  bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+      size_t* num_leaks, size_t* leak_bytes);
+  size_t Allocations() { return heap_walker_.Allocations(); }
+  size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+ private:
+  bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
+      allocator::vector<Mapping>& heap_mappings,
+      allocator::vector<Mapping>& anon_mappings,
+      allocator::vector<Mapping>& globals_mappings,
+      allocator::vector<Mapping>& stack_mappings);
+  DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
+  pid_t pid_;
+  Allocator<void> allocator_;
+  HeapWalker heap_walker_;
+};
+
+static void HeapIterate(const Mapping& heap_mapping,
+    const std::function<void(uintptr_t, size_t)>& func) {
+  malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
+      [](uintptr_t base, size_t size, void* arg) {
+    auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+    (*f)(base, size);
+  }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+}
+
+bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+    const allocator::vector<Mapping>& mappings) {
+  ALOGI("searching process %d for allocations", pid_);
+  allocator::vector<Mapping> heap_mappings{mappings};
+  allocator::vector<Mapping> anon_mappings{mappings};
+  allocator::vector<Mapping> globals_mappings{mappings};
+  allocator::vector<Mapping> stack_mappings{mappings};
+  if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
+      globals_mappings, stack_mappings)) {
+    return false;
+  }
+
+  for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
+    ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    HeapIterate(*it, [&](uintptr_t base, size_t size) {
+      heap_walker_.Allocation(base, base + size);
+    });
+  }
+
+  for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
+    ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    heap_walker_.Allocation(it->begin, it->end);
+  }
+
+  for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
+    ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+    heap_walker_.Root(it->begin, it->end);
+  }
+
+  for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
+    for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
+      if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
+        ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+        heap_walker_.Root(thread_it->stack.first, it->end);
+      }
+    }
+    heap_walker_.Root(thread_it->regs);
+  }
+
+  ALOGI("searching done");
+
+  return true;
+}
+
+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)) {
+    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);
+  }
+
+  ALOGI("sweeping done");
+
+  return true;
+}
+
+static bool has_prefix(const allocator::string& s, const char* prefix) {
+  int ret = s.compare(0, strlen(prefix), prefix);
+  return ret == 0;
+}
+
+bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
+    allocator::vector<Mapping>& heap_mappings,
+    allocator::vector<Mapping>& anon_mappings,
+    allocator::vector<Mapping>& globals_mappings,
+    allocator::vector<Mapping>& stack_mappings)
+{
+  heap_mappings.clear();
+  anon_mappings.clear();
+  globals_mappings.clear();
+  stack_mappings.clear();
+
+  allocator::string current_lib{allocator_};
+
+  for (auto it = mappings.begin(); it != mappings.end(); it++) {
+    if (it->execute) {
+      current_lib = it->name;
+      continue;
+    }
+
+    if (!it->read) {
+      continue;
+    }
+
+    const allocator::string mapping_name{it->name, allocator_};
+    if (mapping_name == "[anon:.bss]") {
+      // named .bss section
+      globals_mappings.emplace_back(*it);
+    } else if (mapping_name == current_lib) {
+      // .rodata or .data section
+      globals_mappings.emplace_back(*it);
+    } else if (mapping_name == "[anon:libc_malloc]") {
+      // named malloc mapping
+      heap_mappings.emplace_back(*it);
+    } else if (has_prefix(mapping_name, "/dev/ashmem/dalvik")) {
+      // named dalvik heap mapping
+      globals_mappings.emplace_back(*it);
+    } else if (has_prefix(mapping_name, "[stack")) {
+      // named stack mapping
+      stack_mappings.emplace_back(*it);
+    } else if (mapping_name.size() == 0) {
+      globals_mappings.emplace_back(*it);
+    } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+      // TODO(ccross): it would be nice to treat named anonymous mappings as
+      // possible leaks, but naming something in a .bss or .data section makes
+      // it impossible to distinguish them from mmaped and then named mappings.
+      globals_mappings.emplace_back(*it);
+    }
+  }
+
+  return true;
+}
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+  int parent_pid = getpid();
+  int parent_tid = gettid();
+
+  Heap heap;
+
+  Semaphore continue_parent_sem;
+  LeakPipe pipe;
+
+  PtracerThread thread{[&]() -> int {
+    /////////////////////////////////////////////
+    // Collection thread
+    /////////////////////////////////////////////
+    ALOGI("collecting thread info for process %d...", parent_pid);
+
+    ThreadCapture thread_capture(parent_pid, heap);
+    allocator::vector<ThreadInfo> thread_info(heap);
+    allocator::vector<Mapping> mappings(heap);
+
+    // ptrace all the threads
+    if (!thread_capture.CaptureThreads()) {
+      continue_parent_sem.Post();
+      return 1;
+    }
+
+    // collect register contents and stacks
+    if (!thread_capture.CapturedThreadInfo(thread_info)) {
+      continue_parent_sem.Post();
+      return 1;
+    }
+
+    // snapshot /proc/pid/maps
+    if (!ProcessMappings(parent_pid, mappings)) {
+      continue_parent_sem.Post();
+      return 1;
+    }
+
+    // malloc must be enabled to call fork, at_fork handlers take the same
+    // locks as ScopedDisableMalloc.  All threads are paused in ptrace, so
+    // memory state is still consistent.  Unfreeze the original thread so it
+    // can drop the malloc locks, it will block until the collection thread
+    // exits.
+    thread_capture.ReleaseThread(parent_tid);
+    continue_parent_sem.Post();
+
+    // fork a process to do the heap walking
+    int ret = fork();
+    if (ret < 0) {
+      return 1;
+    } else if (ret == 0) {
+      /////////////////////////////////////////////
+      // Heap walker process
+      /////////////////////////////////////////////
+      // Examine memory state in the child using the data collected above and
+      // the CoW snapshot of the process memory contents.
+
+      if (!pipe.OpenSender()) {
+        _exit(1);
+      }
+
+      MemUnreachable unreachable{parent_pid, heap};
+
+      if (!unreachable.CollectAllocations(thread_info, mappings)) {
+        _exit(2);
+      }
+      size_t num_allocations = unreachable.Allocations();
+      size_t allocation_bytes = unreachable.AllocationBytes();
+
+      allocator::vector<Leak> leaks{heap};
+
+      size_t num_leaks = 0;
+      size_t leak_bytes = 0;
+      bool ok = unreachable.GetUnreachableMemory(leaks, limit, &num_leaks, &leak_bytes);
+
+      ok = ok && pipe.Sender().Send(num_allocations);
+      ok = ok && pipe.Sender().Send(allocation_bytes);
+      ok = ok && pipe.Sender().Send(num_leaks);
+      ok = ok && pipe.Sender().Send(leak_bytes);
+      ok = ok && pipe.Sender().SendVector(leaks);
+
+      if (!ok) {
+        _exit(3);
+      }
+
+      _exit(0);
+    } else {
+      // Nothing left to do in the collection thread, return immediately,
+      // releasing all the captured threads.
+      ALOGI("collection thread done");
+      return 0;
+    }
+  }};
+
+  /////////////////////////////////////////////
+  // Original thread
+  /////////////////////////////////////////////
+
+  {
+    // Disable malloc to get a consistent view of memory
+    ScopedDisableMalloc disable_malloc;
+
+    // Start the collection thread
+    thread.Start();
+
+    // Wait for the collection thread to signal that it is ready to fork the
+    // heap walker process.
+    continue_parent_sem.Wait(30s);
+
+    // Re-enable malloc so the collection thread can fork.
+  }
+
+  // Wait for the collection thread to exit
+  int ret = thread.Join();
+  if (ret != 0) {
+    return false;
+  }
+
+  // Get a pipe from the heap walker process.  Transferring a new pipe fd
+  // ensures no other forked processes can have it open, so when the heap
+  // walker process dies the remote side of the pipe will close.
+  if (!pipe.OpenReceiver()) {
+    return false;
+  }
+
+  bool ok = true;
+  ok = ok && pipe.Receiver().Receive(&info.num_allocations);
+  ok = ok && pipe.Receiver().Receive(&info.allocation_bytes);
+  ok = ok && pipe.Receiver().Receive(&info.num_leaks);
+  ok = ok && pipe.Receiver().Receive(&info.leak_bytes);
+  ok = ok && pipe.Receiver().ReceiveVector(info.leaks);
+  if (!ok) {
+    return false;
+  }
+
+  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");
+
+  return true;
+}
+
+std::string Leak::ToString(bool log_contents) const {
+
+  std::ostringstream oss;
+
+  oss << "  " << std::dec << size;
+  oss << " bytes unreachable at ";
+  oss << std::hex << begin;
+  oss << std::endl;
+
+  if (log_contents) {
+    const int bytes_per_line = 16;
+    const size_t bytes = std::min(size, contents_length);
+
+    if (bytes == size) {
+      oss << "   contents:" << std::endl;
+    } else {
+      oss << "  first " << bytes << " bytes of contents:" << std::endl;
+    }
+
+    for (size_t i = 0; i < bytes; i += bytes_per_line) {
+      oss << "   " << std::hex << begin + i << ": ";
+      size_t j;
+      oss << std::setfill('0');
+      for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+        oss << std::setw(2) << static_cast<int>(contents[j]) << " ";
+      }
+      oss << std::setfill(' ');
+      for (; j < i + bytes_per_line; j++) {
+        oss << "   ";
+      }
+      for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+        char c = contents[j];
+        if (c < ' ' || c >= 0x7f) {
+          c = '.';
+        }
+        oss << c;
+      }
+      oss << std::endl;
+    }
+  }
+  if (num_backtrace_frames > 0) {
+    oss << backtrace_string(backtrace_frames, num_backtrace_frames);
+  }
+
+  return oss.str();
+}
+
+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 << std::endl;
+
+  for (auto it = leaks.begin(); it != leaks.end(); it++) {
+      oss << it->ToString(log_contents);
+  }
+
+  return oss.str();
+}
+
+std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
+  UnreachableMemoryInfo info;
+  if (!GetUnreachableMemory(info, limit)) {
+    return "Failed to get unreachable memory\n";
+  }
+
+  return info.ToString(log_contents);
+}
+
+bool LogUnreachableMemory(bool log_contents, size_t limit) {
+  UnreachableMemoryInfo info;
+  if (!GetUnreachableMemory(info, limit)) {
+    return false;
+  }
+
+  for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
+    ALOGE("%s", it->ToString(log_contents).c_str());
+  }
+  return true;
+}
+
+
+bool NoLeaks() {
+  UnreachableMemoryInfo info;
+  if (!GetUnreachableMemory(info, 0)) {
+    return false;
+  }
+
+  return info.num_leaks == 0;
+}
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
new file mode 100644
index 0000000..7cca7c1
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include "LineBuffer.h"
+#include "ProcessMappings.h"
+#include "log.h"
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
+  char map_buffer[1024];
+  snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
+  int fd = open(map_buffer, O_RDONLY);
+  if (fd < 0) {
+    return false;
+  }
+  android::base::unique_fd fd_guard{fd};
+
+  LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
+  char* line;
+  size_t line_len;
+  while (line_buf.GetLine(&line, &line_len)) {
+    int name_pos;
+    char perms[5];
+    Mapping mapping{};
+    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
+        &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+      if (perms[0] == 'r') {
+        mapping.read = true;
+      }
+      if (perms[1] == 'w') {
+        mapping.write = true;
+      }
+      if (perms[2] == 'x') {
+        mapping.execute = true;
+      }
+      if (perms[3] == 'p') {
+        mapping.priv = true;
+      }
+      if ((size_t)name_pos < line_len) {
+        strlcpy(mapping.name, line + name_pos, sizeof(mapping.name));
+      }
+      mappings.emplace_back(mapping);
+    }
+  }
+  return true;
+}
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
new file mode 100644
index 0000000..d3b7496
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.h
@@ -0,0 +1,36 @@
+/*
+ * 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_PROCESS_MAPPING_H_
+#define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+
+#include "Allocator.h"
+
+struct Mapping {
+  uintptr_t begin;
+  uintptr_t end;
+  bool read;
+  bool write;
+  bool execute;
+  bool priv;
+  char name[96];
+};
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
+
+#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
new file mode 100644
index 0000000..aa5b344
--- /dev/null
+++ b/libmemunreachable/PtracerThread.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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 <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "log.h"
+#include "PtracerThread.h"
+
+class Stack {
+ public:
+  Stack(size_t size) : size_(size) {
+    int prot = PROT_READ | PROT_WRITE;
+    int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+    page_size_ = sysconf(_SC_PAGE_SIZE);
+    size_ += page_size_*2; // guard pages
+    base_ = mmap(NULL, size_, prot, flags, -1, 0);
+    if (base_ == MAP_FAILED) {
+      base_ = NULL;
+      size_ = 0;
+      return;
+    }
+    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base_, size_, "libmemunreachable stack");
+    mprotect(base_, page_size_, PROT_NONE);
+    mprotect(top(), page_size_, PROT_NONE);
+  };
+  ~Stack() {
+    munmap(base_, size_);
+  };
+  void* top() {
+    return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
+  };
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Stack);
+
+  void *base_;
+  size_t size_;
+  size_t page_size_;
+};
+
+PtracerThread::PtracerThread(const std::function<int()>& func) :
+    child_pid_(0) {
+  stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
+  if (stack_->top() == nullptr) {
+    LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+  }
+
+  func_ = std::function<int()>{[&, func]() -> int {
+    // In the child thread, lock and unlock the mutex to wait for the parent
+    // to finish setting up for the child thread
+    std::unique_lock<std::mutex> lk(m_);
+    lk.unlock();
+    _exit(func());
+  }};
+}
+
+PtracerThread::~PtracerThread() {
+  Kill();
+  Join();
+  ClearTracer();
+  stack_ = nullptr;
+}
+
+bool PtracerThread::Start() {
+  std::unique_lock<std::mutex> lk(m_);
+
+  // Convert from void(*)(void*) to lambda with captures
+  auto proxy = [](void *arg) -> int {
+    prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
+    return (*reinterpret_cast<std::function<int()>*>(arg))();
+  };
+
+  child_pid_ = clone(proxy, stack_->top(),
+       CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
+       reinterpret_cast<void*>(&func_));
+  if (child_pid_ < 0) {
+    ALOGE("failed to clone child: %s", strerror(errno));
+    return false;
+  }
+
+  SetTracer(child_pid_);
+
+  lk.unlock();
+
+  return true;
+}
+
+int PtracerThread::Join() {
+  if (child_pid_ == -1) {
+    return -1;
+  }
+  int status;
+  int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
+  if (ret < 0) {
+    ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+    return -1;
+  }
+
+  child_pid_ = -1;
+
+  if (WIFEXITED(status)) {
+    return WEXITSTATUS(status);
+  } else if (WIFSIGNALED(status)) {
+    return -WTERMSIG(status);
+  } else {
+    ALOGE("unexpected status %x", status);
+    return -1;
+  }
+}
+
+void PtracerThread::Kill() {
+  if (child_pid_ == -1) {
+    return;
+  }
+
+  syscall(SYS_tkill, child_pid_, SIGKILL);
+}
+
+void PtracerThread::SetTracer(pid_t tracer_pid) {
+  prctl(PR_SET_PTRACER, tracer_pid);
+}
+
+void PtracerThread::ClearTracer() {
+  prctl(PR_SET_PTRACER, 0);
+}
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
new file mode 100644
index 0000000..4d6ca9a
--- /dev/null
+++ b/libmemunreachable/PtracerThread.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+#define LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+
+#include <functional>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+
+class Stack;
+
+// PtracerThread is similar to std::thread, except that it creates a "thread"
+// that can ptrace the other threads.  The thread is actually a separate
+// process, with its own thread group, but shares address space and fds with
+// the parent.
+class PtracerThread {
+ public:
+  PtracerThread(const std::function<int()>& func);
+  ~PtracerThread();
+  bool Start();
+  int Join();
+ private:
+  void SetTracer(pid_t);
+  void ClearTracer();
+  void Kill();
+  DISALLOW_COPY_AND_ASSIGN(PtracerThread);
+  std::unique_ptr<Stack> stack_;
+  std::function<int()> func_;
+  std::mutex m_;
+  pid_t child_pid_;
+};
+
+#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
new file mode 100644
index 0000000..61a47de
--- /dev/null
+++ b/libmemunreachable/README.md
@@ -0,0 +1,71 @@
+libmemunreachable
+================
+
+Introduction
+--------------
+libmemunreachable is a zero-overhead native memory leak detector.  It uses an imprecise mark-and-sweep garbage collector pass over all native memory, reporting any unreachable blocks as leaks.  It is similar to the [Heap Checker from tcmalloc](http://htmlpreview.github.io/?https://github.com/gperftools/gperftools/blob/master/doc/heap_checker.html), but with a few key differences to remove the overhead.  Instead of instrumenting every call to malloc and free, it queries the allocator (jemalloc) for active allocations when leak detection is requested.  In addition, it performs a very short stop-the-world data collection on the main process, and then forks a copy of the process to perform the mark-and-sweep, minimizing disruption to the original process.
+
+In the default (zero-overhead) mode, the returned data on leaks is limited to the address, approximate (upper bound) size, and the the first 32 bytes of the contents of the leaked allocation.  If malloc_debug backtraces are enabled they will be included in the leak information, but backtracing allocations requires significant overhead.
+
+----------
+
+Usage
+-------
+
+### C interface ###
+
+#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
+Writes a description of leaked memory to the log.  A summary is always written, followed by details of up to `limit` leaks.  If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+#### `bool NoLeaks()` ####
+Returns `true` if no unreachable memory was found.
+
+### C++ interface ###
+
+####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks.  Returns true if leak detection succeeded.
+
+#### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
+Returns a description of leaked memory.  A summary is always written, followed by details of up to `limit` leaks.  If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+Implementation
+-------------------
+The sequence of steps required to perform a leak detection pass is divided into three processes - the original process, the collection process, and the sweeper process.
+
+ 1. *Original process*: Leak detection is requested by calling `GetUnreachableMemory()`
+ 2. Allocations are disabled using `malloc_disable()`
+ 3. The collection process is spawned.  The collection process is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa.
+ 4. *Collection process*: All threads in the original process are paused with `ptrace()`.
+ 5. Registers contents, active stack areas, and memory mapping information are collected.
+ 6. *Original process*: Allocations are re-enabled using `malloc_enable()`, but all threads are still paused with `ptrace()`.
+ 7. *Collection process*: The sweeper process is spawned using a normal `fork()`.  The sweeper process has a copy of all memory from the original process, including all the data collected by the collection process.
+ 8. Collection process releases all threads from `ptrace` and exits
+ 9. *Original process*: All threads continue, the thread that called `GetUnreachableMemory()` blocks waiting for leak data over a pipe.
+ 10. *Sweeper process*: A list of all active allocations is produced by examining the memory mappings and calling `malloc_iterate()` on any heap mappings.
+ 11. A list of all roots is produced from globals (.data and .bss sections of binaries), and registers and stacks from each thread.
+ 12. The mark-and-sweep pass is performed starting from roots.
+ 13. Unmarked allocations are sent over the pipe back to the original process.
+
+----------
+
+
+Components
+---------------
+- `MemUnreachable.cpp`: Entry points, implements the sequencing described above.
+- `PtracerThread.cpp`: Used to clone the collection process with shared address space.
+- `ThreadCapture.cpp`: Pauses threads in the main process and collects register contents.
+- `ProcessMappings.cpp`: Collects snapshots of `/proc/pid/maps`.
+- `HeapWalker.cpp`: Performs the mark-and-sweep pass over active allocations.
+- `LeakPipe.cpp`: transfers data describing leaks from the sweeper process to the original process.
+
+
+Heap allocator requirements
+----------------------------------
+libmemunreachable requires a small interface to the allocator in order to collect information about active allocations.
+
+ - `malloc_disable()`: prevent any thread from mutating internal allocator state.
+ - `malloc enable()`: re-enable allocations in all threads.
+ - `malloc_iterate()`: call a callback on each active allocation in a given heap region.
+ - `malloc_backtrace()`: return the backtrace from when the allocation at the given address was allocated, if it was collected.
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
new file mode 100644
index 0000000..019deea
--- /dev/null
+++ b/libmemunreachable/ScopedAlarm.h
@@ -0,0 +1,52 @@
+/*
+ * 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_SCOPED_ALARM_H_
+#define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+
+#include <signal.h>
+
+#include <chrono>
+#include <functional>
+
+class ScopedAlarm {
+ public:
+  ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
+    func_ = func;
+    struct sigaction oldact{};
+    struct sigaction act{};
+    act.sa_handler = [](int) {
+      ScopedAlarm::func_();
+    };
+    sigaction(SIGALRM, &act, &oldact);
+
+    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);
+  }
+  ~ScopedAlarm() {
+    itimerval t = itimerval{};
+    setitimer(ITIMER_REAL, &t, NULL);
+    struct sigaction act{};
+    act.sa_handler = SIG_DFL;
+    sigaction(SIGALRM, &act, NULL);
+  }
+ private:
+  static std::function<void()> func_;
+};
+#endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
new file mode 100644
index 0000000..4f96376
--- /dev/null
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -0,0 +1,113 @@
+/*
+ * 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_SCOPED_DISABLE_MALLOC_H_
+#define LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+
+#include <memory>
+
+#include "android-base/macros.h"
+
+#include "bionic.h"
+#include "log.h"
+#include "ScopedAlarm.h"
+
+class DisableMallocGuard{
+ public:
+  DisableMallocGuard() : disabled_(false){}
+  ~DisableMallocGuard() {
+    Enable();
+  }
+
+  void Disable() {
+    if (!disabled_) {
+      malloc_disable();
+      disabled_ = true;
+    }
+  }
+
+  void Enable() {
+    if (disabled_) {
+      malloc_enable();
+      disabled_ = false;
+    }
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
+  bool disabled_;
+};
+
+// Any calls to malloc or free from this thread will deadlock as long as this
+// object is in scope.  Calls to malloc from other threads may succeed (for
+// example if the allocation is satisfied out of the thread's tcache), or may
+// block until the object is destroyed.
+//
+// Don't call fork() while malloc is disabled, it needs the same locks held
+// here.
+class ScopedDisableMalloc {
+ public:
+  ScopedDisableMalloc() {
+    disable_malloc_.Disable();
+  }
+
+  ~ScopedDisableMalloc() {
+    disable_malloc_.Enable();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
+  DisableMallocGuard disable_malloc_;
+};
+
+class ScopedDisableMallocTimeout {
+ public:
+  ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
+    timeout_(timeout), timed_out_(false), disable_malloc_() {
+    Disable();
+  }
+
+  ~ScopedDisableMallocTimeout() {
+    Enable();
+  }
+
+  bool timed_out() {
+    return timed_out_;
+  }
+
+  void Enable() {
+    disable_malloc_.Enable();
+    alarm_ = nullptr;
+  }
+
+  void Disable() {
+    // set up the alarm before disabling malloc so unique_ptr can be used
+    alarm_ = std::make_unique<ScopedAlarm>(timeout_, [&]() {
+      disable_malloc_.Enable();
+      timed_out_ = true;
+    });
+
+    disable_malloc_.Disable();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedDisableMallocTimeout);
+  std::chrono::milliseconds timeout_;
+  bool timed_out_;
+  std::unique_ptr<ScopedAlarm> alarm_;
+  DisableMallocGuard disable_malloc_;
+};
+
+#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
new file mode 100644
index 0000000..9beef9a
--- /dev/null
+++ b/libmemunreachable/ScopedPipe.h
@@ -0,0 +1,81 @@
+/*
+ * 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_SCOPED_PIPE_H_
+#define LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+
+#include <unistd.h>
+
+#include "log.h"
+
+class ScopedPipe {
+ public:
+  ScopedPipe() : pipefd_{-1, -1} {
+    int ret = pipe2(pipefd_, O_CLOEXEC);
+    if (ret < 0) {
+      LOG_ALWAYS_FATAL("failed to open pipe");
+    }
+  }
+  ~ScopedPipe() {
+    Close();
+  }
+
+  ScopedPipe(ScopedPipe&& other) {
+    SetReceiver(other.ReleaseReceiver());
+    SetSender(other.ReleaseSender());
+  }
+
+  ScopedPipe& operator = (ScopedPipe&& other) {
+    SetReceiver(other.ReleaseReceiver());
+    SetSender(other.ReleaseSender());
+    return *this;
+  }
+
+  void CloseReceiver() {
+    close(ReleaseReceiver());
+  }
+
+  void CloseSender() {
+    close(ReleaseSender());
+  }
+
+  void Close() {
+    CloseReceiver();
+    CloseSender();
+  }
+
+  int Receiver() { return pipefd_[0]; }
+  int Sender() { return pipefd_[1]; }
+
+  int ReleaseReceiver() {
+    int ret = Receiver();
+    SetReceiver(-1);
+    return ret;
+  }
+
+  int ReleaseSender() {
+    int ret = Sender();
+    SetSender(-1);
+    return ret;
+  }
+
+ private:
+  void SetReceiver(int fd) { pipefd_[0] = fd; };
+  void SetSender(int fd) { pipefd_[1] = fd; };
+
+  int pipefd_[2];
+};
+#endif
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
new file mode 100644
index 0000000..45e8c81
--- /dev/null
+++ b/libmemunreachable/Semaphore.h
@@ -0,0 +1,56 @@
+/*
+ * 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_SEMAPHORE_H_
+#define LIBMEMUNREACHABLE_SEMAPHORE_H_
+
+#include <chrono>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+class Semaphore {
+ public:
+  Semaphore(int count = 0) : count_(count) {}
+  ~Semaphore() = default;
+
+  void Wait(std::chrono::milliseconds ms) {
+    std::unique_lock<std::mutex> lk(m_);
+    cv_.wait_for(lk, ms, [&]{
+      if (count_ > 0) {
+        count_--;
+        return true;
+      }
+      return false;
+    });
+  }
+  void Post() {
+    {
+      std::lock_guard<std::mutex> lk(m_);
+      count_++;
+    }
+    cv_.notify_one();
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Semaphore);
+
+  int count_;
+  std::mutex m_;
+  std::condition_variable cv_;
+};
+
+
+#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
new file mode 100644
index 0000000..6357840
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -0,0 +1,370 @@
+/*
+ * 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 "ThreadCapture.h"
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "log.h"
+
+// bionic interfaces used:
+// atoi
+// strlcat
+// writev
+
+// bionic interfaces reimplemented to avoid allocation:
+// getdents64
+
+// Convert a pid > 0 to a string.  sprintf might allocate, so we can't use it.
+// Returns a pointer somewhere in buf to a null terminated string, or NULL
+// on error.
+static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+  if (pid <= 0) {
+    return nullptr;
+  }
+
+  char *ptr = buf + len - 1;
+  *ptr = 0;
+  while (pid > 0) {
+    ptr--;
+    if (ptr < buf) {
+      return nullptr;
+    }
+    *ptr = '0' + (pid % 10);
+    pid /= 10;
+  }
+
+  return ptr;
+}
+
+class ThreadCaptureImpl {
+ public:
+  ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
+  ~ThreadCaptureImpl() {}
+  bool ListThreads(TidList& tids);
+  bool CaptureThreads();
+  bool ReleaseThreads();
+  bool ReleaseThread(pid_t tid);
+  bool CapturedThreadInfo(ThreadInfoList& threads);
+  void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+ private:
+  int CaptureThread(pid_t tid);
+  bool ReleaseThread(pid_t tid, unsigned int signal);
+  int PtraceAttach(pid_t tid);
+  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<ThreadCaptureImpl> allocator_;
+  pid_t pid_;
+  std::function<void(pid_t)> inject_test_func_;
+};
+
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
+    captured_threads_(allocator), allocator_(allocator), pid_(pid) {
+}
+
+bool ThreadCaptureImpl::ListThreads(TidList& tids) {
+  tids.clear();
+
+  char pid_buf[11];
+  char path[256] = "/proc/";
+  char* pid_str = pid_to_str(pid_buf, sizeof(pid_buf), pid_);
+  if (!pid_str) {
+    return false;
+  }
+  strlcat(path, pid_str, sizeof(path));
+  strlcat(path, "/task", sizeof(path));
+
+  int fd = open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+  if (fd < 0) {
+    ALOGE("failed to open %s: %s", path, strerror(errno));
+    return false;
+  }
+  android::base::unique_fd fd_guard{fd};
+
+  struct linux_dirent64 {
+    uint64_t  d_ino;
+    int64_t   d_off;
+    uint16_t  d_reclen;
+    char      d_type;
+    char      d_name[];
+  } __attribute((packed));
+  char dirent_buf[4096];
+  ssize_t nread;
+  do {
+    nread = syscall(SYS_getdents64, fd, dirent_buf, sizeof(dirent_buf));
+    if (nread < 0) {
+      ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+      return false;
+    } else if (nread > 0) {
+      ssize_t off = 0;
+      while (off < nread) {
+        linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
+        off += dirent->d_reclen;
+        pid_t tid = atoi(dirent->d_name);
+        if (tid <= 0) {
+          continue;
+        }
+        tids.push_back(tid);
+      }
+    }
+
+  } while (nread != 0);
+
+  return true;
+}
+
+bool ThreadCaptureImpl::CaptureThreads() {
+  TidList tids{allocator_};
+
+  bool found_new_thread;
+  do {
+    if (!ListThreads(tids)) {
+      ReleaseThreads();
+      return false;
+    }
+
+    found_new_thread = false;
+
+    for (auto it = tids.begin(); it != tids.end(); it++) {
+      auto captured = captured_threads_.find(*it);
+      if (captured == captured_threads_.end()) {
+        if (CaptureThread(*it) < 0) {
+          ReleaseThreads();
+          return false;
+        }
+        found_new_thread = true;
+      }
+    }
+  } while (found_new_thread);
+
+  return true;
+}
+
+// Detatches from a thread, delivering signal if nonzero, logs on error
+void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
+  void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
+  if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
+    ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
+        strerror(errno));
+  }
+}
+
+// Attaches to and pauses thread.
+// Returns 1 on attach, 0 on tid not found, -1 and logs on error
+int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
+  int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
+  if (ret < 0) {
+    ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
+        strerror(errno));
+    return -1;
+  }
+
+  if (inject_test_func_) {
+    inject_test_func_(tid);
+  }
+
+  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
+    if (errno == ESRCH) {
+      return 0;
+    } else {
+      ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
+          strerror(errno));
+      PtraceDetach(tid, 0);
+      return -1;
+    }
+  }
+  return 1;
+}
+
+bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
+  thread_info.tid = tid;
+
+  const unsigned int max_num_regs = 128; // larger than number of registers on any device
+  uintptr_t regs[max_num_regs];
+  struct iovec iovec;
+  iovec.iov_base = &regs;
+  iovec.iov_len = sizeof(regs);
+
+  if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
+    ALOGE("ptrace getregset for thread %d of process %d failed: %s",
+        tid, pid_, strerror(errno));
+    return false;
+  }
+
+  unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
+  thread_info.regs.assign(&regs[0], &regs[num_regs]);
+
+  const int sp =
+#if defined(__x86_64__)
+      offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
+#elif defined(__i386__)
+      offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
+#elif defined(__arm__)
+      offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
+#elif defined(__aarch64__)
+      offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
+#elif defined(__mips__) || defined(__mips64__)
+      offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
+#else
+#error Unrecognized architecture
+#endif
+      ;
+
+  // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
+
+  thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
+
+   return true;
+}
+
+int ThreadCaptureImpl::CaptureThread(pid_t tid) {
+  int ret = PtraceAttach(tid);
+  if (ret <= 0) {
+    return ret;
+  }
+
+  int status = 0;
+  if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
+    ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
+        strerror(errno));
+    PtraceDetach(tid, 0);
+    return -1;
+  }
+
+  if (!WIFSTOPPED(status)) {
+    ALOGE("thread %d of process %d was not paused after waitpid, killed?",
+        tid, pid_);
+    return 0;
+  }
+
+  unsigned int resume_signal = 0;
+
+  unsigned int signal =  WSTOPSIG(status);
+  if ((status >> 16) == PTRACE_EVENT_STOP) {
+    switch (signal) {
+      case SIGSTOP:
+      case SIGTSTP:
+      case SIGTTIN:
+      case SIGTTOU:
+        // group-stop signals
+        break;
+      case SIGTRAP:
+        // normal ptrace interrupt stop
+        break;
+      default:
+        ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
+            signal, tid, pid_);
+        return -1;
+    }
+  } else {
+    // signal-delivery-stop
+    resume_signal = signal;
+  }
+
+  captured_threads_[tid] = resume_signal;
+  return 1;
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
+  auto it = captured_threads_.find(tid);
+  if (it == captured_threads_.end()) {
+    return false;
+  }
+  return ReleaseThread(it->first, it->second);
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
+  PtraceDetach(tid, signal);
+  return true;
+}
+
+bool ThreadCaptureImpl::ReleaseThreads() {
+  bool ret = true;
+  for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+    if (ReleaseThread(it->first, it->second)) {
+      it = captured_threads_.erase(it);
+    } else {
+      it++;
+      ret = false;
+    }
+  }
+  return ret;
+}
+
+bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
+  threads.clear();
+
+  for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
+    ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
+    if (!PtraceThreadInfo(it->first, t)) {
+      return false;
+    }
+    threads.push_back(t);
+  }
+  return true;
+}
+
+ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
+  Allocator<ThreadCaptureImpl> impl_allocator = allocator;
+  impl_ = impl_allocator.make_unique(pid, impl_allocator);
+}
+
+ThreadCapture::~ThreadCapture() {}
+
+bool ThreadCapture::ListThreads(TidList& tids) {
+  return impl_->ListThreads(tids);
+}
+
+bool ThreadCapture::CaptureThreads() {
+  return impl_->CaptureThreads();
+}
+
+bool ThreadCapture::ReleaseThreads() {
+  return impl_->ReleaseThreads();
+}
+
+bool ThreadCapture::ReleaseThread(pid_t tid) {
+  return impl_->ReleaseThread(tid);
+}
+
+bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
+  return impl_->CapturedThreadInfo(threads);
+}
+
+void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
+  impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
+}
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
new file mode 100644
index 0000000..1022cad
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.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 LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+#define LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+
+#include <utility>
+
+#include "Allocator.h"
+
+struct ThreadInfo {
+  pid_t tid;
+  allocator::vector<uintptr_t> regs;
+  std::pair<uintptr_t, uintptr_t> stack;
+};
+
+using TidList = allocator::vector<pid_t>;
+using ThreadInfoList = allocator::vector<ThreadInfo>;
+
+class ThreadCaptureImpl;
+
+class ThreadCapture {
+public:
+  ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
+  ~ThreadCapture();
+
+  bool ListThreads(TidList& tids);
+  bool CaptureThreads();
+  bool ReleaseThreads();
+  bool ReleaseThread(pid_t tid);
+  bool CapturedThreadInfo(ThreadInfoList& threads);
+  void InjectTestFunc(std::function<void(pid_t)>&& f);
+
+private:
+  ThreadCapture(const ThreadCapture&) = delete;
+  void operator=(const ThreadCapture&) = delete;
+
+  Allocator<ThreadCaptureImpl>::unique_ptr impl_;
+};
+
+#endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
new file mode 100644
index 0000000..1e4ade1
--- /dev/null
+++ b/libmemunreachable/anon_vma_naming.h
@@ -0,0 +1,25 @@
+/*
+ * 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_ANON_VMA_NAMING_H_
+#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+
+#include <sys/prctl.h>
+
+#define PR_SET_VMA            0x53564d41
+#define  PR_SET_VMA_ANON_NAME 0
+
+#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
new file mode 100644
index 0000000..92de24a
--- /dev/null
+++ b/libmemunreachable/bionic.h
@@ -0,0 +1,33 @@
+/*
+ * 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_BIONIC_H_
+#define LIBMEMUNREACHABLE_BIONIC_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Exported from bionic */
+extern void malloc_disable();
+extern void malloc_enable();
+extern int malloc_iterate(uintptr_t base, size_t size,
+    void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
new file mode 100644
index 0000000..f4f01ce
--- /dev/null
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -0,0 +1,71 @@
+/*
+ * 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_MEMUNREACHABLE_H_
+#define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+
+#include <sys/cdefs.h>
+
+#ifdef __cplusplus
+
+#include <vector>
+#include <string>
+
+struct Leak {
+  uintptr_t begin;
+  size_t size;
+  size_t num_backtrace_frames;
+  static const size_t contents_length = 32;
+  char contents[contents_length];
+  static const size_t backtrace_length = 16;
+  uintptr_t backtrace_frames[backtrace_length];
+
+  std::string ToString(bool log_contents) const;
+};
+
+struct UnreachableMemoryInfo {
+  std::vector<Leak> leaks;
+  size_t num_leaks;
+  size_t leak_bytes;
+  size_t num_allocations;
+  size_t allocation_bytes;
+
+  UnreachableMemoryInfo() {}
+  ~UnreachableMemoryInfo() {
+    // Clear the memory that holds the leaks, otherwise the next attempt to
+    // detect leaks may find the old data (for example in the jemalloc tcache)
+    // and consider all the leaks to be referenced.
+    memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
+  }
+
+  std::string ToString(bool log_contents) const;
+};
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100);
+
+std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
+
+#endif
+
+__BEGIN_DECLS
+
+bool LogUnreachableMemory(bool log_contents, size_t limit);
+
+bool NoLeaks();
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
new file mode 100644
index 0000000..cdfbfd9
--- /dev/null
+++ b/libmemunreachable/log.h
@@ -0,0 +1,24 @@
+/*
+ * 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_LOG_H_
+#define LIBMEMUNREACHABLE_LOG_H_
+
+#define LOG_TAG "libmemunreachable"
+
+#include <log/log.h>
+
+#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
new file mode 100644
index 0000000..d8e473e
--- /dev/null
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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 <Allocator.h>
+#include <sys/time.h>
+
+#include <chrono>
+#include <functional>
+#include <list>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+
+std::function<void()> ScopedAlarm::func_;
+
+using namespace std::chrono_literals;
+
+class AllocatorTest : public testing::Test {
+ protected:
+  AllocatorTest() : heap(), disable_malloc_() {}
+  virtual void SetUp() {
+    heap_count = 0;
+  }
+  virtual void TearDown() {
+    ASSERT_EQ(heap_count, 0);
+    ASSERT_TRUE(heap.empty());
+    ASSERT_FALSE(disable_malloc_.timed_out());
+  }
+  Heap heap;
+ private:
+  ScopedDisableMallocTimeout disable_malloc_;
+};
+
+TEST_F(AllocatorTest, simple) {
+  Allocator<char[100]> allocator(heap);
+  void *ptr = allocator.allocate();
+  ASSERT_TRUE(ptr != NULL);
+  allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, multiple) {
+  Allocator<char[100]> allocator(heap);
+  void *ptr1 = allocator.allocate();
+  ASSERT_TRUE(ptr1 != NULL);
+  void *ptr2 = allocator.allocate();
+  ASSERT_TRUE(ptr2 != NULL);
+  ASSERT_NE(ptr1, ptr2);
+  allocator.deallocate(ptr1);
+  void *ptr3 = allocator.allocate();
+  ASSERT_EQ(ptr1, ptr3);
+  allocator.deallocate(ptr3);
+  allocator.deallocate(ptr2);
+}
+
+TEST_F(AllocatorTest, many) {
+  const int num = 4096;
+  const int size = 128;
+  Allocator<char[size]> allocator(heap);
+  void *ptr[num];
+  for (int i = 0; i < num; i++) {
+    ptr[i] = allocator.allocate();
+    memset(ptr[i], 0xaa, size);
+    *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+  }
+
+  for (int i = 0; i < num; i++) {
+    for (int j = 0; j < num; j++) {
+      if (i != j) {
+        ASSERT_NE(ptr[i], ptr[j]);
+      }
+    }
+  }
+
+  for (int i = 0; i < num; i++) {
+    ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+    allocator.deallocate(ptr[i]);
+  }
+}
+
+TEST_F(AllocatorTest, large) {
+  const size_t size = 1024 * 1024;
+  Allocator<char[size]> allocator(heap);
+  void *ptr = allocator.allocate();
+  memset(ptr, 0xaa, size);
+  allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, many_large) {
+  const int num = 128;
+  const int size = 1024 * 1024;
+  Allocator<char[size]> allocator(heap);
+  void *ptr[num];
+  for (int i = 0; i < num; i++) {
+    ptr[i] = allocator.allocate();
+    memset(ptr[i], 0xaa, size);
+    *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+  }
+
+  for (int i = 0; i < num; i++) {
+    ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+    allocator.deallocate(ptr[i]);
+  }
+}
+
+TEST_F(AllocatorTest, copy) {
+  Allocator<char[100]> a(heap);
+  Allocator<char[200]> b = a;
+  Allocator<char[300]> c(b);
+  Allocator<char[100]> d(a);
+  Allocator<char[100]> e(heap);
+
+  ASSERT_EQ(a, b);
+  ASSERT_EQ(a, c);
+  ASSERT_EQ(a, d);
+  ASSERT_EQ(a, e);
+
+  void* ptr1 = a.allocate();
+  void* ptr2 = b.allocate();
+  void* ptr3 = c.allocate();
+  void* ptr4 = d.allocate();
+
+  b.deallocate(ptr1);
+  d.deallocate(ptr2);
+  a.deallocate(ptr3);
+  c.deallocate(ptr4);
+}
+
+TEST_F(AllocatorTest, stl_vector) {
+  auto v = allocator::vector<int>(Allocator<int>(heap));
+  for (int i = 0; i < 1024; i++) {
+    v.push_back(i);
+  }
+  for (int i = 0; i < 1024; i++) {
+    ASSERT_EQ(v[i], i);
+  }
+  v.clear();
+}
+
+TEST_F(AllocatorTest, stl_list) {
+  auto v = allocator::list<int>(Allocator<int>(heap));
+  for (int i = 0; i < 1024; i++) {
+    v.push_back(i);
+  }
+  int i = 0;
+  for (auto iter = v.begin(); iter != v.end(); iter++, i++) {
+    ASSERT_EQ(*iter, i);
+  }
+  v.clear();
+}
+
+TEST_F(AllocatorTest, shared) {
+  Allocator<int> allocator(heap);
+
+  Allocator<int>::shared_ptr ptr = allocator.make_shared(0);
+  {
+    auto ptr2 = ptr;
+  }
+  ASSERT_NE(ptr, nullptr);
+}
+
+TEST_F(AllocatorTest, unique) {
+  Allocator<int> allocator(heap);
+
+  Allocator<int>::unique_ptr ptr = allocator.make_unique(0);
+
+  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/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
new file mode 100644
index 0000000..9921eb6
--- /dev/null
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class HeapWalkerTest : public ::testing::Test {
+ public:
+  HeapWalkerTest() : disable_malloc_(), heap_() {}
+
+  void TearDown() {
+    ASSERT_TRUE(heap_.empty());
+    if (!HasFailure()) {
+      ASSERT_FALSE(disable_malloc_.timed_out());
+    }
+  }
+
+ protected:
+  ScopedDisableMallocTimeout disable_malloc_;
+  Heap heap_;
+};
+
+TEST_F(HeapWalkerTest, allocation) {
+  HeapWalker heap_walker(heap_);
+  ASSERT_TRUE(heap_walker.Allocation(3, 4));
+  ASSERT_TRUE(heap_walker.Allocation(2, 3));
+  ASSERT_TRUE(heap_walker.Allocation(4, 5));
+  ASSERT_TRUE(heap_walker.Allocation(6, 7));
+  ASSERT_TRUE(heap_walker.Allocation(0, 1));
+}
+
+TEST_F(HeapWalkerTest, overlap) {
+  HeapWalker heap_walker(heap_);
+  ASSERT_TRUE(heap_walker.Allocation(2, 3));
+  ASSERT_TRUE(heap_walker.Allocation(3, 4));
+  ASSERT_FALSE(heap_walker.Allocation(2, 3));
+  ASSERT_FALSE(heap_walker.Allocation(1, 3));
+  ASSERT_FALSE(heap_walker.Allocation(1, 4));
+  ASSERT_FALSE(heap_walker.Allocation(1, 5));
+  ASSERT_FALSE(heap_walker.Allocation(3, 4));
+  ASSERT_FALSE(heap_walker.Allocation(3, 5));
+  ASSERT_TRUE(heap_walker.Allocation(4, 5));
+  ASSERT_TRUE(heap_walker.Allocation(1, 2));
+}
+
+TEST_F(HeapWalkerTest, zero) {
+  HeapWalker heap_walker(heap_);
+  ASSERT_TRUE(heap_walker.Allocation(2, 2));
+  ASSERT_FALSE(heap_walker.Allocation(2, 2));
+  ASSERT_TRUE(heap_walker.Allocation(3, 3));
+  ASSERT_TRUE(heap_walker.Allocation(1, 1));
+  ASSERT_FALSE(heap_walker.Allocation(2, 3));
+}
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
+
+TEST_F(HeapWalkerTest, leak) {
+  void* buffer1[16]{};
+  char buffer2[16]{};
+  buffer1[0] = &buffer2[0] - sizeof(void*);
+  buffer1[1] = &buffer2[15] + sizeof(void*);
+
+  HeapWalker heap_walker(heap_);
+  heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+  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(1U, num_leaks);
+  EXPECT_EQ(16U, leaked_bytes);
+  ASSERT_EQ(1U, leaked.size());
+  EXPECT_EQ(buffer_begin(buffer2), leaked[0].begin);
+  EXPECT_EQ(buffer_end(buffer2), leaked[0].end);
+}
+
+TEST_F(HeapWalkerTest, live) {
+  const int from_buffer_entries = 4;
+  const int to_buffer_bytes = 16;
+
+  for (int i = 0; i < from_buffer_entries; i++) {
+    for (int j = 0; j < to_buffer_bytes; j++) {
+      void* buffer1[from_buffer_entries]{};
+      char buffer2[to_buffer_bytes]{};
+      buffer1[i] = &buffer2[j];
+
+      HeapWalker heap_walker(heap_);
+      heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+      heap_walker.Root(buffer_begin(buffer1), buffer_end(buffer1));
+
+      allocator::vector<Range> leaked(heap_);
+      size_t num_leaks = SIZE_T_MAX;
+      size_t leaked_bytes = SIZE_T_MAX;
+      ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+      EXPECT_EQ(0U, num_leaks);
+      EXPECT_EQ(0U, leaked_bytes);
+      EXPECT_EQ(0U, leaked.size());
+    }
+  }
+}
+
+TEST_F(HeapWalkerTest, unaligned) {
+  const int from_buffer_entries = 4;
+  const int to_buffer_bytes = 16;
+  void* buffer1[from_buffer_entries]{};
+  char buffer2[to_buffer_bytes]{};
+
+  buffer1[1] = &buffer2;
+
+  for (unsigned int i = 0; i < sizeof(uintptr_t); i++) {
+    for (unsigned int j = 0; j < sizeof(uintptr_t); j++) {
+      HeapWalker heap_walker(heap_);
+      heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+      heap_walker.Root(buffer_begin(buffer1) + i, buffer_end(buffer1) - j);
+
+      allocator::vector<Range> leaked(heap_);
+      size_t num_leaks = SIZE_T_MAX;
+      size_t leaked_bytes = SIZE_T_MAX;
+      ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+      EXPECT_EQ(0U, num_leaks);
+      EXPECT_EQ(0U, leaked_bytes);
+      EXPECT_EQ(0U, leaked.size());
+    }
+  }
+}
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
new file mode 100644
index 0000000..0747b12
--- /dev/null
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include <gtest/gtest.h>
+
+#include <memunreachable/memunreachable.h>
+
+void* ptr;
+
+class HiddenPointer {
+ public:
+  HiddenPointer(size_t size = 256) {
+    Set(malloc(size));
+  }
+  ~HiddenPointer() {
+    Free();
+  }
+  void* Get() {
+    return reinterpret_cast<void*>(~ptr_);
+  }
+  void Free() {
+    free(Get());
+    Set(nullptr);
+  }
+ private:
+  void Set(void* ptr) {
+    ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
+  }
+  volatile uintptr_t ptr_;
+};
+
+static void Ref(void* ptr) {
+  write(0, ptr, 0);
+}
+
+TEST(MemunreachableTest, clean) {
+  UnreachableMemoryInfo info;
+
+  ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+  ASSERT_TRUE(GetUnreachableMemory(info));
+  ASSERT_EQ(0U, info.leaks.size());
+}
+
+TEST(MemunreachableTest, stack) {
+  HiddenPointer hidden_ptr;
+
+  {
+    void* ptr = hidden_ptr.Get();
+    Ref(ptr);
+
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+
+    Ref(ptr);
+  }
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(1U, info.leaks.size());
+  }
+
+  hidden_ptr.Free();
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+}
+
+TEST(MemunreachableTest, global) {
+  HiddenPointer hidden_ptr;
+
+  ptr = hidden_ptr.Get();
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+
+  ptr = NULL;
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(1U, info.leaks.size());
+  }
+
+  hidden_ptr.Free();
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+}
+
+TEST(MemunreachableTest, tls) {
+  HiddenPointer hidden_ptr;
+  pthread_key_t key;
+  pthread_key_create(&key, NULL);
+
+  pthread_setspecific(key, hidden_ptr.Get());
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+
+  pthread_setspecific(key, nullptr);
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(1U, info.leaks.size());
+  }
+
+  hidden_ptr.Free();
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+
+  pthread_key_delete(key);
+}
+
+TEST(MemunreachableTest, twice) {
+  HiddenPointer hidden_ptr;
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(1U, info.leaks.size());
+  }
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(1U, info.leaks.size());
+  }
+
+  hidden_ptr.Free();
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+}
+
+TEST(MemunreachableTest, log) {
+  HiddenPointer hidden_ptr;
+
+  ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+  hidden_ptr.Free();
+
+  {
+    UnreachableMemoryInfo info;
+
+    ASSERT_TRUE(GetUnreachableMemory(info));
+    ASSERT_EQ(0U, info.leaks.size());
+  }
+}
+
+TEST(MemunreachableTest, notdumpable) {
+  ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
+
+  HiddenPointer hidden_ptr;
+
+  ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+  ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
+}
+
+TEST(MemunreachableTest, leak_lots) {
+  std::vector<HiddenPointer> hidden_ptrs;
+  hidden_ptrs.resize(1024);
+
+  ASSERT_TRUE(LogUnreachableMemory(true, 100));
+}
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
new file mode 100644
index 0000000..cefe94e
--- /dev/null
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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 "ThreadCapture.h"
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "ScopedDisableMalloc.h"
+#include "ScopedPipe.h"
+
+using namespace std::chrono_literals;
+
+class ThreadListTest : public ::testing::TestWithParam<int> {
+ public:
+  ThreadListTest() : stop_(false) {}
+
+  ~ThreadListTest() {
+    // pthread_join may return before the entry in /proc/pid/task/ is gone,
+    // loop until ListThreads only finds the main thread so the next test
+    // doesn't fail.
+    WaitForThreads();
+  }
+
+  virtual void TearDown() {
+    ASSERT_TRUE(heap.empty());
+  }
+
+ protected:
+  template<class Function>
+  void StartThreads(unsigned int threads, Function&& func) {
+    threads_.reserve(threads);
+    tids_.reserve(threads);
+    for (unsigned int i = 0; i < threads; i++) {
+      threads_.emplace_back([&, i, threads, this]() {
+        {
+          std::lock_guard<std::mutex> lk(m_);
+          tids_.push_back(gettid());
+          if (tids_.size() == threads) {
+            cv_start_.notify_one();
+          }
+        }
+
+        func();
+
+        {
+          std::unique_lock<std::mutex> lk(m_);
+          cv_stop_.wait(lk, [&] {return stop_;});
+        }
+      });
+    }
+
+    {
+      std::unique_lock<std::mutex> lk(m_);
+      cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+    }
+  }
+
+  void StopThreads() {
+    {
+      std::lock_guard<std::mutex> lk(m_);
+      stop_ = true;
+    }
+    cv_stop_.notify_all();
+
+    for (auto i = threads_.begin(); i != threads_.end(); i++) {
+      i->join();
+    }
+    threads_.clear();
+    tids_.clear();
+  }
+
+  std::vector<pid_t>& tids() {
+    return tids_;
+  }
+
+  Heap heap;
+
+ private:
+  void WaitForThreads() {
+    auto tids = TidList{heap};
+    ThreadCapture thread_capture{getpid(), heap};
+
+    for (unsigned int i = 0; i < 100; i++) {
+      EXPECT_TRUE(thread_capture.ListThreads(tids));
+      if (tids.size() == 1) {
+        break;
+      }
+      std::this_thread::sleep_for(10ms);
+    }
+    EXPECT_EQ(1U, tids.size());
+  }
+
+  std::mutex m_;
+  std::condition_variable cv_start_;
+  std::condition_variable cv_stop_;
+  bool stop_;
+  std::vector<pid_t> tids_;
+
+  std::vector<std::thread> threads_;
+};
+
+TEST_F(ThreadListTest, list_one) {
+  ScopedDisableMallocTimeout disable_malloc;
+
+  ThreadCapture thread_capture(getpid(), heap);
+
+  auto expected_tids = allocator::vector<pid_t>(1, getpid(), heap);
+  auto list_tids = allocator::vector<pid_t>(heap);
+
+  ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+  ASSERT_EQ(expected_tids, list_tids);
+
+  if (!HasFailure()) {
+    ASSERT_FALSE(disable_malloc.timed_out());
+  }
+}
+
+TEST_P(ThreadListTest, list_some) {
+  const unsigned int threads = GetParam() - 1;
+
+  StartThreads(threads, [](){});
+  std::vector<pid_t> expected_tids = tids();
+  expected_tids.push_back(getpid());
+
+  auto list_tids = allocator::vector<pid_t>(heap);
+
+  {
+    ScopedDisableMallocTimeout disable_malloc;
+
+    ThreadCapture thread_capture(getpid(), heap);
+
+    ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+    if (!HasFailure()) {
+      ASSERT_FALSE(disable_malloc.timed_out());
+    }
+  }
+
+  StopThreads();
+
+  std::sort(list_tids.begin(), list_tids.end());
+  std::sort(expected_tids.begin(), expected_tids.end());
+
+  ASSERT_EQ(expected_tids.size(), list_tids.size());
+  EXPECT_TRUE(std::equal(expected_tids.begin(), expected_tids.end(), list_tids.begin()));
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadListTest, ThreadListTest, ::testing::Values(1, 2, 10, 1024));
+
+class ThreadCaptureTest : public ThreadListTest {
+ public:
+  ThreadCaptureTest() {}
+  ~ThreadCaptureTest() {}
+  void Fork(std::function<void()>&& child_init,
+      std::function<void()>&& child_cleanup,
+      std::function<void(pid_t)>&& parent) {
+
+    ScopedPipe start_pipe;
+    ScopedPipe stop_pipe;
+
+    int pid = fork();
+
+    if (pid == 0) {
+      // child
+      child_init();
+      EXPECT_EQ(1, TEMP_FAILURE_RETRY(write(start_pipe.Sender(), "+", 1))) << strerror(errno);
+      char buf;
+      EXPECT_EQ(1, TEMP_FAILURE_RETRY(read(stop_pipe.Receiver(), &buf, 1))) << strerror(errno);
+      child_cleanup();
+      _exit(0);
+    } else {
+      // parent
+      ASSERT_GT(pid, 0);
+      char buf;
+      ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(start_pipe.Receiver(), &buf, 1))) << strerror(errno);
+
+      parent(pid);
+
+      ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(stop_pipe.Sender(), "+", 1))) << strerror(errno);
+      siginfo_t info{};
+      ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) << strerror(errno);
+    }
+  }
+};
+
+TEST_P(ThreadCaptureTest, capture_some) {
+  const unsigned int threads = GetParam();
+
+  Fork([&](){
+    // child init
+    StartThreads(threads - 1, [](){});
+  },
+  [&](){
+    // child cleanup
+    StopThreads();
+  },
+  [&](pid_t child){
+    // parent
+    ASSERT_GT(child, 0);
+
+    {
+      ScopedDisableMallocTimeout disable_malloc;
+
+      ThreadCapture thread_capture(child, heap);
+      auto list_tids = allocator::vector<pid_t>(heap);
+
+      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+      ASSERT_EQ(threads, list_tids.size());
+
+      ASSERT_TRUE(thread_capture.CaptureThreads());
+
+      auto thread_info = allocator::vector<ThreadInfo>(heap);
+      ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+      ASSERT_EQ(threads, thread_info.size());
+      ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+      if (!HasFailure()) {
+        ASSERT_FALSE(disable_malloc.timed_out());
+      }
+}
+  });
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
+
+TEST_F(ThreadCaptureTest, capture_kill) {
+  int ret = fork();
+
+  if (ret == 0) {
+    // child
+    sleep(10);
+  } else {
+    // parent
+    ASSERT_GT(ret, 0);
+
+    {
+      ScopedDisableMallocTimeout disable_malloc;
+
+      ThreadCapture thread_capture(ret, heap);
+      thread_capture.InjectTestFunc([&](pid_t tid){
+        syscall(SYS_tgkill, ret, tid, SIGKILL);
+        usleep(10000);
+      });
+      auto list_tids = allocator::vector<pid_t>(heap);
+
+      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+      ASSERT_EQ(1U, list_tids.size());
+
+      ASSERT_FALSE(thread_capture.CaptureThreads());
+
+      if (!HasFailure()) {
+        ASSERT_FALSE(disable_malloc.timed_out());
+      }
+    }
+  }
+}
+
+TEST_F(ThreadCaptureTest, capture_signal) {
+  const int sig = SIGUSR1;
+
+  ScopedPipe pipe;
+
+  // For signal handler
+  static ScopedPipe* g_pipe;
+
+  Fork([&](){
+    // child init
+    pipe.CloseReceiver();
+
+    g_pipe = &pipe;
+
+    struct sigaction act{};
+    act.sa_handler = [](int){
+      char buf = '+';
+      write(g_pipe->Sender(), &buf, 1);
+      g_pipe->CloseSender();
+    };
+    sigaction(sig, &act, NULL);
+    sigset_t set;
+    sigemptyset(&set);
+    sigaddset(&set, sig);
+    pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+  },
+  [&](){
+    // child cleanup
+    g_pipe = nullptr;
+    pipe.Close();
+  },
+  [&](pid_t child){
+    // parent
+    ASSERT_GT(child, 0);
+    pipe.CloseSender();
+
+    {
+      ScopedDisableMallocTimeout disable_malloc;
+
+      ThreadCapture thread_capture(child, heap);
+      thread_capture.InjectTestFunc([&](pid_t tid){
+        syscall(SYS_tgkill, child, tid, sig);
+        usleep(10000);
+      });
+      auto list_tids = allocator::vector<pid_t>(heap);
+
+      ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+      ASSERT_EQ(1U, list_tids.size());
+
+      ASSERT_TRUE(thread_capture.CaptureThreads());
+
+      auto thread_info = allocator::vector<ThreadInfo>(heap);
+      ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+      ASSERT_EQ(1U, thread_info.size());
+      ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+      usleep(100000);
+      char buf;
+      ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+      ASSERT_EQ(buf, '+');
+
+      if (!HasFailure()) {
+        ASSERT_FALSE(disable_malloc.timed_out());
+      }
+    }
+  });
+}
diff --git a/libnativeloader/Android.mk b/libnativeloader/Android.mk
index 5e65c4c..6c064c7 100644
--- a/libnativeloader/Android.mk
+++ b/libnativeloader/Android.mk
@@ -17,7 +17,8 @@
 LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
 LOCAL_LDFLAGS := -ldl
 LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_SHARED_LIBRARY)
 
 # Shared library for host
@@ -34,7 +35,8 @@
 LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
 LOCAL_LDFLAGS := -ldl
 LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 # Static library for host
@@ -50,5 +52,6 @@
 LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
 LOCAL_LDFLAGS := -ldl
 LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
similarity index 100%
rename from include/nativeloader/native_loader.h
rename to libnativeloader/include/nativeloader/native_loader.h
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
index 2060df4..281b6c8 100644
--- a/libnetutils/Android.mk
+++ b/libnetutils/Android.mk
@@ -16,6 +16,9 @@
 
 LOCAL_CFLAGS := -Werror
 
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/include/netutils/dhcp.h b/libnetutils/include/netutils/dhcp.h
similarity index 100%
rename from include/netutils/dhcp.h
rename to libnetutils/include/netutils/dhcp.h
diff --git a/include/netutils/ifc.h b/libnetutils/include/netutils/ifc.h
similarity index 100%
rename from include/netutils/ifc.h
rename to libnetutils/include/netutils/ifc.h
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 16052e2..e309027 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -27,7 +27,7 @@
 #include <sys/limits.h>
 
 #define LOG_TAG "packagelistparser"
-#include <utils/Log.h>
+#include <cutils/log.h>
 
 #include <packagelistparser/packagelistparser.h>
 
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index ad0500d..00a0357 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <memory>
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
@@ -162,11 +163,11 @@
 
 static void removeUidProcessGroups(const char *uid_path)
 {
-    DIR *uid = opendir(uid_path);
+    std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
     if (uid != NULL) {
         struct dirent cur;
         struct dirent *dir;
-        while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
+        while ((readdir_r(uid.get(), &cur, &dir) == 0) && dir) {
             char path[PROCESSGROUP_MAX_PATH_LEN];
 
             if (dir->d_type != DT_DIR) {
@@ -181,20 +182,19 @@
             SLOGV("removing %s\n", path);
             rmdir(path);
         }
-        closedir(uid);
     }
 }
 
 void removeAllProcessGroups()
 {
     SLOGV("removeAllProcessGroups()");
-    DIR *root = opendir(PROCESSGROUP_CGROUP_PATH);
+    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(PROCESSGROUP_CGROUP_PATH), closedir);
     if (root == NULL) {
         SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno));
     } else {
         struct dirent cur;
         struct dirent *dir;
-        while ((readdir_r(root, &cur, &dir) == 0) && dir) {
+        while ((readdir_r(root.get(), &cur, &dir) == 0) && dir) {
             char path[PROCESSGROUP_MAX_PATH_LEN];
 
             if (dir->d_type != DT_DIR) {
@@ -209,7 +209,6 @@
             SLOGV("removing %s\n", path);
             rmdir(path);
         }
-        closedir(root);
     }
 }
 
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index cdb586d..4e87a98 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -21,6 +21,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <memory>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -130,11 +131,10 @@
 }
 
 void ProcessCallStack::update() {
-    DIR *dp;
     struct dirent *ep;
     struct dirent entry;
 
-    dp = opendir(PATH_SELF_TASK);
+    std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
     if (dp == NULL) {
         ALOGE("%s: Failed to update the process's call stacks: %s",
               __FUNCTION__, strerror(errno));
@@ -159,7 +159,7 @@
      * - Read every file in directory => get every tid
      */
     int code;
-    while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+    while ((code = readdir_r(dp.get(), &entry, &ep)) == 0 && ep != NULL) {
         pid_t tid = -1;
         sscanf(ep->d_name, "%d", &tid);
 
@@ -198,8 +198,6 @@
         ALOGE("%s: Failed to readdir from %s: %s",
               __FUNCTION__, PATH_SELF_TASK, strerror(code));
     }
-
-    closedir(dp);
 }
 
 void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 6a5273f..87eda1b 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -18,7 +18,6 @@
 
 #include <utils/Log.h>
 #include <utils/Unicode.h>
-#include <utils/String8.h>
 #include <utils/threads.h>
 
 #include <memory.h>
@@ -77,7 +76,7 @@
         //printf("Created UTF-16 string from UTF-8 \"%s\":", in);
         //printHexData(1, str, buf->size(), 16, 1);
         //printf("\n");
-        
+
         return u16str;
     }
 
@@ -127,7 +126,7 @@
         mString = str;
         return;
     }
-    
+
     mString = getEmptyString();
 }
 
@@ -142,7 +141,7 @@
         mString = str;
         return;
     }
-    
+
     mString = getEmptyString();
 }
 
@@ -228,7 +227,7 @@
     } else if (otherLen == 0) {
         return NO_ERROR;
     }
-    
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
@@ -249,7 +248,7 @@
     } else if (otherLen == 0) {
         return NO_ERROR;
     }
-    
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 771d312..ad45282 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -362,7 +362,7 @@
 status_t String8::real_append(const char* other, size_t otherLen)
 {
     const size_t myLen = bytes();
-    
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize(myLen+otherLen+1);
     if (buf) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ddc91ca..c148d89 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -35,7 +35,7 @@
 #include <log/logd.h>
 #include <log/logger.h>
 #include <log/logprint.h>
-#include <utils/threads.h>
+#include <system/thread_defs.h>
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 4f517bb..8459bd3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <memory>
 
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -734,8 +735,8 @@
         EXPECT_FALSE(system(command));
         return;
     }
-    DIR *dir;
-    EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+    EXPECT_NE(nullptr, dir);
     if (!dir) {
         snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
         EXPECT_FALSE(system(command));
@@ -743,7 +744,7 @@
     }
     struct dirent *entry;
     unsigned count = 0;
-    while ((entry = readdir(dir))) {
+    while ((entry = readdir(dir.get()))) {
         if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
             continue;
         }
@@ -766,7 +767,6 @@
         free(line);
         unlink(command);
     }
-    closedir(dir);
     if (count > 1) {
         char *brk = strpbrk(second_last_line, "\r\n");
         if (!brk) {
diff --git a/logd/Android.mk b/logd/Android.mk
index feca8d5..203943c 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -42,6 +42,10 @@
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
+ifeq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_CFLAGS += -DAUDITD_ENFORCE_INTEGRITY=true
+endif
+
 include $(BUILD_EXECUTABLE)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 143fb04..fffc9ba 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -24,6 +24,7 @@
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <cutils/properties.h>
 #include <log/logger.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -32,6 +33,10 @@
 #include "LogAudit.h"
 #include "LogKlog.h"
 
+#ifndef AUDITD_ENFORCE_INTEGRITY
+#define AUDITD_ENFORCE_INTEGRITY false
+#endif
+
 #define KMSG_PRIORITY(PRI)                          \
     '<',                                            \
     '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
@@ -43,11 +48,10 @@
         logbuf(buf),
         reader(reader),
         fdDmesg(fdDmesg),
+        policyLoaded(false),
+        rebootToSafeMode(false),
         initialized(false) {
-    static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
-        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
-        ' ', 's', 't', 'a', 'r', 't', '\n' };
-    write(fdDmesg, auditd_message, sizeof(auditd_message));
+    logToDmesg("start");
 }
 
 bool LogAudit::onDataAvailable(SocketClient *cli) {
@@ -73,6 +77,46 @@
     return true;
 }
 
+void LogAudit::logToDmesg(const std::string& str)
+{
+    static const char prefix[] = { KMSG_PRIORITY(LOG_INFO),
+        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
+        ' ', '\0' };
+    std::string message = prefix + str + "\n";
+    write(fdDmesg, message.c_str(), message.length());
+}
+
+std::string LogAudit::getProperty(const std::string& name)
+{
+    char value[PROP_VALUE_MAX] = {0};
+    property_get(name.c_str(), value, "");
+    return value;
+}
+
+void LogAudit::enforceIntegrity() {
+    if (!AUDITD_ENFORCE_INTEGRITY) {
+        logToDmesg("integrity enforcement suppressed; not rebooting");
+    } else if (rebootToSafeMode) {
+        if (getProperty("persist.sys.safemode") == "1") {
+            logToDmesg("integrity enforcement suppressed; in safe mode");
+            return;
+        }
+
+        logToDmesg("enforcing integrity; rebooting to safe mode");
+        property_set("persist.sys.safemode", "1");
+
+        std::string buildDate = getProperty("ro.build.date.utc");
+        if (!buildDate.empty()) {
+            property_set("persist.sys.audit_safemode", buildDate.c_str());
+        }
+
+        property_set("sys.powerctl", "reboot");
+    } else {
+        logToDmesg("enforcing integrity: rebooting to recovery");
+        property_set("sys.powerctl", "reboot,recovery");
+    }
+}
+
 int LogAudit::logPrint(const char *fmt, ...) {
     if (fmt == NULL) {
         return -EINVAL;
@@ -94,7 +138,27 @@
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
 
-    bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+    bool loaded = strstr(str, " policy loaded ");
+
+    if (loaded) {
+        if (policyLoaded) {
+            // SELinux policy changes are not allowed
+            enforceIntegrity();
+        } else {
+            logToDmesg("policy loaded");
+            policyLoaded = true;
+        }
+    }
+
+    bool permissive = strstr(str, " enforcing=0") ||
+                      strstr(str, " permissive=1");
+
+    if (permissive) {
+        // SELinux in permissive mode is not allowed
+        enforceIntegrity();
+    }
+
+    bool info = loaded || permissive;
     if ((fdDmesg >= 0) && initialized) {
         struct iovec iov[3];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 8a82630..455ed58 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -24,12 +24,15 @@
     LogBuffer *logbuf;
     LogReader *reader;
     int fdDmesg;
+    bool policyLoaded;
+    bool rebootToSafeMode;
     bool initialized;
 
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
     int log(char *buf, size_t len);
     bool isMonotonic() { return logbuf->isMonotonic(); }
+    void allowSafeMode(bool allow = true) { rebootToSafeMode = allow; }
 
 protected:
     virtual bool onDataAvailable(SocketClient *cli);
@@ -38,6 +41,9 @@
     static int getLogSocket();
     int logPrint(const char *fmt, ...)
         __attribute__ ((__format__ (__printf__, 2, 3)));
+    void logToDmesg(const std::string& str);
+    std::string getProperty(const std::string& name);
+    void enforceIntegrity();
 };
 
 #endif
diff --git a/logd/README.property b/logd/README.property
index 6200d3e..4bc5541 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,7 +1,6 @@
 The properties that logd responds to are:
 
 name                       type default  description
-ro.logd.auditd             bool   true   Enable selinux audit daemon
 ro.logd.auditd.dmesg       bool   true   selinux audit messages duplicated and
                                          sent on to dmesg log
 persist.logd.security      bool   false  Enable security buffer.
diff --git a/logd/main.cpp b/logd/main.cpp
index bef40c7..f4d7464 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -245,6 +245,7 @@
 static sem_t reinit;
 static bool reinit_running = false;
 static LogBuffer *logBuf = NULL;
+static LogAudit *logAudit = NULL;
 
 static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
 
@@ -292,6 +293,10 @@
             logBuf->init();
             logBuf->initPrune(NULL);
         }
+
+        if (logAudit) {
+            logAudit->allowSafeMode();
+        }
     }
 
     return NULL;
@@ -512,25 +517,19 @@
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
 
-    bool auditd = property_get_bool("logd.auditd",
-                                    BOOL_DEFAULT_TRUE |
-                                    BOOL_DEFAULT_FLAG_PERSIST);
-    LogAudit *al = NULL;
-    if (auditd) {
-        al = new LogAudit(logBuf, reader,
-                          property_get_bool("logd.auditd.dmesg",
-                                            BOOL_DEFAULT_TRUE |
-                                            BOOL_DEFAULT_FLAG_PERSIST)
-                              ? fdDmesg
-                              : -1);
-    }
+    logAudit = new LogAudit(logBuf, reader,
+                            property_get_bool("logd.auditd.dmesg",
+                                              BOOL_DEFAULT_TRUE |
+                                              BOOL_DEFAULT_FLAG_PERSIST)
+                                ? fdDmesg
+                                : -1);
 
     LogKlog *kl = NULL;
     if (klogd) {
-        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, logAudit != NULL);
     }
 
-    readDmesg(al, kl);
+    readDmesg(logAudit, kl);
 
     // failure is an option ... messages are in dmesg (required by standard)
 
@@ -538,8 +537,9 @@
         delete kl;
     }
 
-    if (al && al->startListener()) {
-        delete al;
+    if (logAudit && logAudit->startListener()) {
+        delete logAudit;
+        logAudit = NULL;
     }
 
     TEMP_FAILURE_RETRY(pause());
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 28d6de7..ccbe0bf 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -408,7 +408,7 @@
         if (poll_fds[0].revents & POLLHUP) {
             int ret;
 
-            ret = waitpid(pid, &status, WNOHANG);
+            ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
             if (ret < 0) {
                 rc = errno;
                 ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
deleted file mode 100644
index c98efc2..0000000
--- a/metricsd/.clang-format
+++ /dev/null
@@ -1,10 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: Inline
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-BinPackArguments: false
-BinPackParameters: false
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-PointerAlignment: Left
-TabWidth: 2
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
new file mode 120000
index 0000000..f9066d4
--- /dev/null
+++ b/metricsd/.clang-format
@@ -0,0 +1 @@
+../../../build/tools/brillo-clang-format
\ No newline at end of file
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index c5f3d1d..2d04a7f 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -5,6 +5,6 @@
 LOCAL_SRC_FILES := sdcard.c
 LOCAL_MODULE := sdcard
 LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-LOCAL_SHARED_LIBRARIES := libcutils libpackagelistparser
+LOCAL_SHARED_LIBRARIES := liblog libcutils libpackagelistparser
 
 include $(BUILD_EXECUTABLE)
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 9ade759..fc2898e 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -55,6 +55,7 @@
 LOCAL_CONLYFLAGS += -std=gnu99
 
 LOCAL_SHARED_LIBRARIES := \
+    liblog \
     libcutils \
     libselinux \