Merge "usblib: usb_device_get_string decoding fixes."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e2e9577..dc45959 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -71,3 +71,6 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
diff --git a/adb/Android.mk b/adb/Android.mk
index 2b6df70..d76d175 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -101,6 +101,8 @@
     sysdeps_win32.cpp \
     sysdeps/win32/errno.cpp \
     sysdeps/win32/stat.cpp \
+    client/usb_dispatch.cpp \
+    client/usb_libusb.cpp \
     client/usb_windows.cpp \
 
 LIBADB_TEST_windows_SRCS := \
@@ -159,9 +161,7 @@
 
 # Even though we're building a static library (and thus there's no link step for
 # this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libmdnssd
-LOCAL_STATIC_LIBRARIES_linux := libusb
-LOCAL_STATIC_LIBRARIES_darwin := libusb
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libmdnssd libusb
 
 LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
 LOCAL_MULTILIB := first
@@ -230,9 +230,7 @@
     libdiagnose_usb \
     libmdnssd \
     libgmock_host \
-
-LOCAL_STATIC_LIBRARIES_linux := libusb
-LOCAL_STATIC_LIBRARIES_darwin := libusb
+    libusb \
 
 # Set entrypoint to wmain from sysdeps_win32.cpp instead of main
 LOCAL_LDFLAGS_windows := -municode
@@ -298,14 +296,12 @@
     libdiagnose_usb \
     liblog \
     libmdnssd \
+    libusb \
 
 # Don't use libcutils on Windows.
 LOCAL_STATIC_LIBRARIES_darwin := libcutils
 LOCAL_STATIC_LIBRARIES_linux := libcutils
 
-LOCAL_STATIC_LIBRARIES_darwin += libusb
-LOCAL_STATIC_LIBRARIES_linux += libusb
-
 LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 6b30be8..0e38897 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -240,7 +240,10 @@
     D("Calling send_connect");
     apacket* cp = get_apacket();
     cp->msg.command = A_CNXN;
-    cp->msg.arg0 = t->get_protocol_version();
+    // Send the max supported version, but because the transport is
+    // initialized to A_VERSION_MIN, this will be compatible with every
+    // device.
+    cp->msg.arg0 = A_VERSION;
     cp->msg.arg1 = t->get_max_payload();
 
     std::string connection_str = get_connection_string();
diff --git a/adb/adb.h b/adb/adb.h
index 3651413..21e5d4b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -44,7 +44,12 @@
 #define A_AUTH 0x48545541
 
 // ADB protocol version.
-#define A_VERSION 0x01000000
+// Version revision:
+// 0x01000000: original
+// 0x01000001: skip checksum (Dec 2017)
+#define A_VERSION_MIN 0x01000000
+#define A_VERSION_SKIP_CHECKSUM 0x01000001
+#define A_VERSION 0x01000001
 
 // Used for help/version information.
 #define ADB_VERSION_MAJOR 1
@@ -53,7 +58,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 39
+#define ADB_SERVER_VERSION 40
 
 using TransportId = uint64_t;
 class atransport;
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 8120199..a5e6f23 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -41,8 +41,6 @@
 #include "transport.h"
 #include "usb.h"
 
-using namespace std::literals;
-
 using android::base::StringPrintf;
 
 // RAII wrappers for libusb.
@@ -222,7 +220,7 @@
 
     // Use size_t for interface_num so <iostream>s don't mangle it.
     size_t interface_num;
-    uint16_t zero_mask;
+    uint16_t zero_mask = 0;
     uint8_t bulk_in = 0, bulk_out = 0;
     size_t packet_size = 0;
     bool found_adb = false;
@@ -372,9 +370,9 @@
 #endif
     }
 
-    auto result =
-        std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
-                                     interface_num, bulk_in, bulk_out, zero_mask, packet_size);
+    std::unique_ptr<usb_handle> result(new usb_handle(device_address, device_serial,
+                                                      std::move(handle), interface_num, bulk_in,
+                                                      bulk_out, zero_mask, packet_size));
     usb_handle* usb_handle_raw = result.get();
 
     {
@@ -397,7 +395,7 @@
     // hack around this by inserting a sleep.
     auto thread = std::thread([device]() {
         std::string device_path = get_device_dev_path(device);
-        std::this_thread::sleep_for(1s);
+        std::this_thread::sleep_for(std::chrono::seconds(1));
 
         process_device(device);
         if (--connecting_devices == 0) {
@@ -448,8 +446,8 @@
     }
 }
 
-static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
-                            void*) {
+static LIBUSB_CALL int hotplug_callback(libusb_context*, libusb_device* device,
+                                        libusb_hotplug_event event, void*) {
     // We're called with the libusb lock taken. Call these on a separate thread outside of this
     // function so that the usb_handle mutex is always taken before the libusb mutex.
     static std::once_flag once;
@@ -493,59 +491,60 @@
     libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
 }
 
+static LIBUSB_CALL void transfer_callback(libusb_transfer* transfer) {
+    transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
+
+    LOG(DEBUG) << info->name << " transfer callback entered";
+
+    // Make sure that the original submitter has made it to the condition_variable wait.
+    std::unique_lock<std::mutex> lock(info->mutex);
+
+    LOG(DEBUG) << info->name << " callback successfully acquired lock";
+
+    if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+        LOG(WARNING) << info->name << " transfer failed: " << libusb_error_name(transfer->status);
+        info->Notify();
+        return;
+    }
+
+    // usb_read() can return when receiving some data.
+    if (info->is_bulk_out && transfer->actual_length != transfer->length) {
+        LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
+        transfer->length -= transfer->actual_length;
+        transfer->buffer += transfer->actual_length;
+        int rc = libusb_submit_transfer(transfer);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to submit " << info->name
+                         << " transfer: " << libusb_error_name(rc);
+            transfer->status = LIBUSB_TRANSFER_ERROR;
+            info->Notify();
+        }
+        return;
+    }
+
+    if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
+        LOG(DEBUG) << "submitting zero-length write";
+        transfer->length = 0;
+        int rc = libusb_submit_transfer(transfer);
+        if (rc != 0) {
+            LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
+            transfer->status = LIBUSB_TRANSFER_ERROR;
+            info->Notify();
+        }
+        return;
+    }
+
+    LOG(VERBOSE) << info->name << "transfer fully complete";
+    info->Notify();
+}
+
 // Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.
 static int perform_usb_transfer(usb_handle* h, transfer_info* info,
                                 std::unique_lock<std::mutex> device_lock) {
     libusb_transfer* transfer = info->transfer;
 
     transfer->user_data = info;
-    transfer->callback = [](libusb_transfer* transfer) {
-        transfer_info* info = static_cast<transfer_info*>(transfer->user_data);
-
-        LOG(DEBUG) << info->name << " transfer callback entered";
-
-        // Make sure that the original submitter has made it to the condition_variable wait.
-        std::unique_lock<std::mutex> lock(info->mutex);
-
-        LOG(DEBUG) << info->name << " callback successfully acquired lock";
-
-        if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
-            LOG(WARNING) << info->name
-                         << " transfer failed: " << libusb_error_name(transfer->status);
-            info->Notify();
-            return;
-        }
-
-        // usb_read() can return when receiving some data.
-        if (info->is_bulk_out && transfer->actual_length != transfer->length) {
-            LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
-            transfer->length -= transfer->actual_length;
-            transfer->buffer += transfer->actual_length;
-            int rc = libusb_submit_transfer(transfer);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to submit " << info->name
-                             << " transfer: " << libusb_error_name(rc);
-                transfer->status = LIBUSB_TRANSFER_ERROR;
-                info->Notify();
-            }
-            return;
-        }
-
-        if (should_perform_zero_transfer(transfer->endpoint, transfer->length, info->zero_mask)) {
-            LOG(DEBUG) << "submitting zero-length write";
-            transfer->length = 0;
-            int rc = libusb_submit_transfer(transfer);
-            if (rc != 0) {
-                LOG(WARNING) << "failed to submit zero-length write: " << libusb_error_name(rc);
-                transfer->status = LIBUSB_TRANSFER_ERROR;
-                info->Notify();
-            }
-            return;
-        }
-
-        LOG(VERBOSE) << info->name << "transfer fully complete";
-        info->Notify();
-    };
+    transfer->callback = transfer_callback;
 
     LOG(DEBUG) << "locking " << info->name << " transfer_info mutex";
     std::unique_lock<std::mutex> lock(info->mutex);
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 61981b1..9751ebf 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <algorithm>
 #include <mutex>
 #include <thread>
 
@@ -40,6 +41,8 @@
 #include "sysdeps/chrono.h"
 #include "transport.h"
 
+namespace native {
+
 /** Structure usb_handle describes our connection to the usb device via
   AdbWinApi.dll. This structure is returned from usb_open() routine and
   is expected in each subsequent call that is accessing the device.
@@ -48,13 +51,7 @@
   rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
   ability to break a thread out of pipe IO.
 */
-struct usb_handle {
-    /// Previous entry in the list of opened usb handles
-    usb_handle* prev;
-
-    /// Next entry in the list of opened usb handles
-    usb_handle* next;
-
+struct usb_handle : public ::usb_handle {
     /// Handle to USB interface
     ADBAPIHANDLE adb_interface;
 
@@ -78,9 +75,7 @@
 static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
 
 /// List of opened usb handles
-static usb_handle handle_list = {
-    .prev = &handle_list, .next = &handle_list,
-};
+static std::vector<usb_handle*> handle_list;
 
 /// Locker for the list of opened usb handles
 static std::mutex& usb_lock = *new std::mutex();
@@ -131,11 +126,9 @@
 int usb_close(usb_handle* handle);
 
 int known_device_locked(const wchar_t* dev_name) {
-    usb_handle* usb;
-
     if (NULL != dev_name) {
         // Iterate through the list looking for the name match.
-        for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+        for (usb_handle* usb : handle_list) {
             // In Windows names are not case sensetive!
             if ((NULL != usb->interface_name) && (0 == wcsicmp(usb->interface_name, dev_name))) {
                 return 1;
@@ -168,10 +161,7 @@
     }
 
     // Not in the list. Add this handle to the list.
-    handle->next = &handle_list;
-    handle->prev = handle_list.prev;
-    handle->prev->next = handle;
-    handle->next->prev = handle;
+    handle_list.push_back(handle);
 
     return 1;
 }
@@ -274,10 +264,6 @@
         goto fail;
     }
 
-    // Set linkers back to the handle
-    ret->next = ret;
-    ret->prev = ret;
-
     // Create interface.
     ret->adb_interface = AdbCreateInterfaceByName(interface_name);
     if (NULL == ret->adb_interface) {
@@ -484,13 +470,8 @@
         // Remove handle from the list
         {
             std::lock_guard<std::mutex> lock(usb_lock);
-
-            if ((handle->next != handle) && (handle->prev != handle)) {
-                handle->next->prev = handle->prev;
-                handle->prev->next = handle->next;
-                handle->prev = handle;
-                handle->next = handle;
-            }
+            handle_list.erase(std::remove(handle_list.begin(), handle_list.end(), handle),
+                              handle_list.end());
         }
 
         // Cleanup handle
@@ -623,7 +604,9 @@
     // Need to acquire lock to safely walk the list which might be modified
     // by another thread.
     std::lock_guard<std::mutex> lock(usb_lock);
-    for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+    for (usb_handle* usb : handle_list) {
         usb_kick_locked(usb);
     }
 }
+
+}  // namespace native
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 0c7e1f9..f9f80c0 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -262,6 +262,7 @@
         env["HOSTNAME"] = GetHostName();
         env["LOGNAME"] = pw->pw_name;
         env["SHELL"] = pw->pw_shell;
+        env["TMPDIR"] = "/data/local/tmp";
         env["USER"] = pw->pw_name;
     }
 
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 14eb16b..eb4df97 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -118,7 +118,7 @@
 bool is_socket_spec(const std::string& spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             return true;
         }
     }
@@ -128,7 +128,7 @@
 bool is_local_socket_spec(const std::string& spec) {
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             return true;
         }
     }
@@ -170,7 +170,7 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("socket type %s is unavailable on this platform",
                                       it.first.c_str());
@@ -213,7 +213,7 @@
 
     for (const auto& it : kLocalSocketTypes) {
         std::string prefix = it.first + ":";
-        if (StartsWith(spec, prefix.c_str())) {
+        if (StartsWith(spec, prefix)) {
             if (!it.second.available) {
                 *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
                                       spec.c_str());
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 089a1ec..5cf2450 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -163,7 +163,12 @@
 
 void send_packet(apacket* p, atransport* t) {
     p->msg.magic = p->msg.command ^ 0xffffffff;
-    p->msg.data_check = calculate_apacket_checksum(p);
+    // compute a checksum for connection/auth packets for compatibility reasons
+    if (t->get_protocol_version() >= A_VERSION_SKIP_CHECKSUM) {
+        p->msg.data_check = 0;
+    } else {
+        p->msg.data_check = calculate_apacket_checksum(p);
+    }
 
     print_packet("send", p);
 
@@ -1089,10 +1094,6 @@
     return true;
 }
 
-bool check_data(apacket* p) {
-    return calculate_apacket_checksum(p) == p->msg.data_check;
-}
-
 #if ADB_HOST
 std::shared_ptr<RSA> atransport::NextKey() {
     if (keys_.empty()) keys_ = adb_auth_get_private_keys();
diff --git a/adb/transport.h b/adb/transport.h
index 8c101fd..86cd992 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -66,7 +66,9 @@
     atransport(ConnectionState state = kCsOffline)
         : id(NextTransportId()), connection_state_(state) {
         transport_fde = {};
-        protocol_version = A_VERSION;
+        // Initialize protocol to min version for compatibility with older versions.
+        // Version will be updated post-connect.
+        protocol_version = A_VERSION_MIN;
         max_payload = MAX_PAYLOAD;
     }
     virtual ~atransport() {}
@@ -223,7 +225,6 @@
 void unregister_usb_transport(usb_handle* usb);
 
 bool check_header(apacket* p, atransport* t);
-bool check_data(apacket* p);
 
 void close_usb_devices();
 void close_usb_devices(std::function<bool(const atransport*)> predicate);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9cd378c..d6c84da 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -77,11 +77,6 @@
         return -1;
     }
 
-    if (!check_data(p)) {
-        D("bad data: terminated (data)");
-        return -1;
-    }
-
     return 0;
 }
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index a00ed5e..3474820 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -109,10 +109,6 @@
             goto err_msg;
         }
     }
-    if (!check_data(p)) {
-        D("remote usb: check_data failed, skip it");
-        goto err_msg;
-    }
     return 0;
 
 err_msg:
@@ -143,11 +139,6 @@
         }
     }
 
-    if (!check_data(p)) {
-        LOG(ERROR) << "remote usb: check_data failed";
-        return -1;
-    }
-
     return 0;
 }
 #endif
@@ -195,7 +186,7 @@
 }
 
 bool should_use_libusb() {
-#if defined(_WIN32) || !ADB_HOST
+#if !ADB_HOST
     return false;
 #else
     static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
diff --git a/adb/usb.h b/adb/usb.h
index f428ede..cd83c42 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -29,8 +29,8 @@
     void usb_kick(handle_ref_type h);                            \
     size_t usb_get_max_packet_size(handle_ref_type)
 
-#if defined(_WIN32) || !ADB_HOST
-// Windows and the daemon have a single implementation.
+#if !ADB_HOST
+// The daemon has a single implementation.
 
 struct usb_handle;
 ADB_USB_INTERFACE(usb_handle*);
diff --git a/base/Android.bp b/base/Android.bp
index ad0edf4..01800af 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -116,6 +116,7 @@
         "stringprintf_test.cpp",
         "strings_test.cpp",
         "test_main.cpp",
+        "test_utils_test.cpp",
     ],
     target: {
         android: {
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index f93c696..afff2c9 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -42,6 +42,10 @@
 // By default, output goes to logcat on Android and stderr on the host.
 // A process can use `SetLogger` to decide where all logging goes.
 // Implementations are provided for logcat, stderr, and dmesg.
+//
+// By default, the process' name is used as the log tag.
+// Code can choose a specific log tag by defining LOG_TAG
+// before including this header.
 
 // This header also provides assertions:
 //
@@ -63,6 +67,16 @@
 
 #include "android-base/macros.h"
 
+// Note: DO NOT USE DIRECTLY. Use LOG_TAG instead.
+#ifdef _LOG_TAG_INTERNAL
+#error "_LOG_TAG_INTERNAL must not be defined"
+#endif
+#ifdef LOG_TAG
+#define _LOG_TAG_INTERNAL LOG_TAG
+#else
+#define _LOG_TAG_INTERNAL nullptr
+#endif
+
 namespace android {
 namespace base {
 
@@ -201,10 +215,10 @@
 
 // Get an ostream that can be used for logging at the given severity and to the
 // given destination. The same notes as for LOG_STREAM apply.
-#define LOG_STREAM_TO(dest, severity)                                   \
-  ::android::base::LogMessage(__FILE__, __LINE__,                       \
-                              ::android::base::dest,                    \
-                              SEVERITY_LAMBDA(severity), -1).stream()
+#define LOG_STREAM_TO(dest, severity)                                           \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,        \
+                              SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, -1) \
+      .stream()
 
 // Logs a message to logcat on Android otherwise to stderr. If the severity is
 // FATAL it also causes an abort. For example:
@@ -231,10 +245,10 @@
 #define PLOG(severity) PLOG_TO(DEFAULT, severity)
 
 // Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                              \
-  LOGGING_PREAMBLE(severity) &&                                              \
-      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
-                                  SEVERITY_LAMBDA(severity), errno)          \
+#define PLOG_TO(dest, severity)                                                        \
+  LOGGING_PREAMBLE(severity) &&                                                        \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest,           \
+                                  SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL, errno) \
           .stream()
 
 // Marker that code is yet to be implemented.
@@ -247,23 +261,26 @@
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                                \
-  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                           \
-      ::android::base::LogMessage(                                              \
-          __FILE__, __LINE__, ::android::base::DEFAULT, ::android::base::FATAL, \
-          -1).stream()                                                          \
+#define CHECK(x)                                                                 \
+  LIKELY((x)) || ABORT_AFTER_LOG_FATAL_EXPR(false) ||                            \
+      ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
+                                  ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+              .stream()                                                          \
           << "Check failed: " #x << " "
 
+// clang-format off
 // Helper for CHECK_xx(x,y) macros.
-#define CHECK_OP(LHS, RHS, OP)                                              \
-  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);        \
-       UNLIKELY(!(_values.lhs OP _values.rhs));                             \
-       /* empty */)                                                         \
-  ABORT_AFTER_LOG_FATAL                                                     \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
-                              ::android::base::FATAL, -1).stream()          \
-      << "Check failed: " << #LHS << " " << #OP << " " << #RHS              \
-      << " (" #LHS "=" << _values.lhs << ", " #RHS "=" << _values.rhs << ") "
+#define CHECK_OP(LHS, RHS, OP)                                                                 \
+  for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                           \
+       UNLIKELY(!(_values.lhs OP _values.rhs));                                                \
+       /* empty */)                                                                            \
+  ABORT_AFTER_LOG_FATAL                                                                        \
+  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,                    \
+                              ::android::base::FATAL, _LOG_TAG_INTERNAL, -1)                   \
+          .stream()                                                                            \
+      << "Check failed: " << #LHS << " " << #OP << " " << #RHS << " (" #LHS "=" << _values.lhs \
+      << ", " #RHS "=" << _values.rhs << ") "
+// clang-format on
 
 // Check whether a condition holds between x and y, LOG(FATAL) if not. The value
 // of the expressions x and y is evaluated once. Extra logging can be appended
@@ -278,14 +295,17 @@
 #define CHECK_GE(x, y) CHECK_OP(x, y, >= )
 #define CHECK_GT(x, y) CHECK_OP(x, y, > )
 
+// clang-format off
 // Helper for CHECK_STRxx(s1,s2) macros.
 #define CHECK_STROP(s1, s2, sense)                                             \
   while (UNLIKELY((strcmp(s1, s2) == 0) != (sense)))                           \
     ABORT_AFTER_LOG_FATAL                                                      \
     ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT,  \
-                                ::android::base::FATAL, -1).stream()           \
+                                ::android::base::FATAL, _LOG_TAG_INTERNAL, -1) \
+        .stream()                                                              \
         << "Check failed: " << "\"" << (s1) << "\""                            \
         << ((sense) ? " == " : " != ") << "\"" << (s2) << "\""
+// clang-format on
 
 // Check for string (const char*) equality between s1 and s2, LOG(FATAL) if not.
 #define CHECK_STREQ(s1, s2) CHECK_STROP(s1, s2, true)
@@ -400,8 +420,8 @@
 // of a CHECK. The destructor will abort if the severity is FATAL.
 class LogMessage {
  public:
-  LogMessage(const char* file, unsigned int line, LogId id,
-             LogSeverity severity, int error);
+  LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, const char* tag,
+             int error);
 
   ~LogMessage();
 
@@ -410,12 +430,17 @@
   std::ostream& stream();
 
   // The routine that performs the actual logging.
-  static void LogLine(const char* file, unsigned int line, LogId id,
-                      LogSeverity severity, const char* msg);
+  static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                      const char* tag, const char* msg);
 
  private:
   const std::unique_ptr<LogMessageData> data_;
 
+  // TODO(b/35361699): remove these symbols once all prebuilds stop using it.
+  LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity, int error);
+  static void LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                      const char* msg);
+
   DISALLOW_COPY_AND_ASSIGN(LogMessage);
 };
 
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index f5f5c11..c11acb1 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -57,12 +57,18 @@
 extern template std::string Join(const std::vector<const char*>&, const std::string&);
 
 // Tests whether 's' starts with 'prefix'.
+// TODO: string_view
 bool StartsWith(const std::string& s, const char* prefix);
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
+bool StartsWith(const std::string& s, const std::string& prefix);
+bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
 
 // Tests whether 's' ends with 'suffix'.
+// TODO: string_view
 bool EndsWith(const std::string& s, const char* suffix);
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
+bool EndsWith(const std::string& s, const std::string& prefix);
+bool EndsWithIgnoreCase(const std::string& s, const std::string& prefix);
 
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 4cfa06b..2edafe3 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_BASE_TEST_UTILS_H
 #define ANDROID_BASE_TEST_UTILS_H
 
+#include <regex>
 #include <string>
 
 #include <android-base/macros.h>
@@ -70,4 +71,32 @@
   DISALLOW_COPY_AND_ASSIGN(CapturedStderr);
 };
 
+#define ASSERT_MATCH(str, pattern)                                             \
+  do {                                                                         \
+    if (!std::regex_search((str), std::regex((pattern)))) {                    \
+      FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << (str); \
+    }                                                                          \
+  } while (0)
+
+#define ASSERT_NOT_MATCH(str, pattern)                                                     \
+  do {                                                                                     \
+    if (std::regex_search((str), std::regex((pattern)))) {                                 \
+      FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
+    }                                                                                      \
+  } while (0)
+
+#define EXPECT_MATCH(str, pattern)                                                    \
+  do {                                                                                \
+    if (!std::regex_search((str), std::regex((pattern)))) {                           \
+      ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << (str); \
+    }                                                                                 \
+  } while (0)
+
+#define EXPECT_NOT_MATCH(str, pattern)                                                            \
+  do {                                                                                            \
+    if (std::regex_search((str), std::regex((pattern)))) {                                        \
+      ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << (str); \
+    }                                                                                             \
+  } while (0)
+
 #endif  // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 75078e5..0f2012a 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -187,8 +187,8 @@
 }
 #endif
 
-void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
-                  unsigned int line, const char* message) {
+void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
+                  const char* message) {
   struct tm now;
   time_t t = time(nullptr);
 
@@ -205,8 +205,8 @@
   static_assert(arraysize(log_characters) - 1 == FATAL + 1,
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
-  fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", ProgramInvocationName().c_str(),
-          severity_char, timestamp, getpid(), GetThreadId(), file, line, message);
+  fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", tag ? tag : "nullptr", severity_char, timestamp,
+          getpid(), GetThreadId(), file, line, message);
 }
 
 void DefaultAborter(const char* abort_message) {
@@ -344,14 +344,14 @@
 // checks/logging in a function.
 class LogMessageData {
  public:
-  LogMessageData(const char* file, unsigned int line, LogId id,
-                 LogSeverity severity, int error)
+  LogMessageData(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                 const char* tag, int error)
       : file_(GetFileBasename(file)),
         line_number_(line),
         id_(id),
         severity_(severity),
-        error_(error) {
-  }
+        tag_(tag),
+        error_(error) {}
 
   const char* GetFile() const {
     return file_;
@@ -365,6 +365,8 @@
     return severity_;
   }
 
+  const char* GetTag() const { return tag_; }
+
   LogId GetId() const {
     return id_;
   }
@@ -387,15 +389,19 @@
   const unsigned int line_number_;
   const LogId id_;
   const LogSeverity severity_;
+  const char* const tag_;
   const int error_;
 
   DISALLOW_COPY_AND_ASSIGN(LogMessageData);
 };
 
-LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
-                       LogSeverity severity, int error)
-    : data_(new LogMessageData(file, line, id, severity, error)) {
-}
+LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                       const char* tag, int error)
+    : data_(new LogMessageData(file, line, id, severity, tag, error)) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                       int error)
+    : LogMessage(file, line, id, severity, nullptr, error) {}
 
 LogMessage::~LogMessage() {
   // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
@@ -413,16 +419,16 @@
     // Do the actual logging with the lock held.
     std::lock_guard<std::mutex> lock(LoggingLock());
     if (msg.find('\n') == std::string::npos) {
-      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-              data_->GetSeverity(), msg.c_str());
+      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
+              data_->GetTag(), msg.c_str());
     } else {
       msg += '\n';
       size_t i = 0;
       while (i < msg.size()) {
         size_t nl = msg.find('\n', i);
         msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-                data_->GetSeverity(), &msg[i]);
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(), data_->GetSeverity(),
+                data_->GetTag(), &msg[i]);
         // Undo the zero-termination so we can give the complete message to the aborter.
         msg[nl] = '\n';
         i = nl + 1;
@@ -440,12 +446,17 @@
   return data_->GetBuffer();
 }
 
-void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
-                         LogSeverity severity, const char* message) {
-  const char* tag = ProgramInvocationName().c_str();
+void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                         const char* tag, const char* message) {
+  if (tag == nullptr) tag = ProgramInvocationName().c_str();
   Logger()(id, severity, tag, file, line, message);
 }
 
+void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
+                         const char* message) {
+  LogLine(file, line, id, severity, nullptr, message);
+}
+
 LogSeverity GetMinimumLogSeverity() {
     return gMinimumLogSeverity;
 }
diff --git a/base/strings.cpp b/base/strings.cpp
index bfdaf12..a8bb2a9 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -91,12 +91,20 @@
   return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
 
+bool StartsWith(const std::string& s, const std::string& prefix) {
+  return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+}
+
 bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
   return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
 }
 
-static bool EndsWith(const std::string& s, const char* suffix, bool case_sensitive) {
-  size_t suffix_length = strlen(suffix);
+bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) {
+  return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+}
+
+static bool EndsWith(const std::string& s, const char* suffix, size_t suffix_length,
+                     bool case_sensitive) {
   size_t string_length = s.size();
   if (suffix_length > string_length) {
     return false;
@@ -106,11 +114,19 @@
 }
 
 bool EndsWith(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, true);
+  return EndsWith(s, suffix, strlen(suffix), true);
+}
+
+bool EndsWith(const std::string& s, const std::string& suffix) {
+  return EndsWith(s, suffix.c_str(), suffix.size(), true);
 }
 
 bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
-  return EndsWith(s, suffix, false);
+  return EndsWith(s, suffix, strlen(suffix), false);
+}
+
+bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix) {
+  return EndsWith(s, suffix.c_str(), suffix.size(), false);
 }
 
 bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 121197c..b8639ea 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -253,6 +253,26 @@
   ASSERT_FALSE(android::base::EndsWithIgnoreCase("foobar", "FOO"));
 }
 
+TEST(strings, StartsWith_std_string) {
+  ASSERT_TRUE(android::base::StartsWith("hello", std::string{"hell"}));
+  ASSERT_FALSE(android::base::StartsWith("goodbye", std::string{"hell"}));
+}
+
+TEST(strings, StartsWithIgnoreCase_std_string) {
+  ASSERT_TRUE(android::base::StartsWithIgnoreCase("HeLlO", std::string{"hell"}));
+  ASSERT_FALSE(android::base::StartsWithIgnoreCase("GoOdByE", std::string{"hell"}));
+}
+
+TEST(strings, EndsWith_std_string) {
+  ASSERT_TRUE(android::base::EndsWith("hello", std::string{"lo"}));
+  ASSERT_FALSE(android::base::EndsWith("goodbye", std::string{"lo"}));
+}
+
+TEST(strings, EndsWithIgnoreCase_std_string) {
+  ASSERT_TRUE(android::base::EndsWithIgnoreCase("HeLlO", std::string{"lo"}));
+  ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
+}
+
 TEST(strings, EqualsIgnoreCase) {
   ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
   ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
new file mode 100644
index 0000000..597271a
--- /dev/null
+++ b/base/test_utils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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 "android-base/test_utils.h"
+
+#include <gtest/gtest-spi.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+TEST(TestUtilsTest, AssertMatch) {
+  ASSERT_MATCH("foobar", R"(fo+baz?r)");
+  EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, AssertNotMatch) {
+  ASSERT_NOT_MATCH("foobar", R"(foobaz)");
+  EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, ExpectMatch) {
+  EXPECT_MATCH("foobar", R"(fo+baz?r)");
+  EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+}
+
+TEST(TestUtilsTest, ExpectNotMatch) {
+  EXPECT_NOT_MATCH("foobar", R"(foobaz)");
+  EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 40ebde0..f81206a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -246,6 +246,32 @@
     {"watchdog_sdi_apps_reset", 106},
     {"smpl", 107},
     {"oem_modem_failed_to_powerup", 108},
+    {"reboot_normal", 109},
+    {"oem_lpass_cfg", 110},
+    {"oem_xpu_ns_error", 111},
+    {"power_key_press", 112},
+    {"hardware_reset", 113},
+    {"reboot_by_powerkey", 114},
+    {"reboot_verity", 115},
+    {"oem_rpm_undef_error", 116},
+    {"oem_crash_on_the_lk", 117},
+    {"oem_rpm_reset", 118},
+    {"oem_lpass_cfg", 119},
+    {"oem_xpu_ns_error", 120},
+    {"factory_cable", 121},
+    {"oem_ar6320_failed_to_powerup", 122},
+    {"watchdog_rpm_bite", 123},
+    {"power_on_cable", 124},
+    {"reboot_unknown", 125},
+    {"wireless_charger", 126},
+    {"0x776655ff", 127},
+    {"oem_thermal_bite_reset", 128},
+    {"charger", 129},
+    {"pon1", 130},
+    {"unknown", 131},
+    {"reboot_rtc", 132},
+    {"cold_boot", 133},
+    {"hard_rst", 134},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -289,8 +315,7 @@
   for (auto& s : knownReasons) {
     if (s == "cold") break;
     // Prefix defined as terminated by a nul or comma (,).
-    if (android::base::StartsWith(r, s.c_str()) &&
-        ((r.length() == s.length()) || (r[s.length()] == ','))) {
+    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {
       return true;
     }
   }
@@ -302,8 +327,7 @@
   for (auto& s : knownReasons) {
     if (s == "recovery") break;
     // Prefix defined as terminated by a nul or comma (,).
-    if (android::base::StartsWith(r, s.c_str()) &&
-        ((r.length() == s.length()) || (r[s.length()] == ','))) {
+    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {
       return true;
     }
   }
@@ -314,8 +338,7 @@
 bool isKnownRebootReason(const std::string& r) {
   for (auto& s : knownReasons) {
     // Prefix defined as terminated by a nul or comma (,).
-    if (android::base::StartsWith(r, s.c_str()) &&
-        ((r.length() == s.length()) || (r[s.length()] == ','))) {
+    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {
       return true;
     }
   }
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 17a9f3a..7fec47d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -4,9 +4,12 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wno-unused-argument",
+        "-Wno-unused-function",
         "-Wno-nullability-completeness",
         "-Os",
     ],
+    cpp_std: "gnu++17",
 
     local_include_dirs: ["include"],
 }
@@ -73,6 +76,7 @@
 
     whole_static_libs: [
         "libasync_safe",
+        "libcutils",
         "libdebuggerd",
     ],
 
@@ -148,27 +152,6 @@
         "libdebuggerd/utility.cpp",
     ],
 
-    target: {
-        android_arm: {
-            srcs: ["libdebuggerd/arm/machine.cpp"],
-        },
-        android_arm64: {
-            srcs: ["libdebuggerd/arm64/machine.cpp"],
-        },
-        android_mips: {
-            srcs: ["libdebuggerd/mips/machine.cpp"],
-        },
-        android_mips64: {
-            srcs: ["libdebuggerd/mips64/machine.cpp"],
-        },
-        android_x86: {
-            srcs: ["libdebuggerd/x86/machine.cpp"],
-        },
-        android_x86_64: {
-            srcs: ["libdebuggerd/x86_64/machine.cpp"],
-        },
-    },
-
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
@@ -193,7 +176,6 @@
         "libdebuggerd/test/elf_fake.cpp",
         "libdebuggerd/test/log_fake.cpp",
         "libdebuggerd/test/open_files_list_test.cpp",
-        "libdebuggerd/test/ptrace_fake.cpp",
         "libdebuggerd/test/tombstone_test.cpp",
     ],
 
@@ -218,6 +200,7 @@
 
     static_libs: [
         "libdebuggerd",
+        "libunwindstack",
     ],
 
     local_include_dirs: [
@@ -235,6 +218,16 @@
     },
 }
 
+cc_benchmark {
+    name: "debuggerd_benchmark",
+    defaults: ["debuggerd_defaults"],
+    srcs: ["debuggerd_benchmark.cpp"],
+    shared_libs: [
+        "libbase",
+        "libdebuggerd_client",
+    ],
+}
+
 cc_binary {
     name: "crash_dump",
     srcs: [
@@ -264,6 +257,7 @@
         "libbase",
         "liblog",
         "libprocinfo",
+        "libunwindstack",
     ],
 }
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 827420e..a1f0211 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -18,11 +18,11 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <stdlib.h>
-#include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/un.h>
+#include <sys/wait.h>
 #include <syscall.h>
 #include <unistd.h>
 
@@ -47,6 +47,8 @@
 #define ATRACE_TAG ATRACE_TAG_BIONIC
 #include <utils/Trace.h>
 
+#include <unwindstack/Regs.h>
+
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/tombstone.h"
 #include "libdebuggerd/utility.h"
@@ -58,21 +60,9 @@
 #include "util.h"
 
 using android::base::unique_fd;
-using android::base::ReadFileToString;
 using android::base::StringPrintf;
-using android::base::Trim;
 
-static std::string get_process_name(pid_t pid) {
-  std::string result = "<unknown>";
-  ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &result);
-  return result;
-}
-
-static std::string get_thread_name(pid_t tid) {
-  std::string result = "<unknown>";
-  ReadFileToString(StringPrintf("/proc/%d/comm", tid), &result);
-  return Trim(result);
-}
+using unwindstack::Regs;
 
 static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
   struct stat st;
@@ -90,8 +80,8 @@
 }
 
 // Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) {
-  if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
+static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) {
+  if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) {
     if (errno == EPERM) {
       pid_t tracer = get_tracer(tid);
       if (tracer != -1) {
@@ -108,18 +98,43 @@
   // Make sure that the task we attached to is actually part of the pid we're dumping.
   if (!pid_contains_tid(pid_proc_fd, tid)) {
     if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-      PLOG(FATAL) << "failed to detach from thread " << tid;
+      PLOG(WARNING) << "failed to detach from thread " << tid;
     }
     *error = StringPrintf("thread %d is not in process", tid);
     return false;
   }
 
-  // Put the task into ptrace-stop state.
-  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) {
-    PLOG(FATAL) << "failed to interrupt thread " << tid;
+  return true;
+}
+
+static bool wait_for_stop(pid_t tid, int* received_signal) {
+  while (true) {
+    int status;
+    pid_t result = waitpid(tid, &status, __WALL);
+    if (result != tid) {
+      PLOG(ERROR) << "waitpid failed on " << tid << " while detaching";
+      return false;
+    }
+
+    if (WIFSTOPPED(status)) {
+      if (status >> 16 == PTRACE_EVENT_STOP) {
+        *received_signal = 0;
+      } else {
+        *received_signal = WSTOPSIG(status);
+      }
+      return true;
+    }
+  }
+}
+
+// Interrupt a process and wait for it to be interrupted.
+static bool ptrace_interrupt(pid_t tid, int* received_signal) {
+  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) == 0) {
+    return wait_for_stop(tid, received_signal);
   }
 
-  return true;
+  PLOG(ERROR) << "failed to interrupt " << tid << " to detach";
+  return false;
 }
 
 static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
@@ -169,70 +184,39 @@
   return true;
 }
 
-static void signal_handler(int) {
-  // We can't log easily, because the heap might be corrupt.
-  // Just die and let the surrounding log context explain things.
-  _exit(1);
-}
+// Globals used by the abort handler.
+static pid_t g_target_thread = -1;
+static bool g_tombstoned_connected = false;
+static unique_fd g_tombstoned_socket;
+static unique_fd g_output_fd;
 
-static void abort_handler(pid_t target, const bool tombstoned_connected,
-                          unique_fd& tombstoned_socket, unique_fd& output_fd,
-                          const char* abort_msg) {
-  // If we abort before we get an output fd, contact tombstoned to let any
-  // potential listeners know that we failed.
-  if (!tombstoned_connected) {
-    if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) {
-      // We failed to connect, not much we can do.
-      LOG(ERROR) << "failed to connected to tombstoned to report failure";
-      _exit(1);
-    }
-  }
-
-  dprintf(output_fd.get(), "crash_dump failed to dump process");
-  if (target != 1) {
-    dprintf(output_fd.get(), " %d: %s\n", target, abort_msg);
-  } else {
-    dprintf(output_fd.get(), ": %s\n", abort_msg);
-  }
-
-  _exit(1);
-}
-
-static void drop_capabilities() {
-  ATRACE_CALL();
-  __user_cap_header_struct capheader;
-  memset(&capheader, 0, sizeof(capheader));
-  capheader.version = _LINUX_CAPABILITY_VERSION_3;
-  capheader.pid = 0;
-
-  __user_cap_data_struct capdata[2];
-  memset(&capdata, 0, sizeof(capdata));
-
-  if (capset(&capheader, &capdata[0]) == -1) {
-    PLOG(FATAL) << "failed to drop capabilities";
-  }
-
-  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
-    PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
-  }
-}
-
-int main(int argc, char** argv) {
-  atrace_begin(ATRACE_TAG, "before reparent");
-
-  pid_t target = getppid();
-  bool tombstoned_connected = false;
-  unique_fd tombstoned_socket;
-  unique_fd output_fd;
-
+static void Initialize(char** argv) {
   android::base::InitLogging(argv);
-  android::base::SetAborter([&](const char* abort_msg) {
-    abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg);
+  android::base::SetAborter([](const char* abort_msg) {
+    // If we abort before we get an output fd, contact tombstoned to let any
+    // potential listeners know that we failed.
+    if (!g_tombstoned_connected) {
+      if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd,
+                              kDebuggerdAnyIntercept)) {
+        // We failed to connect, not much we can do.
+        LOG(ERROR) << "failed to connected to tombstoned to report failure";
+        _exit(1);
+      }
+    }
+
+    dprintf(g_output_fd.get(), "crash_dump failed to dump process");
+    if (g_target_thread != 1) {
+      dprintf(g_output_fd.get(), " %d: %s\n", g_target_thread, abort_msg);
+    } else {
+      dprintf(g_output_fd.get(), ": %s\n", abort_msg);
+    }
+
+    _exit(1);
   });
 
   // Don't try to dump ourselves.
   struct sigaction action = {};
-  action.sa_handler = signal_handler;
+  action.sa_handler = SIG_DFL;
   debuggerd_register_handlers(&action);
 
   sigset_t mask;
@@ -240,216 +224,328 @@
   if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {
     PLOG(FATAL) << "failed to set signal mask";
   }
+}
 
+static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdDumpType* dump_type) {
   if (argc != 4) {
-    LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)";
+    LOG(FATAL) << "wrong number of args: " << argc << " (expected 4)";
   }
 
-  pid_t main_tid;
-  pid_t pseudothread_tid;
-  int dump_type;
-
-  if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits<pid_t>::max())) {
-    LOG(FATAL) << "invalid main tid: " << argv[1];
+  if (!android::base::ParseInt(argv[1], &g_target_thread, 1, std::numeric_limits<pid_t>::max())) {
+    LOG(FATAL) << "invalid target tid: " << argv[1];
   }
 
-  if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
+  if (!android::base::ParseInt(argv[2], pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {
     LOG(FATAL) << "invalid pseudothread tid: " << argv[2];
   }
 
-  if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) {
+  int dump_type_int;
+  if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) {
     LOG(FATAL) << "invalid requested dump type: " << argv[3];
   }
+  *dump_type = static_cast<DebuggerdDumpType>(dump_type_int);
+}
 
-  if (target == 1) {
-    LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")";
+static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
+                          std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_address) {
+  std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
+  ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
+  if (rc == -1) {
+    PLOG(FATAL) << "failed to read target ucontext";
+  } else if (rc != sizeof(CrashInfo)) {
+    LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+               << sizeof(CrashInfo);
   }
 
-  android::procinfo::ProcessInfo target_info;
-  if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) {
-    LOG(FATAL) << "failed to fetch process info for target " << main_tid;
+  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
+  if (crash_info->version != 1) {
+    LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version;
   }
 
-  if (main_tid != target_info.tid || target != target_info.pid) {
-    LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid
-               << ", received pid " << target_info.pid << ", tid " << target_info.tid;
+  *siginfo = crash_info->siginfo;
+  regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext));
+  *abort_address = crash_info->abort_msg_address;
+}
+
+// Wait for a process to clone and return the child's pid.
+// Note: this leaves the parent in PTRACE_EVENT_STOP.
+static pid_t wait_for_clone(pid_t pid, bool resume_child) {
+  int status;
+  pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
+  if (result == -1) {
+    PLOG(FATAL) << "failed to waitpid";
   }
 
-  // Open /proc/`getppid()` in the original process, and pass it down to the forked child.
-  std::string target_proc_path = "/proc/" + std::to_string(target);
+  if (WIFEXITED(status)) {
+    LOG(FATAL) << "traced process exited with status " << WEXITSTATUS(status);
+  } else if (WIFSIGNALED(status)) {
+    LOG(FATAL) << "traced process exited with signal " << WTERMSIG(status);
+  } else if (!WIFSTOPPED(status)) {
+    LOG(FATAL) << "process didn't stop? (status = " << status << ")";
+  }
+
+  if (status >> 8 != (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
+    LOG(FATAL) << "process didn't stop due to PTRACE_O_TRACECLONE (status = " << status << ")";
+  }
+
+  pid_t child;
+  if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &child) != 0) {
+    PLOG(FATAL) << "failed to get child pid via PTRACE_GETEVENTMSG";
+  }
+
+  int stop_signal;
+  if (!wait_for_stop(child, &stop_signal)) {
+    PLOG(FATAL) << "failed to waitpid on child";
+  }
+
+  CHECK_EQ(0, stop_signal);
+
+  if (resume_child) {
+    if (ptrace(PTRACE_CONT, child, 0, 0) != 0) {
+      PLOG(FATAL) << "failed to resume child (pid = " << child << ")";
+    }
+  }
+
+  return child;
+}
+
+static pid_t wait_for_vm_process(pid_t pseudothread_tid) {
+  // The pseudothread will double-fork, we want its grandchild.
+  pid_t intermediate = wait_for_clone(pseudothread_tid, true);
+  pid_t vm_pid = wait_for_clone(intermediate, false);
+  if (ptrace(PTRACE_DETACH, intermediate, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to detach from intermediate vm process";
+  }
+
+  return vm_pid;
+}
+
+int main(int argc, char** argv) {
+  atrace_begin(ATRACE_TAG, "before reparent");
+  pid_t target_process = getppid();
+
+  // Open /proc/`getppid()` before we daemonize.
+  std::string target_proc_path = "/proc/" + std::to_string(target_process);
   int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);
   if (target_proc_fd == -1) {
     PLOG(FATAL) << "failed to open " << target_proc_path;
   }
 
-  // Make sure our parent didn't die.
-  if (getppid() != target) {
-    PLOG(FATAL) << "parent died";
+  // Make sure getppid() hasn't changed.
+  if (getppid() != target_process) {
+    LOG(FATAL) << "parent died";
   }
-
   atrace_end(ATRACE_TAG);
 
   // Reparent ourselves to init, so that the signal handler can waitpid on the
   // original process to avoid leaving a zombie for non-fatal dumps.
+  // Move the input/output pipes off of stdout/stderr, out of paranoia.
+  unique_fd output_pipe(dup(STDOUT_FILENO));
+  unique_fd input_pipe(dup(STDIN_FILENO));
+
+  unique_fd fork_exit_read, fork_exit_write;
+  if (!Pipe(&fork_exit_read, &fork_exit_write)) {
+    PLOG(FATAL) << "failed to create pipe";
+  }
+
   pid_t forkpid = fork();
   if (forkpid == -1) {
     PLOG(FATAL) << "fork failed";
-  } else if (forkpid != 0) {
-    exit(0);
+  } else if (forkpid == 0) {
+    fork_exit_read.reset();
+  } else {
+    // We need the pseudothread to live until we get around to verifying the vm pid against it.
+    // The last thing it does is block on a waitpid on us, so wait until our child tells us to die.
+    fork_exit_write.reset();
+    char buf;
+    TEMP_FAILURE_RETRY(read(fork_exit_read.get(), &buf, sizeof(buf)));
+    _exit(0);
   }
 
   ATRACE_NAME("after reparent");
+  pid_t pseudothread_tid;
+  DebuggerdDumpType dump_type;
+  uintptr_t abort_address = 0;
+
+  Initialize(argv);
+  ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
 
   // Die if we take too long.
   //
   // Note: processes with many threads and minidebug-info can take a bit to
   //       unwind, do not make this too small. b/62828735
-  alarm(5);
+  alarm(30);
 
-  std::string attach_error;
-
-  std::map<pid_t, std::string> threads;
-
-  {
-    ATRACE_NAME("ptrace");
-    // Seize the main thread.
-    if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
-      LOG(FATAL) << attach_error;
-    }
-
-    // Seize the siblings.
-    {
-      std::set<pid_t> siblings;
-      if (!android::procinfo::GetProcessTids(target, &siblings)) {
-        PLOG(FATAL) << "failed to get process siblings";
-      }
-
-      // but not the already attached main thread.
-      siblings.erase(main_tid);
-      // or the handler pseudothread.
-      siblings.erase(pseudothread_tid);
-
-      for (pid_t sibling_tid : siblings) {
-        if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
-          LOG(WARNING) << attach_error;
-        } else {
-          threads.emplace(sibling_tid, get_thread_name(sibling_tid));
-        }
-      }
-    }
-  }
-
-  // Collect the backtrace map, open files, and process/thread names, while we still have caps.
-  std::unique_ptr<BacktraceMap> backtrace_map;
-  {
-    ATRACE_NAME("backtrace map");
-    backtrace_map.reset(BacktraceMap::Create(main_tid));
-    if (!backtrace_map) {
-      LOG(FATAL) << "failed to create backtrace map";
-    }
-  }
+  // Get the process name (aka cmdline).
+  std::string process_name = get_process_name(g_target_thread);
 
   // Collect the list of open files.
   OpenFilesList open_files;
   {
     ATRACE_NAME("open files");
-    populate_open_files_list(target, &open_files);
+    populate_open_files_list(g_target_thread, &open_files);
   }
 
-  std::string process_name = get_process_name(main_tid);
-  threads.emplace(main_tid, get_thread_name(main_tid));
+  // In order to reduce the duration that we pause the process for, we ptrace
+  // the threads, fetch their registers and associated information, and then
+  // fork a separate process as a snapshot of the process's address space.
+  std::set<pid_t> threads;
+  if (!android::procinfo::GetProcessTids(g_target_thread, &threads)) {
+    PLOG(FATAL) << "failed to get process threads";
+  }
 
-  // Drop our capabilities now that we've attached to the threads we care about.
+  std::map<pid_t, ThreadInfo> thread_info;
+  siginfo_t siginfo;
+  std::string error;
+
+  {
+    ATRACE_NAME("ptrace");
+    for (pid_t thread : threads) {
+      // Trace the pseudothread separately, so we can use different options.
+      if (thread == pseudothread_tid) {
+        continue;
+      }
+
+      if (!ptrace_seize_thread(target_proc_fd, thread, &error)) {
+        bool fatal = thread == g_target_thread;
+        LOG(fatal ? FATAL : WARNING) << error;
+      }
+
+      ThreadInfo info;
+      info.pid = target_process;
+      info.tid = thread;
+      info.process_name = process_name;
+      info.thread_name = get_thread_name(thread);
+
+      if (!ptrace_interrupt(thread, &info.signo)) {
+        PLOG(WARNING) << "failed to ptrace interrupt thread " << thread;
+        ptrace(PTRACE_DETACH, thread, 0, 0);
+        continue;
+      }
+
+      if (thread == g_target_thread) {
+        // Read the thread's registers along with the rest of the crash info out of the pipe.
+        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_address);
+        info.siginfo = &siginfo;
+        info.signo = info.siginfo->si_signo;
+      } else {
+        info.registers.reset(Regs::RemoteGet(thread));
+        if (!info.registers) {
+          PLOG(WARNING) << "failed to fetch registers for thread " << thread;
+          ptrace(PTRACE_DETACH, thread, 0, 0);
+          continue;
+        }
+      }
+
+      thread_info[thread] = std::move(info);
+    }
+  }
+
+  // Trace the pseudothread with PTRACE_O_TRACECLONE and tell it to fork.
+  if (!ptrace_seize_thread(target_proc_fd, pseudothread_tid, &error, PTRACE_O_TRACECLONE)) {
+    LOG(FATAL) << "failed to seize pseudothread: " << error;
+  }
+
+  if (TEMP_FAILURE_RETRY(write(output_pipe.get(), "\1", 1)) != 1) {
+    PLOG(FATAL) << "failed to write to pseudothread";
+  }
+
+  pid_t vm_pid = wait_for_vm_process(pseudothread_tid);
+  if (ptrace(PTRACE_DETACH, pseudothread_tid, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to detach from pseudothread";
+  }
+
+  // The pseudothread can die now.
+  fork_exit_write.reset();
+
+  // Defer the message until later, for readability.
+  bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
+  if (siginfo.si_signo == DEBUGGER_SIGNAL) {
+    wait_for_gdb = false;
+  }
+
+  // Detach from all of our attached threads before resuming.
+  for (const auto& [tid, thread] : thread_info) {
+    int resume_signal = thread.signo == DEBUGGER_SIGNAL ? 0 : thread.signo;
+    if (wait_for_gdb) {
+      resume_signal = 0;
+      if (tgkill(target_process, tid, SIGSTOP) != 0) {
+        PLOG(WARNING) << "failed to send SIGSTOP to " << tid;
+      }
+    }
+
+    LOG(DEBUG) << "detaching from thread " << tid;
+    if (ptrace(PTRACE_DETACH, tid, 0, resume_signal) != 0) {
+      PLOG(ERROR) << "failed to detach from thread " << tid;
+    }
+  }
+
+  // Drop our capabilities now that we've fetched all of the information we need.
   drop_capabilities();
 
   {
     ATRACE_NAME("tombstoned_connect");
-    const DebuggerdDumpType dump_type_enum = static_cast<DebuggerdDumpType>(dump_type);
-    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum;
-    tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum);
+    LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type;
+    g_tombstoned_connected =
+        tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type);
   }
 
-  // Write a '\1' to stdout to tell the crashing process to resume.
-  // It also restores the value of PR_SET_DUMPABLE at this point.
-  if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) {
-    PLOG(ERROR) << "failed to communicate to target process";
-  }
-
-  if (tombstoned_connected) {
-    if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) {
-      PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO";
+  if (g_tombstoned_connected) {
+    if (TEMP_FAILURE_RETRY(dup2(g_output_fd.get(), STDOUT_FILENO)) == -1) {
+      PLOG(ERROR) << "failed to dup2 output fd (" << g_output_fd.get() << ") to STDOUT_FILENO";
     }
   } else {
     unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
     TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));
-    output_fd = std::move(devnull);
+    g_output_fd = std::move(devnull);
   }
 
-  LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")";
-
-  // At this point, the thread that made the request has been attached and is
-  // in ptrace-stopped state. After resumption, the triggering signal that has
-  // been queued will be delivered.
-  if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) {
-    PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed";
-    exit(1);
-  }
-
-  siginfo_t siginfo = {};
-  {
-    ATRACE_NAME("wait_for_signal");
-    if (!wait_for_signal(main_tid, &siginfo)) {
-      printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno));
-      exit(1);
-    }
-  }
+  LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread
+            << ")";
 
   int signo = siginfo.si_signo;
   bool fatal_signal = signo != DEBUGGER_SIGNAL;
   bool backtrace = false;
-  uintptr_t abort_address = 0;
 
-  // si_value can represent three things:
+  // si_value is special when used with DEBUGGER_SIGNAL.
   //   0: dump tombstone
   //   1: dump backtrace
-  //   everything else: abort message address (implies dump tombstone)
-  if (siginfo.si_value.sival_int == 1) {
-    backtrace = true;
-  } else if (siginfo.si_value.sival_ptr != nullptr) {
-    abort_address = reinterpret_cast<uintptr_t>(siginfo.si_value.sival_ptr);
+  if (!fatal_signal) {
+    int si_val = siginfo.si_value.sival_int;
+    if (si_val == 0) {
+      backtrace = false;
+    } else if (si_val == 1) {
+      backtrace = true;
+    } else {
+      LOG(WARNING) << "unknown si_value value " << si_val;
+    }
   }
 
   // TODO: Use seccomp to lock ourselves down.
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(vm_pid, false));
+  if (!map) {
+    LOG(FATAL) << "failed to create backtrace map";
+  }
+
+  std::shared_ptr<unwindstack::Memory> process_memory = map->GetProcessMemory();
+  if (!process_memory) {
+    LOG(FATAL) << "failed to get unwindstack::Memory handle";
+  }
 
   std::string amfd_data;
   if (backtrace) {
     ATRACE_NAME("dump_backtrace");
-    dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
+    dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
   } else {
     ATRACE_NAME("engrave_tombstone");
-    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
-                      process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+    engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
+                      g_target_thread, abort_address, &open_files, &amfd_data);
   }
 
-  // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
-  // group-stop state, which is true as long as no stopping signals are sent.
-
-  bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false);
-  if (!fatal_signal || siginfo.si_code == SI_USER) {
-    // Don't wait_for_gdb when the process didn't actually crash.
-    wait_for_gdb = false;
-  }
-
-  // If the process crashed or we need to send it SIGSTOP for wait_for_gdb,
-  // get it in a state where it can receive signals, and then send the relevant
-  // signal.
-  if (wait_for_gdb || fatal_signal) {
-    if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) {
-      PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid;
-    }
-
-    if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) {
-      PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid;
+  if (fatal_signal) {
+    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
+    if (thread_info[target_process].thread_name != "system_server") {
+      activity_manager_notify(target_process, signo, amfd_data);
     }
   }
 
@@ -463,19 +559,12 @@
         "*     gdbclient.py -p %d\n"
         "*\n"
         "***********************************************************",
-        target, target);
-  }
-
-  if (fatal_signal) {
-    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
-    if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) {
-      activity_manager_notify(target, signo, amfd_data);
-    }
+        target_process, target_process);
   }
 
   // Close stdout before we notify tombstoned of completion.
   close(STDOUT_FILENO);
-  if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) {
+  if (g_tombstoned_connected && !tombstoned_notify_completion(g_tombstoned_socket.get())) {
     LOG(ERROR) << "failed to notify tombstoned of completion";
   }
 
diff --git a/debuggerd/debuggerd_benchmark.cpp b/debuggerd/debuggerd_benchmark.cpp
new file mode 100644
index 0000000..37ee214
--- /dev/null
+++ b/debuggerd/debuggerd_benchmark.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2017, 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 <err.h>
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <thread>
+
+#include <benchmark/benchmark.h>
+#include <debuggerd/client.h>
+
+using namespace std::chrono_literals;
+
+static_assert(std::chrono::high_resolution_clock::is_steady);
+
+enum class ThreadState { Starting, Started, Stopping };
+
+static void SetScheduler() {
+  struct sched_param param {
+    .sched_priority = 1,
+  };
+
+  if (sched_setscheduler(getpid(), SCHED_FIFO, &param) != 0) {
+    fprintf(stderr, "failed to set scheduler to SCHED_FIFO: %s", strerror(errno));
+  }
+}
+
+static std::chrono::duration<double> GetMaximumPause(std::atomic<ThreadState>& state) {
+  std::chrono::duration<double> max_diff(0);
+
+  const auto begin = std::chrono::high_resolution_clock::now();
+  auto last = begin;
+  state.store(ThreadState::Started);
+  while (state.load() != ThreadState::Stopping) {
+    auto now = std::chrono::high_resolution_clock::now();
+
+    auto diff = now - last;
+    if (diff > max_diff) {
+      max_diff = diff;
+    }
+
+    last = now;
+  }
+
+  return max_diff;
+}
+
+static void PerformDump() {
+  pid_t target = getpid();
+  pid_t forkpid = fork();
+  if (forkpid == -1) {
+    err(1, "fork failed");
+  } else if (forkpid != 0) {
+    int status;
+    pid_t pid = waitpid(forkpid, &status, 0);
+    if (pid == -1) {
+      err(1, "waitpid failed");
+    } else if (!WIFEXITED(status)) {
+      err(1, "child didn't exit");
+    } else if (WEXITSTATUS(status) != 0) {
+      errx(1, "child exited with non-zero status %d", WEXITSTATUS(status));
+    }
+  } else {
+    android::base::unique_fd output_fd(open("/dev/null", O_WRONLY | O_CLOEXEC));
+    if (output_fd == -1) {
+      err(1, "failed to open /dev/null");
+    }
+
+    if (!debuggerd_trigger_dump(target, kDebuggerdNativeBacktrace, 1000, std::move(output_fd))) {
+      errx(1, "failed to trigger dump");
+    }
+
+    _exit(0);
+  }
+}
+
+template <typename Fn>
+static void BM_maximum_pause_impl(benchmark::State& state, const Fn& function) {
+  SetScheduler();
+
+  for (auto _ : state) {
+    std::chrono::duration<double> max_pause;
+    std::atomic<ThreadState> thread_state(ThreadState::Starting);
+    auto thread = std::thread([&]() { max_pause = GetMaximumPause(thread_state); });
+
+    while (thread_state != ThreadState::Started) {
+      std::this_thread::sleep_for(1ms);
+    }
+
+    function();
+
+    thread_state = ThreadState::Stopping;
+    thread.join();
+
+    state.SetIterationTime(max_pause.count());
+  }
+}
+
+static void BM_maximum_pause_noop(benchmark::State& state) {
+  BM_maximum_pause_impl(state, []() {});
+}
+
+static void BM_maximum_pause_debuggerd(benchmark::State& state) {
+  BM_maximum_pause_impl(state, []() { PerformDump(); });
+}
+
+BENCHMARK(BM_maximum_pause_noop)->Iterations(128)->UseManualTime();
+BENCHMARK(BM_maximum_pause_debuggerd)->Iterations(128)->UseManualTime();
+
+BENCHMARK_MAIN();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 8d0c98b..939f4d2 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
@@ -75,22 +76,6 @@
     return value;                                                  \
   }()
 
-#define ASSERT_MATCH(str, pattern)                                              \
-  do {                                                                          \
-    std::regex r((pattern));                                                    \
-    if (!std::regex_search((str), r)) {                                         \
-      FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
-    }                                                                           \
-  } while (0)
-
-#define ASSERT_NOT_MATCH(str, pattern)                                                      \
-  do {                                                                                      \
-    std::regex r((pattern));                                                                \
-    if (std::regex_search((str), r)) {                                                      \
-      FAIL() << "regex mismatch: expected to not find " << (pattern) << " in: \n" << (str); \
-    }                                                                                       \
-  } while (0)
-
 #define ASSERT_BACKTRACE_FRAME(result, frame_name)                        \
   ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \
                        R"(/libc.so \()" frame_name R"(\+)")
@@ -245,6 +230,8 @@
   int status;
   pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
   if (pid != crasher_pid) {
+    printf("failed to wait for crasher (pid %d)\n", crasher_pid);
+    sleep(100);
     FAIL() << "failed to wait for crasher: " << strerror(errno);
   }
 
@@ -341,13 +328,12 @@
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
-    abort();
+    while (true) {
+      sleep(1);
+    }
   });
   StartIntercept(&output_fd);
-
-  // Wait for a bit, or we might end up killing the process before the signal
-  // handler even gets a chance to be registered.
-  std::this_thread::sleep_for(100ms);
+  FinishCrasher();
   ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
 
   AssertDeath(SIGSEGV);
@@ -439,19 +425,6 @@
   AssertDeath(SIGABRT);
 }
 
-// wait_for_gdb shouldn't trigger on manually sent signals.
-TEST_F(CrasherTest, wait_for_gdb_signal) {
-  if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
-    FAIL() << "failed to enable wait_for_gdb";
-  }
-
-  StartProcess([]() {
-    abort();
-  });
-  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
-  AssertDeath(SIGSEGV);
-}
-
 TEST_F(CrasherTest, backtrace) {
   std::string result;
   int intercept_result;
@@ -596,15 +569,13 @@
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
-    while (true) {
-    }
+    raise(SIGABRT);
   });
 
   StartIntercept(&output_fd);
-  FinishCrasher();
 
   ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));
-  ASSERT_EQ(0, kill(crasher_pid, SIGABRT));
+  FinishCrasher();
 
   int status;
   ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
@@ -622,6 +593,10 @@
   regex += R"( \(.+debuggerd_test)";
   ASSERT_MATCH(result, regex.c_str());
 
+  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_TRUE(WIFSTOPPED(status));
+  ASSERT_EQ(SIGABRT, WSTOPSIG(status));
+
   ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));
   AssertDeath(SIGABRT);
 }
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 06d4a9b..5fddddc 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -36,10 +36,14 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <memory>
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
 #include "debuggerd/handler.h"
 #include "tombstoned/tombstoned.h"
@@ -49,6 +53,7 @@
 #include "libdebuggerd/tombstone.h"
 
 using android::base::unique_fd;
+using unwindstack::Regs;
 
 extern "C" void __linker_enable_fallback_allocator();
 extern "C" void __linker_disable_fallback_allocator();
@@ -61,7 +66,19 @@
 // exhaustion.
 static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
   __linker_enable_fallback_allocator();
-  dump_backtrace_ucontext(output_fd, ucontext);
+  {
+    std::unique_ptr<Regs> regs;
+
+    ThreadInfo thread;
+    thread.pid = getpid();
+    thread.tid = gettid();
+    thread.thread_name = get_thread_name(gettid());
+    thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+
+    // TODO: Create this once and store it in a global?
+    std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+    dump_backtrace_thread(output_fd, map.get(), thread);
+  }
   __linker_disable_fallback_allocator();
 }
 
@@ -162,41 +179,41 @@
 
   // Send a signal to all of our siblings, asking them to dump their stack.
   iterate_siblings(
-    [](pid_t tid, int output_fd) {
-      // Use a pipe, to be able to detect situations where the thread gracefully exits before
-      // receiving our signal.
-      unique_fd pipe_read, pipe_write;
-      if (!Pipe(&pipe_read, &pipe_write)) {
-        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
-                              strerror(errno));
-        return false;
-      }
+      [](pid_t tid, int output_fd) {
+        // Use a pipe, to be able to detect situations where the thread gracefully exits before
+        // receiving our signal.
+        unique_fd pipe_read, pipe_write;
+        if (!Pipe(&pipe_read, &pipe_write)) {
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
+                                strerror(errno));
+          return false;
+        }
 
-      trace_output_fd.store(pipe_write.get());
+        trace_output_fd.store(pipe_write.get());
 
-      siginfo_t siginfo = {};
-      siginfo.si_code = SI_QUEUE;
-      siginfo.si_value.sival_int = ~0;
-      siginfo.si_pid = getpid();
-      siginfo.si_uid = getuid();
+        siginfo_t siginfo = {};
+        siginfo.si_code = SI_QUEUE;
+        siginfo.si_value.sival_int = ~0;
+        siginfo.si_pid = getpid();
+        siginfo.si_uid = getuid();
 
-      if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
-        async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
-                              tid, strerror(errno));
-        return false;
-      }
+        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
+          async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
+                                tid, strerror(errno));
+          return false;
+        }
 
-      bool success = forward_output(pipe_read.get(), output_fd);
-      if (success) {
-        // The signaled thread has closed trace_output_fd already.
-        (void)pipe_write.release();
-      } else {
-        trace_output_fd.store(-1);
-      }
+        bool success = forward_output(pipe_read.get(), output_fd);
+        if (success) {
+          // The signaled thread has closed trace_output_fd already.
+          (void)pipe_write.release();
+        } else {
+          trace_output_fd.store(-1);
+        }
 
-      return true;
-    },
-    output_fd.get());
+        return true;
+      },
+      output_fd.get());
 
   dump_backtrace_footer(output_fd.get());
   tombstoned_notify_completion(tombstone_socket.get());
@@ -206,7 +223,8 @@
 }
 
 static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
-  // Only allow one thread to handle a crash.
+  // Only allow one thread to handle a crash at a time (this can happen multiple times without
+  // exit, since tombstones can be requested without a real crash happening.)
   static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
   int ret = pthread_mutex_lock(&crash_mutex);
   if (ret != 0) {
@@ -221,11 +239,13 @@
   if (tombstoned_connected) {
     tombstoned_notify_completion(tombstone_socket.get());
   }
+
+  pthread_mutex_unlock(&crash_mutex);
 }
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL) {
+  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
     return trace_handler(info, ucontext);
   } else {
     return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index bd202ff..02bc4b8 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -44,15 +44,21 @@
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
+#include <sys/uio.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <cutils/properties.h>
+
+#include <libdebuggerd/utility.h>
 
 #include "dump_type.h"
+#include "protocol.h"
 
+using android::base::Pipe;
 using android::base::unique_fd;
 
 // see man(2) prctl, specifically the section about PR_GET_NAME
@@ -114,7 +120,7 @@
   va_list args;
   va_start(args, fmt);
 
-  char buf[4096];
+  char buf[256];
   async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);
   fatal("%s: %s", buf, strerror(err));
 }
@@ -147,7 +153,7 @@
  * mutex is being held, so we don't want to use any libc functions that
  * could allocate memory or hold a lock.
  */
-static void log_signal_summary(int signum, const siginfo_t* info) {
+static void log_signal_summary(const siginfo_t* info) {
   char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
   if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
     strcpy(thread_name, "<name unknown>");
@@ -157,57 +163,19 @@
     thread_name[MAX_TASK_NAME_LEN] = 0;
   }
 
-  if (signum == DEBUGGER_SIGNAL) {
+  if (info->si_signo == DEBUGGER_SIGNAL) {
     async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
                           thread_name);
     return;
   }
 
-  const char* signal_name = "???";
-  bool has_address = false;
-  switch (signum) {
-    case SIGABRT:
-      signal_name = "SIGABRT";
-      break;
-    case SIGBUS:
-      signal_name = "SIGBUS";
-      has_address = true;
-      break;
-    case SIGFPE:
-      signal_name = "SIGFPE";
-      has_address = true;
-      break;
-    case SIGILL:
-      signal_name = "SIGILL";
-      has_address = true;
-      break;
-    case SIGSEGV:
-      signal_name = "SIGSEGV";
-      has_address = true;
-      break;
-#if defined(SIGSTKFLT)
-    case SIGSTKFLT:
-      signal_name = "SIGSTKFLT";
-      break;
-#endif
-    case SIGSYS:
-      signal_name = "SIGSYS";
-      break;
-    case SIGTRAP:
-      signal_name = "SIGTRAP";
-      break;
-  }
+  const char* signal_name = get_signame(info->si_signo);
+  bool has_address = signal_has_si_addr(info->si_signo, info->si_code);
 
-  // "info" will be null if the siginfo_t information was not available.
-  // Many signals don't have an address or a code.
-  char code_desc[32];  // ", code -6"
-  char addr_desc[32];  // ", fault addr 0x1234"
-  addr_desc[0] = code_desc[0] = 0;
-  if (info != nullptr) {
-    async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code);
-    if (has_address) {
-      async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
-    }
+  // Many signals don't have an address.
+  char addr_desc[32] = "";  // ", fault addr 0x1234"
+  if (has_address) {
+    async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
   }
 
   char main_thread_name[MAX_TASK_NAME_LEN + 1];
@@ -216,8 +184,9 @@
   }
 
   async_safe_format_log(
-      ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum,
-      signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
+      ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s), code %d (%s)%s in tid %d (%s), pid %d (%s)",
+      info->si_signo, signal_name, info->si_code, get_sigcode(info->si_signo, info->si_code),
+      addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
 }
 
 /*
@@ -268,12 +237,44 @@
   }
 }
 
+static pid_t __fork() {
+  return clone(nullptr, nullptr, 0, nullptr);
+}
+
+// Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation.
+// Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure.
+static void create_vm_process() {
+  pid_t first = clone(nullptr, nullptr, CLONE_FILES, nullptr);
+  if (first == -1) {
+    fatal_errno("failed to clone vm process");
+  } else if (first == 0) {
+    drop_capabilities();
+
+    if (clone(nullptr, nullptr, CLONE_FILES, nullptr) == -1) {
+      _exit(errno);
+    }
+
+    // Exit immediately on both sides of the fork.
+    // crash_dump is ptracing us, so it'll get to do whatever it wants in between.
+    _exit(0);
+  }
+
+  int status;
+  if (TEMP_FAILURE_RETRY(waitpid(first, &status, __WCLONE)) != first) {
+    fatal_errno("failed to waitpid in double fork");
+  } else if (!WIFEXITED(status)) {
+    fatal("intermediate process didn't exit cleanly in double fork (status = %d)", status);
+  } else if (WEXITSTATUS(status)) {
+    fatal("second clone failed: %s", strerror(WEXITSTATUS(status)));
+  }
+}
+
 struct debugger_thread_info {
-  bool crash_dump_started;
   pid_t crashing_tid;
   pid_t pseudothread_tid;
-  int signal_number;
-  siginfo_t* info;
+  siginfo_t* siginfo;
+  void* ucontext;
+  uintptr_t abort_msg;
 };
 
 // Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -284,7 +285,8 @@
 static void* pseudothread_stack;
 
 static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {
-  if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) {
+  if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL &&
+      thread_info->siginfo->si_value.sival_int) {
     return kDebuggerdNativeBacktrace;
   }
 
@@ -299,25 +301,58 @@
   }
 
   int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR));
+  if (devnull == -1) {
+    fatal_errno("failed to open /dev/null");
+  } else if (devnull != 0) {
+    fatal_errno("expected /dev/null fd to be 0, actually %d", devnull);
+  }
 
   // devnull will be 0.
-  TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO));
-  TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO));
+  TEMP_FAILURE_RETRY(dup2(devnull, 1));
+  TEMP_FAILURE_RETRY(dup2(devnull, 2));
 
-  unique_fd pipe_read, pipe_write;
-  if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+  unique_fd input_read, input_write;
+  unique_fd output_read, output_write;
+  if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) {
     fatal_errno("failed to create pipe");
   }
 
+  // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
+  uint32_t version = 1;
+  constexpr size_t expected =
+      sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t);
+
+  errno = 0;
+  if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
+    fatal_errno("failed to set pipe bufer size");
+  }
+
+  struct iovec iovs[4] = {
+      {.iov_base = &version, .iov_len = sizeof(version)},
+      {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
+      {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
+      {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
+  };
+
+  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4));
+  if (rc == -1) {
+    fatal_errno("failed to write crash info");
+  } else if (rc != expected) {
+    fatal("failed to write crash info, wrote %zd bytes, expected %zd", rc, expected);
+  }
+
   // Don't use fork(2) to avoid calling pthread_atfork handlers.
-  int forkpid = clone(nullptr, nullptr, 0, nullptr);
-  if (forkpid == -1) {
+  pid_t crash_dump_pid = __fork();
+  if (crash_dump_pid == -1) {
     async_safe_format_log(ANDROID_LOG_FATAL, "libc",
                           "failed to fork in debuggerd signal handler: %s", strerror(errno));
-  } else if (forkpid == 0) {
-    TEMP_FAILURE_RETRY(dup2(pipe_write.get(), STDOUT_FILENO));
-    pipe_write.reset();
-    pipe_read.reset();
+  } else if (crash_dump_pid == 0) {
+    TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO));
+    TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO));
+    input_read.reset();
+    input_write.reset();
+    output_read.reset();
+    output_write.reset();
 
     raise_caps();
 
@@ -332,45 +367,49 @@
 
     execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
            nullptr, nullptr);
-
     fatal_errno("exec failed");
-  } else {
-    pipe_write.reset();
-    char buf[4];
-    ssize_t rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), &buf, sizeof(buf)));
-    if (rc == -1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
-                            strerror(errno));
-    } else if (rc == 0) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
-    } else if (rc != 1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc",
-                            "read of IPC pipe returned unexpected value: %zd", rc);
-    } else {
-      if (buf[0] != '\1') {
-        async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
-      } else {
-        thread_info->crash_dump_started = true;
-      }
-    }
-    pipe_read.reset();
-
-    // Don't leave a zombie child.
-    int status;
-    if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
-                            strerror(errno));
-    } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
-      thread_info->crash_dump_started = false;
-    }
   }
 
-  syscall(__NR_exit, 0);
+  input_write.reset();
+  output_read.reset();
+
+  // crash_dump will ptrace and pause all of our threads, and then write to the pipe to tell
+  // us to fork off a process to read memory from.
+  char buf[4];
+  rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));
+  if (rc == -1) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno));
+    return 1;
+  } else if (rc == 0) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec");
+    return 1;
+  } else if (rc != 1) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc",
+                          "read of IPC pipe returned unexpected value: %zd", rc);
+    return 1;
+  } else if (buf[0] != '\1') {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+    return 1;
+  }
+
+  // crash_dump is ptracing us, fork off a copy of our address space for it to use.
+  create_vm_process();
+
+  input_read.reset();
+  input_write.reset();
+
+  // Don't leave a zombie child.
+  int status;
+  if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
+                          strerror(errno));
+  } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+  }
   return 0;
 }
 
-static void resend_signal(siginfo_t* info, bool crash_dump_started) {
+static void resend_signal(siginfo_t* info) {
   // Signals can either be fatal or nonfatal.
   // For fatal signals, crash_dump will send us the signal we crashed with
   // before resuming us, so that processes using waitpid on us will see that we
@@ -379,16 +418,6 @@
   // to deregister our signal handler for that signal before continuing.
   if (info->si_signo != DEBUGGER_SIGNAL) {
     signal(info->si_signo, SIG_DFL);
-  }
-
-  // We need to return from our signal handler so that crash_dump can see the
-  // signal via ptrace and dump the thread that crashed. However, returning
-  // does not guarantee that the signal will be thrown again, even for SIGSEGV
-  // and friends, since the signal could have been sent manually. We blocked
-  // all signals when registering the handler, so resending the signal (using
-  // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered
-  // when our signal handler returns.
-  if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) {
     int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);
     if (rc != 0) {
       fatal_errno("failed to resend signal during crash");
@@ -425,7 +454,7 @@
   }
 
   void* abort_message = nullptr;
-  if (g_callbacks.get_abort_message) {
+  if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) {
     abort_message = g_callbacks.get_abort_message();
   }
 
@@ -439,7 +468,7 @@
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
     debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message);
-    resend_signal(info, false);
+    resend_signal(info);
     return;
   }
 
@@ -450,20 +479,14 @@
     return;
   }
 
-  log_signal_summary(signal_number, info);
-
-  // If this was a fatal crash, populate si_value with the abort message address if possible.
-  // Note that applications can set an abort message without aborting.
-  if (abort_message && signal_number != DEBUGGER_SIGNAL) {
-    info->si_value.sival_ptr = abort_message;
-  }
+  log_signal_summary(info);
 
   debugger_thread_info thread_info = {
-    .crash_dump_started = false,
-    .pseudothread_tid = -1,
-    .crashing_tid = __gettid(),
-    .signal_number = signal_number,
-    .info = info
+      .pseudothread_tid = -1,
+      .crashing_tid = __gettid(),
+      .siginfo = info,
+      .ucontext = context,
+      .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
   };
 
   // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
@@ -472,7 +495,8 @@
     fatal_errno("failed to set dumpable");
   }
 
-  // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread).
+  // Essentially pthread_create without CLONE_FILES, so we still work during file descriptor
+  // exhaustion.
   pid_t child_pid =
     clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
           CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
@@ -484,7 +508,7 @@
   // Wait for the child to start...
   futex_wait(&thread_info.pseudothread_tid, -1);
 
-  // and then wait for it to finish.
+  // and then wait for it to terminate.
   futex_wait(&thread_info.pseudothread_tid, child_pid);
 
   // Restore PR_SET_DUMPABLE to its original value.
@@ -492,21 +516,13 @@
     fatal_errno("failed to restore dumpable");
   }
 
-  // Signals can either be fatal or nonfatal.
-  // For fatal signals, crash_dump will PTRACE_CONT us with the signal we
-  // crashed with, so that processes using waitpid on us will see that we
-  // exited with the correct exit status (e.g. so that sh will report
-  // "Segmentation fault" instead of "Killed"). For this to work, we need
-  // to deregister our signal handler for that signal before continuing.
-  if (signal_number != DEBUGGER_SIGNAL) {
-    signal(signal_number, SIG_DFL);
-  }
-
-  resend_signal(info, thread_info.crash_dump_started);
   if (info->si_signo == DEBUGGER_SIGNAL) {
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
+  } else {
+    // Resend the signal, so that either gdb or the parent's waitpid sees it.
+    resend_signal(info);
   }
 }
 
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
deleted file mode 100644
index bfb5ea4..0000000
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *
- * Copyright 2006, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  pt_regs regs;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &regs)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
-
-  for (int reg = 0; reg < 14; reg++) {
-    dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", &reg_names[reg * 2]);
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:");
-
-  if (regs.ARM_pc != regs.ARM_lr) {
-    dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:");
-  }
-}
-
-#define DUMP_GP_REGISTERS(log, reg_prefix)                                             \
-  _LOG(log, logtype::REGISTERS, "    r0 %08x  r1 %08x  r2 %08x  r3 %08x\n",            \
-       static_cast<uint32_t>(reg_prefix##r0), static_cast<uint32_t>(reg_prefix##r1),   \
-       static_cast<uint32_t>(reg_prefix##r2), static_cast<uint32_t>(reg_prefix##r3));  \
-  _LOG(log, logtype::REGISTERS, "    r4 %08x  r5 %08x  r6 %08x  r7 %08x\n",            \
-       static_cast<uint32_t>(reg_prefix##r4), static_cast<uint32_t>(reg_prefix##r5),   \
-       static_cast<uint32_t>(reg_prefix##r6), static_cast<uint32_t>(reg_prefix##r7));  \
-  _LOG(log, logtype::REGISTERS, "    r8 %08x  r9 %08x  sl %08x  fp %08x\n",            \
-       static_cast<uint32_t>(reg_prefix##r8), static_cast<uint32_t>(reg_prefix##r9),   \
-       static_cast<uint32_t>(reg_prefix##r10), static_cast<uint32_t>(reg_prefix##fp)); \
-  _LOG(log, logtype::REGISTERS, "    ip %08x  sp %08x  lr %08x  pc %08x  cpsr %08x\n", \
-       static_cast<uint32_t>(reg_prefix##ip), static_cast<uint32_t>(reg_prefix##sp),   \
-       static_cast<uint32_t>(reg_prefix##lr), static_cast<uint32_t>(reg_prefix##pc),   \
-       static_cast<uint32_t>(reg_prefix##cpsr))
-
-void dump_registers(log_t* log, pid_t tid) {
-  pt_regs r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  DUMP_GP_REGISTERS(log, r.ARM_);
-
-  user_vfp vfp_regs;
-  if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-    ALOGE("cannot get FP registers: %s\n", strerror(errno));
-    return;
-  }
-
-  for (size_t i = 0; i < 32; i += 2) {
-    _LOG(log, logtype::FP_REGISTERS, "    d%-2d %016llx  d%-2d %016llx\n",
-         i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
-  }
-  _LOG(log, logtype::FP_REGISTERS, "    scr %08lx\n", vfp_regs.fpscr);
-}
-
-void dump_registers(log_t* log, const ucontext_t* uc) {
-  DUMP_GP_REGISTERS(log, uc->uc_mcontext.arm_);
-}
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
deleted file mode 100644
index ad1c951..0000000
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <elf.h>
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/uio.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  struct user_pt_regs regs;
-  struct iovec io;
-  io.iov_base = &regs;
-  io.iov_len = sizeof(regs);
-
-  if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
-    ALOGE("ptrace failed to get registers: %s", strerror(errno));
-    return;
-  }
-
-  for (int reg = 0; reg < 31; reg++) {
-    dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
-
-  if (regs.pc != regs.sp) {
-    dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:");
-  }
-}
-
-#define DUMP_GP_REGISTERS(log)                                                                   \
-  for (int i = 0; i < 28; i += 4) {                                                              \
-    const char* fmt = "    x%-2d  %016llx  x%-2d  %016llx  x%-2d  %016llx  x%-2d  %016llx\n";    \
-    _LOG(log, logtype::REGISTERS, fmt, i, r.regs[i], i + 1, r.regs[i + 1], i + 2, r.regs[i + 2], \
-         i + 3, r.regs[i + 3]);                                                                  \
-  }                                                                                              \
-  _LOG(log, logtype::REGISTERS, "    x28  %016llx  x29  %016llx  x30  %016llx\n", r.regs[28],    \
-       r.regs[29], r.regs[30]);                                                                  \
-  _LOG(log, logtype::REGISTERS, "    sp   %016llx  pc   %016llx  pstate %016llx\n", r.sp, r.pc,  \
-       r.pstate)
-
-void dump_registers(log_t* log, pid_t tid) {
-  struct user_pt_regs r;
-  struct iovec io;
-  io.iov_base = &r;
-  io.iov_len = sizeof(r);
-
-  if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
-    ALOGE("ptrace error: %s\n", strerror(errno));
-    return;
-  }
-
-  DUMP_GP_REGISTERS(log);
-
-  struct user_fpsimd_state f;
-  io.iov_base = &f;
-  io.iov_len = sizeof(f);
-
-  if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
-    ALOGE("ptrace error: %s\n", strerror(errno));
-    return;
-  }
-
-  for (int i = 0; i < 32; i += 2) {
-    _LOG(log, logtype::FP_REGISTERS,
-         "    v%-2d  %016" PRIx64 "%016" PRIx64 "  v%-2d  %016" PRIx64 "%016" PRIx64 "\n",
-         i,
-         static_cast<uint64_t>(f.vregs[i] >> 64),
-         static_cast<uint64_t>(f.vregs[i]),
-         i+1,
-         static_cast<uint64_t>(f.vregs[i+1] >> 64),
-         static_cast<uint64_t>(f.vregs[i+1]));
-  }
-  _LOG(log, logtype::FP_REGISTERS, "    fpsr %08x  fpcr %08x\n", f.fpsr, f.fpcr);
-}
-
-void dump_registers(log_t* log, const ucontext_t* ucontext) {
-  const mcontext_t& r = ucontext->uc_mcontext;
-  DUMP_GP_REGISTERS(log);
-}
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f616e1b..f0a01f4 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -30,12 +30,15 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <map>
 #include <memory>
 #include <string>
 
+#include <android-base/unique_fd.h>
 #include <backtrace/Backtrace.h>
 #include <log/log.h>
 
+#include "libdebuggerd/types.h"
 #include "libdebuggerd/utility.h"
 
 static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
@@ -56,62 +59,46 @@
   _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
 }
 
-static void log_thread_name(log_t* log, pid_t tid, const char* thread_name) {
-  _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread_name, tid);
-}
-
-static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid,
-                        const std::string& thread_name) {
-  log_thread_name(log, tid, thread_name.c_str());
-
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
-  if (backtrace->Unwind(0)) {
-    dump_backtrace_to_log(backtrace.get(), log, "  ");
-  } else {
-    ALOGE("Unwind failed: tid = %d: %s", tid,
-          backtrace->GetErrorString(backtrace->GetError()).c_str());
-  }
-}
-
-void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name,
-                    const std::map<pid_t, std::string>& threads, std::string* amfd_data) {
-  log_t log;
-  log.tfd = fd;
-  log.amfd_data = amfd_data;
-
-  dump_process_header(&log, pid, process_name.c_str());
-  dump_thread(&log, map, pid, tid, threads.find(tid)->second.c_str());
-
-  for (const auto& it : threads) {
-    pid_t thread_tid = it.first;
-    const std::string& thread_name = it.second;
-    if (thread_tid != tid) {
-      dump_thread(&log, map, pid, thread_tid, thread_name.c_str());
-    }
-  }
-
-  dump_process_footer(&log, pid);
-}
-
-void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext) {
-  pid_t pid = getpid();
-  pid_t tid = gettid();
-
+void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) {
   log_t log;
   log.tfd = output_fd;
   log.amfd_data = nullptr;
 
-  char thread_name[16];
-  read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
-  log_thread_name(&log, tid, thread_name);
+  _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
 
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
-  if (backtrace->Unwind(0, ucontext)) {
-    dump_backtrace_to_log(backtrace.get(), &log, "  ");
-  } else {
-    ALOGE("Unwind failed: tid = %d: %s", tid,
-          backtrace->GetErrorString(backtrace->GetError()).c_str());
+  std::vector<backtrace_frame_data_t> frames;
+  if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) {
+    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
+    return;
   }
+
+  for (auto& frame : frames) {
+    _LOG(&log, logtype::BACKTRACE, "  %s\n", Backtrace::FormatFrameData(&frame).c_str());
+  }
+}
+
+void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+                    const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
+  log_t log;
+  log.tfd = output_fd.get();
+  log.amfd_data = nullptr;
+
+  auto target = thread_info.find(target_thread);
+  if (target == thread_info.end()) {
+    ALOGE("failed to find target thread in thread info");
+    return;
+  }
+
+  dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
+
+  dump_backtrace_thread(output_fd.get(), map, target->second);
+  for (const auto& [tid, info] : thread_info) {
+    if (tid != target_thread) {
+      dump_backtrace_thread(output_fd.get(), map, info);
+    }
+  }
+
+  dump_process_footer(&log, target->second.pid);
 }
 
 void dump_backtrace_header(int output_fd) {
@@ -131,9 +118,3 @@
 
   dump_process_footer(&log, getpid());
 }
-
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) {
-  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str());
-  }
-}
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
index a35102f..d7afc0b 100644
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -26,28 +26,28 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
-#include <backtrace/Backtrace.h>
 #include <log/log.h>
+#include <unwindstack/Memory.h>
 
 #define NOTE_ALIGN(size)  (((size) + 3) & ~3)
 
 template <typename HdrType, typename PhdrType, typename NhdrType>
-static bool get_build_id(
-    Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) {
+static bool get_build_id(unwindstack::Memory* memory, uintptr_t base_addr, uint8_t* e_ident,
+                         std::string* build_id) {
   HdrType hdr;
 
   memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
 
   // First read the rest of the header.
-  if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
-                      sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
+  if (memory->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
+                   sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
     return false;
   }
 
   for (size_t i = 0; i < hdr.e_phnum; i++) {
     PhdrType phdr;
-    if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
-                        reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
+    if (memory->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
+                     reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
       return false;
     }
     // Looking for the .note.gnu.build-id note.
@@ -56,7 +56,7 @@
       uintptr_t addr = base_addr + phdr.p_offset;
       while (hdr_size >= sizeof(NhdrType)) {
         NhdrType nhdr;
-        if (backtrace->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
+        if (memory->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
           return false;
         }
         addr += sizeof(nhdr);
@@ -69,7 +69,7 @@
                   nhdr.n_descsz);
             return false;
           }
-          if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
+          if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
             return false;
           }
 
@@ -95,10 +95,10 @@
   return false;
 }
 
-bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) {
+bool elf_get_build_id(unwindstack::Memory* memory, uintptr_t addr, std::string* build_id) {
   // Read and verify the elf magic number first.
   uint8_t e_ident[EI_NIDENT];
-  if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) {
+  if (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) {
     return false;
   }
 
@@ -107,14 +107,14 @@
   }
 
   // Read the rest of EI_NIDENT.
-  if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
+  if (memory->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
     return false;
   }
 
   if (e_ident[EI_CLASS] == ELFCLASS32) {
-    return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(backtrace, addr, e_ident, build_id);
+    return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(memory, addr, e_ident, build_id);
   } else if (e_ident[EI_CLASS] == ELFCLASS64) {
-    return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(backtrace, addr, e_ident, build_id);
+    return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(memory, addr, e_ident, build_id);
   }
 
   return false;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index fe738f1..119e59b 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -23,21 +23,20 @@
 #include <map>
 #include <string>
 
+#include <android-base/unique_fd.h>
+
+#include "types.h"
 #include "utility.h"
 
-class Backtrace;
 class BacktraceMap;
 
 // Dumps a backtrace using a format similar to what Dalvik uses so that the result
 // can be intermixed in a bug report.
-void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name,
-                    const std::map<pid_t, std::string>& threads, std::string* amfd_data);
+void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+                    const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
 
-/* Dumps the backtrace in the backtrace data structure to the log. */
-void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix);
-
-void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext);
 void dump_backtrace_header(int output_fd);
+void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread);
 void dump_backtrace_footer(int output_fd);
 
 #endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
index 11d0a43..5d0d924 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
@@ -20,8 +20,10 @@
 #include <stdint.h>
 #include <string>
 
-class Backtrace;
+namespace unwindstack {
+class Memory;
+}
 
-bool elf_get_build_id(Backtrace*, uintptr_t, std::string*);
+bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
 
 #endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/machine.h b/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
deleted file mode 100644
index 5e56682..0000000
--- a/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_MACHINE_H
-#define _DEBUGGERD_MACHINE_H
-
-#include <sys/types.h>
-
-#include <backtrace/Backtrace.h>
-
-#include "utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace);
-void dump_registers(log_t* log, pid_t tid);
-void dump_registers(log_t* log, const ucontext_t* uc);
-
-#endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
index b37228d..4727ca4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
@@ -31,6 +31,6 @@
 void populate_open_files_list(pid_t pid, OpenFilesList* list);
 
 /* Dumps the open files list to the log. */
-void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix);
+void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix);
 
 #endif // _DEBUGGERD_OPEN_FILES_LIST_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 79743b6..198c48b 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -24,7 +24,10 @@
 #include <map>
 #include <string>
 
+#include <android-base/unique_fd.h>
+
 #include "open_files_list.h"
+#include "types.h"
 
 class BacktraceMap;
 
@@ -43,11 +46,10 @@
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext);
 
-// Compatibility shim.
-__attribute__((__unused__))
-static void engrave_tombstone_ucontext(int tombstone_fd, pid_t, pid_t, uintptr_t abort_msg_address,
-                                       siginfo_t* siginfo, ucontext_t* ucontext) {
-  engrave_tombstone_ucontext(tombstone_fd, abort_msg_address, siginfo, ucontext);
-}
+void engrave_tombstone(android::base::unique_fd output_fd, BacktraceMap* map,
+                       unwindstack::Memory* process_memory,
+                       const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
+                       uintptr_t abort_msg_address, OpenFilesList* open_files,
+                       std::string* amfd_data);
 
-#endif // _DEBUGGERD_TOMBSTONE_H
+#endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
similarity index 62%
copy from debuggerd/libdebuggerd/test/ptrace_fake.h
copy to debuggerd/libdebuggerd/include/libdebuggerd/types.h
index fdbb663..70583af 100644
--- a/debuggerd/libdebuggerd/test/ptrace_fake.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -1,5 +1,7 @@
+#pragma once
+
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2012 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.
@@ -14,11 +16,19 @@
  * limitations under the License.
  */
 
-#ifndef _DEBUGGERD_TEST_PTRACE_FAKE_H
-#define _DEBUGGERD_TEST_PTRACE_FAKE_H
+#include <memory>
+#include <string>
 
-#include <signal.h>
+#include <unwindstack/Regs.h>
 
-void ptrace_set_fake_getsiginfo(const siginfo_t&);
+struct ThreadInfo {
+  std::unique_ptr<unwindstack::Regs> registers;
+  pid_t tid;
+  std::string thread_name;
 
-#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
+  pid_t pid;
+  std::string process_name;
+
+  int signo = 0;
+  siginfo_t* siginfo = nullptr;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index f481b78..c5abfe2 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -27,21 +27,24 @@
 #include <android-base/macros.h>
 #include <backtrace/Backtrace.h>
 
-struct log_t{
-    // Tombstone file descriptor.
-    int tfd;
-    // Data to be sent to the Activity Manager.
-    std::string* amfd_data;
-    // The tid of the thread that crashed.
-    pid_t crashed_tid;
-    // The tid of the thread we are currently working with.
-    pid_t current_tid;
-    // logd daemon crash, can block asking for logcat data, allow suppression.
-    bool should_retrieve_logcat;
+struct log_t {
+  // Tombstone file descriptor.
+  int tfd;
+  // Data to be sent to the Activity Manager.
+  std::string* amfd_data;
+  // The tid of the thread that crashed.
+  pid_t crashed_tid;
+  // The tid of the thread we are currently working with.
+  pid_t current_tid;
+  // logd daemon crash, can block asking for logcat data, allow suppression.
+  bool should_retrieve_logcat;
 
-    log_t()
-        : tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1),
-          should_retrieve_logcat(true) {}
+  log_t()
+      : tfd(-1),
+        amfd_data(nullptr),
+        crashed_tid(-1),
+        current_tid(-1),
+        should_retrieve_logcat(true) {}
 };
 
 // List of types of logs to simplify the logging decision in _LOG
@@ -59,13 +62,20 @@
 };
 
 // Log information onto the tombstone.
-void _LOG(log_t* log, logtype ltype, const char *fmt, ...)
-        __attribute__ ((format(printf, 3, 4)));
+void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
 
-bool wait_for_signal(pid_t tid, siginfo_t* siginfo);
+namespace unwindstack {
+class Memory;
+}
 
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
+void dump_memory(log_t* log, unwindstack::Memory* backtrace, uintptr_t addr, const char* fmt, ...);
 
 void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
 
+void drop_capabilities();
+
+bool signal_has_si_addr(int si_signo, int si_code);
+const char* get_signame(int sig);
+const char* get_sigcode(int signo, int code);
+
 #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
deleted file mode 100644
index 1fc690b..0000000
--- a/debuggerd/libdebuggerd/mips/machine.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2012, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-#define R(x) (static_cast<uintptr_t>(x))
-
-// If configured to do so, dump memory around *all* registers
-// for the crashing thread.
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  pt_regs r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
-
-  for (int reg = 0; reg < 32; reg++) {
-    // skip uninteresting registers
-    if (reg == 0 // $0
-        || reg == 26 // $k0
-        || reg == 27 // $k1
-        || reg == 31 // $ra (done below)
-       )
-      continue;
-
-    dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", &reg_names[reg * 2]);
-  }
-
-  uintptr_t pc = R(r.cp0_epc);
-  uintptr_t ra = R(r.regs[31]);
-  dump_memory(log, backtrace, pc, "code around pc:");
-  if (pc != ra) {
-    dump_memory(log, backtrace, ra, "code around ra:");
-  }
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  pt_regs r;
-  if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR "  at %08" PRIxPTR
-       "  v0 %08" PRIxPTR "  v1 %08" PRIxPTR "\n",
-       R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
-  _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR "  a1 %08" PRIxPTR
-       "  a2 %08" PRIxPTR "  a3 %08" PRIxPTR "\n",
-       R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
-  _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR "  t1 %08" PRIxPTR
-       "  t2 %08" PRIxPTR "  t3 %08" PRIxPTR "\n",
-       R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
-  _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR "  t5 %08" PRIxPTR
-       "  t6 %08" PRIxPTR "  t7 %08" PRIxPTR "\n",
-       R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
-  _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR "  s1 %08" PRIxPTR
-       "  s2 %08" PRIxPTR "  s3 %08" PRIxPTR "\n",
-       R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
-  _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR "  s5 %08" PRIxPTR
-       "  s6 %08" PRIxPTR "  s7 %08" PRIxPTR "\n",
-       R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
-  _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR "  t9 %08" PRIxPTR
-       "  k0 %08" PRIxPTR "  k1 %08" PRIxPTR "\n",
-       R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
-  _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR "  sp %08" PRIxPTR
-       "  s8 %08" PRIxPTR "  ra %08" PRIxPTR "\n",
-       R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
-  _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR "  lo %08" PRIxPTR
-       " bva %08" PRIxPTR " epc %08" PRIxPTR "\n",
-       R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
-}
diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
deleted file mode 100644
index 955e507..0000000
--- a/debuggerd/libdebuggerd/mips64/machine.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-#define R(x) (static_cast<uintptr_t>(x))
-
-// If configured to do so, dump memory around *all* registers
-// for the crashing thread.
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  pt_regs r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
-
-  for (int reg = 0; reg < 32; reg++) {
-    // skip uninteresting registers
-    if (reg == 0 // $0
-        || reg == 26 // $k0
-        || reg == 27 // $k1
-        || reg == 31 // $ra (done below)
-       )
-      continue;
-
-    dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", &reg_names[reg * 2]);
-  }
-
-  uintptr_t pc = R(r.cp0_epc);
-  uintptr_t ra = R(r.regs[31]);
-  dump_memory(log, backtrace, pc, "code around pc:");
-  if (pc != ra) {
-    dump_memory(log, backtrace, ra, "code around ra:");
-  }
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  pt_regs r;
-  if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR "  at %016" PRIxPTR
-       "  v0 %016" PRIxPTR "  v1 %016" PRIxPTR "\n",
-       R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
-  _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR "  a1 %016" PRIxPTR
-       "  a2 %016" PRIxPTR "  a3 %016" PRIxPTR "\n",
-       R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
-  _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR "  a5 %016" PRIxPTR
-       "  a6 %016" PRIxPTR "  a7 %016" PRIxPTR "\n",
-       R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
-  _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR "  t1 %016" PRIxPTR
-       "  t2 %016" PRIxPTR "  t3 %016" PRIxPTR "\n",
-       R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
-  _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR "  s1 %016" PRIxPTR
-       "  s2 %016" PRIxPTR "  s3 %016" PRIxPTR "\n",
-       R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
-  _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR "  s5 %016" PRIxPTR
-       "  s6 %016" PRIxPTR "  s7 %016" PRIxPTR "\n",
-       R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
-  _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR "  t9 %016" PRIxPTR
-       "  k0 %016" PRIxPTR "  k1 %016" PRIxPTR "\n",
-       R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
-  _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR "  sp %016" PRIxPTR
-       "  s8 %016" PRIxPTR "  ra %016" PRIxPTR "\n",
-       R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
-  _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR "  lo %016" PRIxPTR
-       " bva %016" PRIxPTR " epc %016" PRIxPTR "\n",
-       R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
-}
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index e199db8..b12703e 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -61,7 +61,7 @@
   }
 }
 
-void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix) {
+void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) {
   for (auto& file : files) {
     _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str());
   }
diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
index 6104f7e..e7dbed7 100644
--- a/debuggerd/libdebuggerd/test/BacktraceMock.h
+++ b/debuggerd/libdebuggerd/test/BacktraceMock.h
@@ -17,15 +17,6 @@
 #ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
 #define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ucontext.h>
-
-#include <string>
-#include <vector>
-
-#include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
 class BacktraceMapMock : public BacktraceMap {
@@ -38,69 +29,4 @@
   }
 };
 
-
-class BacktraceMock : public Backtrace {
- public:
-  explicit BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
-    if (map_ == nullptr) {
-      abort();
-    }
-  }
-  virtual ~BacktraceMock() {}
-
-  virtual bool Unwind(size_t, ucontext_t*) { return false; }
-  virtual bool ReadWord(uintptr_t, word_t*) { return false;}
-
-  virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
-
-  virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
-    size_t offset = 0;
-    if (last_read_addr_ > 0) {
-      offset = addr - last_read_addr_;
-    }
-    size_t bytes_available = buffer_.size() - offset;
-
-    if (do_partial_read_) {
-      // Do a partial read.
-      if (bytes > bytes_partial_read_) {
-        bytes = bytes_partial_read_;
-      }
-      bytes_partial_read_ -= bytes;
-      // Only support a single partial read.
-      do_partial_read_ = false;
-    } else if (bytes > bytes_available) {
-      bytes = bytes_available;
-    }
-
-    if (bytes > 0) {
-      memcpy(buffer, buffer_.data() + offset, bytes);
-    }
-
-    last_read_addr_ = addr;
-    return bytes;
-  }
-
-  void SetReadData(uint8_t* buffer, size_t bytes) {
-    buffer_.resize(bytes);
-    memcpy(buffer_.data(), buffer, bytes);
-    bytes_partial_read_ = 0;
-    do_partial_read_ = false;
-    last_read_addr_ = 0;
-  }
-
-  void SetPartialReadAmount(size_t bytes) {
-    if (bytes > buffer_.size()) {
-      abort();
-    }
-    bytes_partial_read_ = bytes;
-    do_partial_read_ = true;
-  }
-
- private:
-  std::vector<uint8_t> buffer_;
-  size_t bytes_partial_read_ = 0;
-  uintptr_t last_read_addr_ = 0;
-  bool do_partial_read_ = false;
-};
-
 #endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 0fad2cf..7c8a0ea 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -19,12 +19,12 @@
 #include <memory>
 #include <string>
 
-#include <gtest/gtest.h>
 #include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <unwindstack/Memory.h>
 
 #include "libdebuggerd/utility.h"
 
-#include "BacktraceMock.h"
 #include "log_fake.h"
 
 const char g_expected_full_dump[] =
@@ -103,11 +103,59 @@
 "    123456d0 -------- -------- -------- --------  ................\n";
 #endif
 
+class MemoryMock : public unwindstack::Memory {
+ public:
+  virtual ~MemoryMock() = default;
+
+  virtual size_t Read(uint64_t addr, void* buffer, size_t bytes) override {
+    size_t offset = 0;
+    if (last_read_addr_ > 0) {
+      offset = addr - last_read_addr_;
+    }
+    size_t bytes_available = buffer_.size() - offset;
+
+    if (partial_read_) {
+      bytes = std::min(bytes, bytes_partial_read_);
+      bytes_partial_read_ -= bytes;
+      partial_read_ = bytes_partial_read_;
+    } else if (bytes > bytes_available) {
+      bytes = bytes_available;
+    }
+
+    if (bytes > 0) {
+      memcpy(buffer, buffer_.data() + offset, bytes);
+    }
+
+    last_read_addr_ = addr;
+    return bytes;
+  }
+
+  void SetReadData(uint8_t* buffer, size_t bytes) {
+    buffer_.resize(bytes);
+    memcpy(buffer_.data(), buffer, bytes);
+    bytes_partial_read_ = 0;
+    last_read_addr_ = 0;
+  }
+
+  void SetPartialReadAmount(size_t bytes) {
+    if (bytes > buffer_.size()) {
+      abort();
+    }
+    partial_read_ = true;
+    bytes_partial_read_ = bytes;
+  }
+
+ private:
+  std::vector<uint8_t> buffer_;
+  bool partial_read_ = false;
+  size_t bytes_partial_read_ = 0;
+  uintptr_t last_read_addr_ = 0;
+};
+
 class DumpMemoryTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    map_mock_.reset(new BacktraceMapMock());
-    backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+    memory_mock_ = std::make_unique<MemoryMock>();
 
     char tmp_file[256];
     const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -138,10 +186,10 @@
     if (log_.tfd >= 0) {
       close(log_.tfd);
     }
+    memory_mock_.reset();
   }
 
-  std::unique_ptr<BacktraceMapMock> map_mock_;
-  std::unique_ptr<BacktraceMock> backtrace_mock_;
+  std::unique_ptr<MemoryMock> memory_mock_;
 
   log_t log_;
 };
@@ -151,9 +199,9 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -170,10 +218,10 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(96);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(96);
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -190,9 +238,9 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -205,7 +253,7 @@
 }
 
 TEST_F(DumpMemoryTest, memory_unreadable) {
-  dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc:");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -259,9 +307,9 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -278,10 +326,10 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(102);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(102);
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -303,10 +351,10 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(45);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(45);
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+  dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -330,9 +378,9 @@
 TEST_F(DumpMemoryTest, address_low_fence) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near %.2s:", "r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -384,9 +432,9 @@
 TEST_F(DumpMemoryTest, memory_address_too_low) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
-  dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0, "memory near %.2s:", "r1");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -401,16 +449,16 @@
 TEST_F(DumpMemoryTest, memory_address_too_high) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
 #else
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
 #endif
 
   std::string tombstone_contents;
@@ -426,12 +474,12 @@
 TEST_F(DumpMemoryTest, memory_address_would_overflow) {
   uint8_t buffer[256];
   memset(buffer, 0, sizeof(buffer));
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
 #else
-  dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+  dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
 #endif
 
   std::string tombstone_contents;
@@ -449,12 +497,12 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
 
 #if defined(__LP64__)
-  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
 #else
-  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
 #endif
 
   std::string tombstone_contents;
@@ -509,12 +557,12 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 120;
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -568,12 +616,12 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 192;
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -627,11 +675,11 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   uintptr_t addr = 0x10000020;
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -685,13 +733,13 @@
   for (size_t i = 0; i < sizeof(buffer); i++) {
     buffer[i] = i;
   }
-  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
-  backtrace_mock_->SetPartialReadAmount(0);
+  memory_mock_->SetReadData(buffer, sizeof(buffer));
+  memory_mock_->SetPartialReadAmount(0);
 
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   uintptr_t addr = 0x10000020 + page_size - 256;
 
-  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+  dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4");
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
index f8cbca7..9b8281a 100644
--- a/debuggerd/libdebuggerd/test/elf_fake.cpp
+++ b/debuggerd/libdebuggerd/test/elf_fake.cpp
@@ -20,7 +20,9 @@
 
 #include <string>
 
-class Backtrace;
+namespace unwindstack {
+class Memory;
+}
 
 std::string g_build_id;
 
@@ -28,7 +30,7 @@
   g_build_id = build_id;
 }
 
-bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) {
+bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string* build_id) {
   if (g_build_id != "") {
     *build_id = g_build_id;
     return true;
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
deleted file mode 100644
index 0d4080e..0000000
--- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-
-#include "ptrace_fake.h"
-
-#include <errno.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <sys/ptrace.h>
-
-#include <string>
-
-siginfo_t g_fake_si = {.si_signo = 0};
-
-void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
-  g_fake_si = si;
-}
-
-#if !defined(__BIONIC__)
-extern "C" long ptrace_fake(enum __ptrace_request request, ...) {
-#else
-extern "C" long ptrace_fake(int request, ...) {
-#endif
-  if (request == PTRACE_GETSIGINFO) {
-    if (g_fake_si.si_signo == 0) {
-      errno = EFAULT;
-      return -1;
-    }
-
-    va_list ap;
-    va_start(ap, request);
-    va_arg(ap, int);
-    va_arg(ap, int);
-    siginfo_t* si = va_arg(ap, siginfo*);
-    va_end(ap);
-    *si = g_fake_si;
-    return 0;
-  }
-  return -1;
-}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 59a43b7..1e3a10f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -29,11 +29,6 @@
 #include "elf_fake.h"
 #include "host_signal_fixup.h"
 #include "log_fake.h"
-#include "ptrace_fake.h"
-
-// In order to test this code, we need to include the tombstone.cpp code.
-// Including it, also allows us to override the ptrace function.
-#define ptrace ptrace_fake
 
 #include "tombstone.cpp"
 
@@ -50,7 +45,6 @@
  protected:
   virtual void SetUp() {
     map_mock_.reset(new BacktraceMapMock());
-    backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
 
     char tmp_file[256];
     const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -77,11 +71,6 @@
 
     resetLogs();
     elf_set_fake_build_id("");
-    siginfo_t si;
-    memset(&si, 0, sizeof(si));
-    si.si_signo = SIGABRT;
-    si.si_code = SI_KERNEL;
-    ptrace_set_fake_getsiginfo(si);
   }
 
   virtual void TearDown() {
@@ -91,7 +80,6 @@
   }
 
   std::unique_ptr<BacktraceMapMock> map_mock_;
-  std::unique_ptr<BacktraceMock> backtrace_mock_;
 
   log_t log_;
   std::string amfd_data_;
@@ -108,7 +96,7 @@
 #endif
   map_mock_->AddMap(map);
 
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -143,7 +131,7 @@
   map_mock_->AddMap(map);
 
   elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -182,7 +170,7 @@
   map_mock_->AddMap(map);
 
   elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -240,7 +228,7 @@
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -294,13 +282,7 @@
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
-  si.si_addr = reinterpret_cast<void*>(0x1000);
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0x1000);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -352,13 +334,7 @@
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
-  si.si_addr = reinterpret_cast<void*>(0xa533000);
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa533000);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -410,13 +386,7 @@
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
-  si.si_addr = reinterpret_cast<void*>(0xa534040);
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa534040);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -466,17 +436,12 @@
   map.name = "/system/lib/fake.so";
   map_mock_->AddMap(map);
 
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  si.si_signo = SIGBUS;
-  si.si_code = SI_KERNEL;
 #if defined(__LP64__)
-  si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
+  uintptr_t addr = 0x12345a534040UL;
 #else
-  si.si_addr = reinterpret_cast<void*>(0xf534040UL);
+  uintptr_t addr = 0xf534040UL;
 #endif
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
+  dump_all_maps(&log_, map_mock_.get(), nullptr, addr);
 
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -503,124 +468,6 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
-  backtrace_map_t map;
-
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.offset = 0x1000;
-  map.load_bias = 0xd000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  ptrace_set_fake_getsiginfo(si);
-  dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
-  std::string tombstone_contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump =
-      "\nmemory map (1 entry):\n"
-#if defined(__LP64__)
-      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n";
-#else
-      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n";
-#endif
-  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
-  ASSERT_STREQ("", amfd_data_.c_str());
-
-  // Verify that the log buf is empty, and no error messages.
-  ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
-}
-
-TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
-  backtrace_map_t map;
-
-  map.start = 0xa434000;
-  map.end = 0xa435000;
-  map.flags = PROT_WRITE;
-  map_mock_->AddMap(map);
-
-  for (int i = 1; i < 255; i++) {
-    ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0);
-    ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-
-    siginfo_t si;
-    memset(&si, 0, sizeof(si));
-    si.si_signo = i;
-    si.si_code = SI_KERNEL;
-    si.si_addr = reinterpret_cast<void*>(0x1000);
-    ptrace_set_fake_getsiginfo(si);
-    dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
-
-    std::string tombstone_contents;
-    ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-    ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-    bool has_addr = false;
-    switch (si.si_signo) {
-    case SIGBUS:
-    case SIGFPE:
-    case SIGILL:
-    case SIGSEGV:
-    case SIGTRAP:
-      has_addr = true;
-      break;
-    }
-
-    const char* expected_addr_dump = \
-"\nmemory map (1 entry):\n"
-#if defined(__LP64__)
-"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
-"    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
-#else
-"--->Fault address falls at 00001000 before any mapped regions\n"
-"    0a434000-0a434fff -w-         0      1000\n";
-#endif
-    const char* expected_dump = \
-"\nmemory map (1 entry):\n"
-#if defined(__LP64__)
-"    00000000'0a434000-00000000'0a434fff -w-         0      1000\n";
-#else
-"    0a434000-0a434fff -w-         0      1000\n";
-#endif
-    if (has_addr) {
-      ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str())
-        << "Signal " << si.si_signo << " expected to include an address.";
-    } else {
-      ASSERT_STREQ(expected_dump, tombstone_contents.c_str())
-        << "Signal " << si.si_signo << " is not expected to include an address.";
-    }
-
-    ASSERT_STREQ("", amfd_data_.c_str());
-
-    // Verify that the log buf is empty, and no error messages.
-    ASSERT_STREQ("", getFakeLogBuf().c_str());
-    ASSERT_STREQ("", getFakeLogPrint().c_str());
-  }
-}
-
-TEST_F(TombstoneTest, dump_signal_info_error) {
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  ptrace_set_fake_getsiginfo(si);
-
-  dump_signal_info(&log_, 123);
-
-  std::string tombstone_contents;
-  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
-  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ("", tombstone_contents.c_str());
-
-  ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
-
-  ASSERT_STREQ("", amfd_data_.c_str());
-}
-
 TEST_F(TombstoneTest, dump_log_file_error) {
   log_.should_retrieve_logcat = true;
   dump_log_file(&log_, 123, "/fake/filename", 10);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index a0ba81b..624637a 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -35,8 +35,10 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/log.h>
 #include <backtrace/Backtrace.h>
@@ -44,169 +46,27 @@
 #include <log/log.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
 
 // Needed to get DEBUGGER_SIGNAL.
 #include "debuggerd/handler.h"
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/elf_utils.h"
-#include "libdebuggerd/machine.h"
 #include "libdebuggerd/open_files_list.h"
+#include "libdebuggerd/utility.h"
 
 using android::base::GetBoolProperty;
 using android::base::GetProperty;
 using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using unwindstack::Memory;
+using unwindstack::Regs;
 
 #define STACK_WORDS 16
 
-static bool signal_has_si_addr(int si_signo, int si_code) {
-  // Manually sent signals won't have si_addr.
-  if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
-    return false;
-  }
-
-  switch (si_signo) {
-    case SIGBUS:
-    case SIGFPE:
-    case SIGILL:
-    case SIGSEGV:
-    case SIGTRAP:
-      return true;
-    default:
-      return false;
-  }
-}
-
-static const char* get_signame(int sig) {
-  switch (sig) {
-    case SIGABRT: return "SIGABRT";
-    case SIGBUS: return "SIGBUS";
-    case SIGFPE: return "SIGFPE";
-    case SIGILL: return "SIGILL";
-    case SIGSEGV: return "SIGSEGV";
-#if defined(SIGSTKFLT)
-    case SIGSTKFLT: return "SIGSTKFLT";
-#endif
-    case SIGSTOP: return "SIGSTOP";
-    case SIGSYS: return "SIGSYS";
-    case SIGTRAP: return "SIGTRAP";
-    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
-    default: return "?";
-  }
-}
-
-static const char* get_sigcode(int signo, int code) {
-  // Try the signal-specific codes...
-  switch (signo) {
-    case SIGILL:
-      switch (code) {
-        case ILL_ILLOPC: return "ILL_ILLOPC";
-        case ILL_ILLOPN: return "ILL_ILLOPN";
-        case ILL_ILLADR: return "ILL_ILLADR";
-        case ILL_ILLTRP: return "ILL_ILLTRP";
-        case ILL_PRVOPC: return "ILL_PRVOPC";
-        case ILL_PRVREG: return "ILL_PRVREG";
-        case ILL_COPROC: return "ILL_COPROC";
-        case ILL_BADSTK: return "ILL_BADSTK";
-      }
-      static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
-      break;
-    case SIGBUS:
-      switch (code) {
-        case BUS_ADRALN: return "BUS_ADRALN";
-        case BUS_ADRERR: return "BUS_ADRERR";
-        case BUS_OBJERR: return "BUS_OBJERR";
-        case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
-        case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
-      }
-      static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
-      break;
-    case SIGFPE:
-      switch (code) {
-        case FPE_INTDIV: return "FPE_INTDIV";
-        case FPE_INTOVF: return "FPE_INTOVF";
-        case FPE_FLTDIV: return "FPE_FLTDIV";
-        case FPE_FLTOVF: return "FPE_FLTOVF";
-        case FPE_FLTUND: return "FPE_FLTUND";
-        case FPE_FLTRES: return "FPE_FLTRES";
-        case FPE_FLTINV: return "FPE_FLTINV";
-        case FPE_FLTSUB: return "FPE_FLTSUB";
-      }
-      static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
-      break;
-    case SIGSEGV:
-      switch (code) {
-        case SEGV_MAPERR: return "SEGV_MAPERR";
-        case SEGV_ACCERR: return "SEGV_ACCERR";
-#if defined(SEGV_BNDERR)
-        case SEGV_BNDERR: return "SEGV_BNDERR";
-#endif
-#if defined(SEGV_PKUERR)
-        case SEGV_PKUERR: return "SEGV_PKUERR";
-#endif
-      }
-#if defined(SEGV_PKUERR)
-      static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
-#elif defined(SEGV_BNDERR)
-      static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
-#else
-      static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
-#endif
-      break;
-#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
-    case SIGSYS:
-      switch (code) {
-        case SYS_SECCOMP: return "SYS_SECCOMP";
-      }
-      static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
-      break;
-#endif
-    case SIGTRAP:
-      switch (code) {
-        case TRAP_BRKPT: return "TRAP_BRKPT";
-        case TRAP_TRACE: return "TRAP_TRACE";
-        case TRAP_BRANCH: return "TRAP_BRANCH";
-        case TRAP_HWBKPT: return "TRAP_HWBKPT";
-      }
-      if ((code & 0xff) == SIGTRAP) {
-        switch ((code >> 8) & 0xff) {
-          case PTRACE_EVENT_FORK:
-            return "PTRACE_EVENT_FORK";
-          case PTRACE_EVENT_VFORK:
-            return "PTRACE_EVENT_VFORK";
-          case PTRACE_EVENT_CLONE:
-            return "PTRACE_EVENT_CLONE";
-          case PTRACE_EVENT_EXEC:
-            return "PTRACE_EVENT_EXEC";
-          case PTRACE_EVENT_VFORK_DONE:
-            return "PTRACE_EVENT_VFORK_DONE";
-          case PTRACE_EVENT_EXIT:
-            return "PTRACE_EVENT_EXIT";
-          case PTRACE_EVENT_SECCOMP:
-            return "PTRACE_EVENT_SECCOMP";
-          case PTRACE_EVENT_STOP:
-            return "PTRACE_EVENT_STOP";
-        }
-      }
-      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
-      break;
-  }
-  // Then the other codes...
-  switch (code) {
-    case SI_USER: return "SI_USER";
-    case SI_KERNEL: return "SI_KERNEL";
-    case SI_QUEUE: return "SI_QUEUE";
-    case SI_TIMER: return "SI_TIMER";
-    case SI_MESGQ: return "SI_MESGQ";
-    case SI_ASYNCIO: return "SI_ASYNCIO";
-    case SI_SIGIO: return "SI_SIGIO";
-    case SI_TKILL: return "SI_TKILL";
-    case SI_DETHREAD: return "SI_DETHREAD";
-  }
-  // Then give up...
-  return "?";
-}
-
 static void dump_header_info(log_t* log) {
   auto fingerprint = GetProperty("ro.build.fingerprint", "unknown");
   auto revision = GetProperty("ro.revision", "unknown");
@@ -216,73 +76,64 @@
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
-static void dump_probable_cause(log_t* log, const siginfo_t& si) {
+static void dump_probable_cause(log_t* log, const siginfo_t* si) {
   std::string cause;
-  if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) {
-    if (si.si_addr < reinterpret_cast<void*>(4096)) {
+  if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
+    if (si->si_addr < reinterpret_cast<void*>(4096)) {
       cause = StringPrintf("null pointer dereference");
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
       cause = "call to kuser_helper_version";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
       cause = "call to kuser_get_tls";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
       cause = "call to kuser_cmpxchg";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
       cause = "call to kuser_memory_barrier";
-    } else if (si.si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
       cause = "call to kuser_cmpxchg64";
     }
-  } else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) {
-    cause = StringPrintf("seccomp prevented call to disallowed %s system call %d",
-                         ABI_STRING, si.si_syscall);
+  } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
+    cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
+                         si->si_syscall);
   }
 
   if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
 }
 
-static void dump_signal_info(log_t* log, const siginfo_t* siginfo) {
-  const siginfo_t& si = *siginfo;
+static void dump_signal_info(log_t* log, const siginfo_t* si) {
   char addr_desc[32]; // ", fault addr 0x1234"
-  if (signal_has_si_addr(si.si_signo, si.si_code)) {
-    snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
+  if (signal_has_si_addr(si->si_signo, si->si_code)) {
+    snprintf(addr_desc, sizeof(addr_desc), "%p", si->si_addr);
   } else {
     snprintf(addr_desc, sizeof(addr_desc), "--------");
   }
 
-  _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si.si_signo,
-       get_signame(si.si_signo), si.si_code, get_sigcode(si.si_signo, si.si_code), addr_desc);
+  _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si->si_signo,
+       get_signame(si->si_signo), si->si_code, get_sigcode(si->si_signo, si->si_code), addr_desc);
 
   dump_probable_cause(log, si);
 }
 
-static void dump_signal_info(log_t* log, pid_t tid) {
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    ALOGE("cannot get siginfo: %s\n", strerror(errno));
-    return;
-  }
-
-  dump_signal_info(log, &si);
-}
-
-static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, const char* process_name,
-                             const char* thread_name) {
+static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
   // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
   // TODO: Why is this controlled by thread name?
-  if (strcmp(thread_name, "logd") == 0 || strncmp(thread_name, "logd.", 4) == 0) {
+  if (thread_info.thread_name == "logd" ||
+      android::base::StartsWith(thread_info.thread_name, "logd.")) {
     log->should_retrieve_logcat = false;
   }
 
-  _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", pid, tid, thread_name,
-       process_name);
+  _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", thread_info.pid,
+       thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
 }
 
-static void dump_stack_segment(
-    Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) {
+static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+                               uintptr_t* sp, size_t words, int label) {
   // Read the data all at once.
   word_t stack_data[words];
-  size_t bytes_read = backtrace->Read(*sp, reinterpret_cast<uint8_t*>(&stack_data[0]), sizeof(word_t) * words);
+
+  // TODO: Do we need to word align this for crashes caused by a misaligned sp?
+  //       The process_vm_readv implementation of Memory should handle this appropriately?
+  size_t bytes_read = process_memory->Read(*sp, stack_data, sizeof(word_t) * words);
   words = bytes_read / sizeof(word_t);
   std::string line;
   for (size_t i = 0; i < words; i++) {
@@ -296,11 +147,11 @@
     line += StringPrintf("%" PRIPTR "  %" PRIPTR, *sp, stack_data[i]);
 
     backtrace_map_t map;
-    backtrace->FillInMap(stack_data[i], &map);
+    backtrace_map->FillIn(stack_data[i], &map);
     if (BacktraceMap::IsValid(map) && !map.name.empty()) {
       line += "  " + map.name;
       uintptr_t offset = 0;
-      std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset, &map));
+      std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
       if (!func_name.empty()) {
         line += " (" + func_name;
         if (offset) {
@@ -315,36 +166,38 @@
   }
 }
 
-static void dump_stack(Backtrace* backtrace, log_t* log) {
+static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+                       std::vector<backtrace_frame_data_t>& frames) {
   size_t first = 0, last;
-  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-    const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
-    if (frame->sp) {
+  for (size_t i = 0; i < frames.size(); i++) {
+    const backtrace_frame_data_t& frame = frames[i];
+    if (frame.sp) {
       if (!first) {
         first = i+1;
       }
       last = i;
     }
   }
+
   if (!first) {
     return;
   }
   first--;
 
   // Dump a few words before the first frame.
-  word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t);
-  dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1);
+  word_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
+  dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1);
 
   // Dump a few words from all successive frames.
   // Only log the first 3 frames, put the rest in the tombstone.
   for (size_t i = first; i <= last; i++) {
-    const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
+    const backtrace_frame_data_t* frame = &frames[i];
     if (sp != frame->sp) {
       _LOG(log, logtype::STACK, "         ........  ........\n");
       sp = frame->sp;
     }
     if (i == last) {
-      dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i);
+      dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i);
       if (sp < frame->sp + frame->stack_size) {
         _LOG(log, logtype::STACK, "         ........  ........\n");
       }
@@ -355,7 +208,7 @@
       } else if (words > STACK_WORDS) {
         words = STACK_WORDS;
       }
-      dump_stack_segment(backtrace, log, &sp, words, i);
+      dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i);
     }
   }
 }
@@ -372,44 +225,34 @@
   return addr_str;
 }
 
-static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) {
+static void dump_abort_message(log_t* log, Memory* process_memory, uintptr_t address) {
   if (address == 0) {
     return;
   }
 
-  address += sizeof(size_t);  // Skip the buffer length.
+  size_t length;
+  if (!process_memory->ReadFully(address, &length, sizeof(length))) {
+    _LOG(log, logtype::HEADER, "Failed to read abort message header: %s\n", strerror(errno));
+    return;
+  }
 
   char msg[512];
-  memset(msg, 0, sizeof(msg));
-  char* p = &msg[0];
-  while (p < &msg[sizeof(msg)]) {
-    word_t data;
-    size_t len = sizeof(word_t);
-    if (!backtrace->ReadWord(address, &data)) {
-      break;
-    }
-    address += sizeof(word_t);
-
-    while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) {
-      len--;
-    }
+  if (length >= sizeof(msg)) {
+    _LOG(log, logtype::HEADER, "Abort message too long: claimed length = %zd\n", length);
+    return;
   }
-  msg[sizeof(msg) - 1] = '\0';
 
+  if (!process_memory->ReadFully(address + sizeof(length), msg, length)) {
+    _LOG(log, logtype::HEADER, "Failed to read abort message: %s\n", strerror(errno));
+    return;
+  }
+
+  msg[length] = '\0';
   _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg);
 }
 
-static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) {
-  bool print_fault_address_marker = false;
-  uintptr_t addr = 0;
-  siginfo_t si;
-  memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) {
-    print_fault_address_marker = signal_has_si_addr(si.si_signo, si.si_code);
-    addr = reinterpret_cast<uintptr_t>(si.si_addr);
-  } else {
-    ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
-  }
+static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uintptr_t addr) {
+  bool print_fault_address_marker = addr;
 
   ScopedBacktraceMapIteratorLock lock(map);
   _LOG(log, logtype::MAPS,
@@ -464,7 +307,7 @@
       space_needed = false;
       line += "  " + entry->name;
       std::string build_id;
-      if ((entry->flags & PROT_READ) && elf_get_build_id(backtrace, entry->start, &build_id)) {
+      if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) {
         line += " (BuildId: " + build_id + ")";
       }
     }
@@ -482,50 +325,117 @@
   }
 }
 
-static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) {
-  if (backtrace->NumFrames()) {
-    _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
-    dump_backtrace_to_log(backtrace, log, "    ");
-
-    _LOG(log, logtype::STACK, "\nstack:\n");
-    dump_stack(backtrace, log);
+void dump_backtrace(log_t* log, std::vector<backtrace_frame_data_t>& frames, const char* prefix) {
+  for (auto& frame : frames) {
+    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, Backtrace::FormatFrameData(&frame).c_str());
   }
 }
 
-// Weak noop implementation, real implementations are in <arch>/machine.cpp.
-__attribute__((weak)) void dump_registers(log_t* log, const ucontext_t*) {
-  _LOG(log, logtype::REGISTERS, "    register dumping unimplemented on this architecture");
+static void print_register_row(log_t* log,
+                               const std::vector<std::pair<std::string, uint64_t>>& registers) {
+  std::string output;
+  for (auto& [name, value] : registers) {
+    output += android::base::StringPrintf("  %-3s %0*" PRIxPTR, name.c_str(),
+                                          static_cast<int>(2 * sizeof(void*)),
+                                          static_cast<uintptr_t>(value));
+  }
+
+  _LOG(log, logtype::REGISTERS, "  %s\n", output.c_str());
 }
 
-static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
-                        const std::string& thread_name, BacktraceMap* map,
-                        uintptr_t abort_msg_address, bool primary_thread) {
-  log->current_tid = tid;
+void dump_registers(log_t* log, Regs* regs) {
+  // Split lr/sp/pc into their own special row.
+  static constexpr size_t column_count = 4;
+  std::vector<std::pair<std::string, uint64_t>> current_row;
+  std::vector<std::pair<std::string, uint64_t>> special_row;
+
+#if defined(__arm__) || defined(__aarch64__)
+  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+#elif defined(__i386__)
+  static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
+#elif defined(__x86_64__)
+  static constexpr const char* special_registers[] = {"rbp", "rsp", "rip"};
+#else
+  static constexpr const char* special_registers[] = {};
+#endif
+
+  regs->IterateRegisters([log, &current_row, &special_row](const char* name, uint64_t value) {
+    auto row = &current_row;
+    for (const char* special_name : special_registers) {
+      if (strcmp(special_name, name) == 0) {
+        row = &special_row;
+        break;
+      }
+    }
+
+    row->emplace_back(name, value);
+    if (current_row.size() == column_count) {
+      print_register_row(log, current_row);
+      current_row.clear();
+    }
+  });
+
+  if (!current_row.empty()) {
+    print_register_row(log, current_row);
+  }
+
+  print_register_row(log, special_row);
+}
+
+void dump_memory_and_code(log_t* log, Memory* memory, Regs* regs) {
+  regs->IterateRegisters([log, memory](const char* name, uint64_t value) {
+    dump_memory(log, memory, value, "memory near %s:", name);
+  });
+}
+
+static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory,
+                        const ThreadInfo& thread_info, uintptr_t abort_msg_address,
+                        bool primary_thread) {
+  UNUSED(process_memory);
+  log->current_tid = thread_info.tid;
   if (!primary_thread) {
     _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
   }
-  dump_thread_info(log, pid, tid, process_name.c_str(), thread_name.c_str());
-  dump_signal_info(log, tid);
+  dump_thread_info(log, thread_info);
 
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
-  if (primary_thread) {
-    dump_abort_message(backtrace.get(), log, abort_msg_address);
+  if (thread_info.siginfo) {
+    dump_signal_info(log, thread_info.siginfo);
   }
-  dump_registers(log, tid);
-  if (backtrace->Unwind(0)) {
-    dump_backtrace_and_stack(backtrace.get(), log);
-  } else {
-    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+
+  dump_registers(log, thread_info.registers.get());
+
+  std::vector<backtrace_frame_data_t> frames;
+  if (!Backtrace::Unwind(thread_info.registers.get(), map, &frames, 0, nullptr)) {
+    _LOG(log, logtype::THREAD, "Failed to unwind");
+    return false;
+  }
+
+  if (!frames.empty()) {
+    _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+    dump_backtrace(log, frames, "    ");
+
+    _LOG(log, logtype::STACK, "\nstack:\n");
+    dump_stack(log, map, process_memory, frames);
   }
 
   if (primary_thread) {
-    dump_memory_and_code(log, backtrace.get());
+    dump_abort_message(log, process_memory, abort_msg_address);
+  }
+
+  if (primary_thread) {
+    dump_memory_and_code(log, process_memory, thread_info.registers.get());
     if (map) {
-      dump_all_maps(backtrace.get(), map, log, tid);
+      uintptr_t addr = 0;
+      siginfo_t* si = thread_info.siginfo;
+      if (signal_has_si_addr(si->si_signo, si->si_code)) {
+        addr = reinterpret_cast<uintptr_t>(si->si_addr);
+      }
+      dump_all_maps(log, map, process_memory, addr);
     }
   }
 
   log->current_tid = log->crashed_tid;
+  return true;
 }
 
 // Reads the contents of the specified log device, filters out the entries
@@ -534,8 +444,7 @@
 // If "tail" is non-zero, log the last "tail" number of lines.
 static EventTagMap* g_eventTagMap = NULL;
 
-static void dump_log_file(
-    log_t* log, pid_t pid, const char* filename, unsigned int tail) {
+static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
   bool first = true;
   struct logger_list* logger_list;
 
@@ -654,56 +563,15 @@
 // Dumps the logs generated by the specified pid to the tombstone, from both
 // "system" and "main" log devices.  Ideally we'd interleave the output.
 static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
+  if (pid == getpid()) {
+    // Cowardly refuse to dump logs while we're running in-process.
+    return;
+  }
+
   dump_log_file(log, pid, "system", tail);
   dump_log_file(log, pid, "main", tail);
 }
 
-// Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
-                       pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
-  // don't copy log messages to tombstone unless this is a dev device
-  bool want_logs = GetBoolProperty("ro.debuggable", false);
-
-  _LOG(log, logtype::HEADER,
-       "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
-  dump_header_info(log);
-  dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
-  if (want_logs) {
-    dump_logs(log, pid, 5);
-  }
-
-  for (const auto& it : threads) {
-    pid_t thread_tid = it.first;
-    const std::string& thread_name = it.second;
-
-    if (thread_tid != tid) {
-      dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
-    }
-  }
-
-  if (open_files) {
-    _LOG(log, logtype::OPEN_FILES, "\nopen files:\n");
-    dump_open_files_list_to_log(*open_files, log, "    ");
-  }
-
-  if (want_logs) {
-    dump_logs(log, pid, 0);
-  }
-}
-
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
-                       pid_t pid, pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
-                       std::string* amfd_data) {
-  log_t log;
-  log.current_tid = tid;
-  log.crashed_tid = tid;
-  log.tfd = tombstone_fd;
-  log.amfd_data = amfd_data;
-  dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
-}
-
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext) {
   pid_t pid = getpid();
@@ -721,31 +589,69 @@
   read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
   read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
 
-  _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
-  dump_header_info(&log);
-  dump_thread_info(&log, pid, tid, thread_name, process_name);
-  dump_signal_info(&log, siginfo);
+  std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
 
-  std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid));
-  dump_abort_message(backtrace.get(), &log, abort_msg_address);
-  dump_registers(&log, ucontext);
+  std::map<pid_t, ThreadInfo> threads;
+  threads[gettid()] = ThreadInfo{
+      .registers = std::move(regs),
+      .tid = tid,
+      .thread_name = thread_name,
+      .pid = pid,
+      .process_name = process_name,
+      .siginfo = siginfo,
+  };
 
-  if (backtrace->Unwind(0, ucontext)) {
-    dump_backtrace_and_stack(backtrace.get(), &log);
-  } else {
-    ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
+  std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid(), false));
+  if (!backtrace_map) {
+    ALOGE("failed to create backtrace map");
+    _exit(1);
   }
 
-  // TODO: Make this match the format of dump_all_maps above.
-  _LOG(&log, logtype::MAPS, "memory map:\n");
-  android::base::unique_fd maps_fd(open("/proc/self/maps", O_RDONLY | O_CLOEXEC));
-  if (maps_fd == -1) {
-    _LOG(&log, logtype::MAPS, "    failed to open /proc/self/maps: %s", strerror(errno));
-  } else {
-    char buf[256];
-    ssize_t rc;
-    while ((rc = TEMP_FAILURE_RETRY(read(maps_fd.get(), buf, sizeof(buf)))) > 0) {
-      android::base::WriteFully(tombstone_fd, buf, rc);
+  std::shared_ptr<Memory> process_memory = backtrace_map->GetProcessMemory();
+  engrave_tombstone(unique_fd(dup(tombstone_fd)), backtrace_map.get(), process_memory.get(),
+                    threads, tid, abort_msg_address, nullptr, nullptr);
+}
+
+void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory,
+                       const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
+                       uintptr_t abort_msg_address, OpenFilesList* open_files,
+                       std::string* amfd_data) {
+  // don't copy log messages to tombstone unless this is a dev device
+  bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
+
+  log_t log;
+  log.current_tid = target_thread;
+  log.crashed_tid = target_thread;
+  log.tfd = output_fd.get();
+  log.amfd_data = amfd_data;
+
+  _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+  dump_header_info(&log);
+
+  auto it = threads.find(target_thread);
+  if (it == threads.end()) {
+    LOG(FATAL) << "failed to find target thread";
+  }
+  dump_thread(&log, map, process_memory, it->second, abort_msg_address, true);
+
+  if (want_logs) {
+    dump_logs(&log, it->second.pid, 50);
+  }
+
+  for (auto& [tid, thread_info] : threads) {
+    if (tid == target_thread) {
+      continue;
     }
+
+    dump_thread(&log, map, process_memory, thread_info, 0, false);
+  }
+
+  if (open_files) {
+    _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n");
+    dump_open_files_list(&log, *open_files, "    ");
+  }
+
+  if (want_logs) {
+    dump_logs(&log, it->second.pid, 0);
   }
 }
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 1b74652..247d806 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -21,6 +21,8 @@
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
@@ -34,7 +36,9 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <backtrace/Backtrace.h>
+#include <debuggerd/handler.h>
 #include <log/log.h>
+#include <unwindstack/Memory.h>
 
 using android::base::unique_fd;
 
@@ -117,34 +121,10 @@
   }
 }
 
-bool wait_for_signal(pid_t tid, siginfo_t* siginfo) {
-  while (true) {
-    int status;
-    pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL));
-    if (n == -1) {
-      ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno));
-      return false;
-    } else if (n == tid) {
-      if (WIFSTOPPED(status)) {
-        if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) {
-          ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno));
-          return false;
-        }
-        return true;
-      } else {
-        ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status);
-        // This is the only circumstance under which we can allow a detach
-        // to fail with ESRCH, which indicates the tid has exited.
-        return false;
-      }
-    }
-  }
-}
-
 #define MEMORY_BYTES_TO_DUMP 256
 #define MEMORY_BYTES_PER_LINE 16
 
-void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
+void dump_memory(log_t* log, unwindstack::Memory* memory, uintptr_t addr, const char* fmt, ...) {
   std::string log_msg;
   va_list ap;
   va_start(ap, fmt);
@@ -172,7 +152,7 @@
   // Dump 256 bytes
   uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
   memset(data, 0, MEMORY_BYTES_TO_DUMP);
-  size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
+  size_t bytes = memory->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
   if (bytes % sizeof(uintptr_t) != 0) {
     // This should never happen, but just in case.
     ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
@@ -199,8 +179,8 @@
     // into a readable map. Only requires one extra read because a map has
     // to contain at least one page, and the total number of bytes to dump
     // is smaller than a page.
-    size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
-                                    sizeof(data) - bytes - start);
+    size_t bytes2 = memory->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+                                 sizeof(data) - bytes - start);
     bytes += bytes2;
     if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
       // This should never happen, but we'll try and continue any way.
@@ -264,3 +244,169 @@
   }
   strcpy(buf, default_value);
 }
+
+void drop_capabilities() {
+  __user_cap_header_struct capheader;
+  memset(&capheader, 0, sizeof(capheader));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capheader.pid = 0;
+
+  __user_cap_data_struct capdata[2];
+  memset(&capdata, 0, sizeof(capdata));
+
+  if (capset(&capheader, &capdata[0]) == -1) {
+    PLOG(FATAL) << "failed to drop capabilities";
+  }
+
+  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+    PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
+  }
+}
+
+bool signal_has_si_addr(int si_signo, int si_code) {
+  // Manually sent signals won't have si_addr.
+  if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
+    return false;
+  }
+
+  switch (si_signo) {
+    case SIGBUS:
+    case SIGFPE:
+    case SIGILL:
+    case SIGSEGV:
+    case SIGTRAP:
+      return true;
+    default:
+      return false;
+  }
+}
+
+const char* get_signame(int sig) {
+  switch (sig) {
+    case SIGABRT: return "SIGABRT";
+    case SIGBUS: return "SIGBUS";
+    case SIGFPE: return "SIGFPE";
+    case SIGILL: return "SIGILL";
+    case SIGSEGV: return "SIGSEGV";
+#if defined(SIGSTKFLT)
+    case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+    case SIGSTOP: return "SIGSTOP";
+    case SIGSYS: return "SIGSYS";
+    case SIGTRAP: return "SIGTRAP";
+    case DEBUGGER_SIGNAL: return "<debuggerd signal>";
+    default: return "?";
+  }
+}
+
+const char* get_sigcode(int signo, int code) {
+  // Try the signal-specific codes...
+  switch (signo) {
+    case SIGILL:
+      switch (code) {
+        case ILL_ILLOPC: return "ILL_ILLOPC";
+        case ILL_ILLOPN: return "ILL_ILLOPN";
+        case ILL_ILLADR: return "ILL_ILLADR";
+        case ILL_ILLTRP: return "ILL_ILLTRP";
+        case ILL_PRVOPC: return "ILL_PRVOPC";
+        case ILL_PRVREG: return "ILL_PRVREG";
+        case ILL_COPROC: return "ILL_COPROC";
+        case ILL_BADSTK: return "ILL_BADSTK";
+      }
+      static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
+      break;
+    case SIGBUS:
+      switch (code) {
+        case BUS_ADRALN: return "BUS_ADRALN";
+        case BUS_ADRERR: return "BUS_ADRERR";
+        case BUS_OBJERR: return "BUS_OBJERR";
+        case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
+        case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
+      }
+      static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
+      break;
+    case SIGFPE:
+      switch (code) {
+        case FPE_INTDIV: return "FPE_INTDIV";
+        case FPE_INTOVF: return "FPE_INTOVF";
+        case FPE_FLTDIV: return "FPE_FLTDIV";
+        case FPE_FLTOVF: return "FPE_FLTOVF";
+        case FPE_FLTUND: return "FPE_FLTUND";
+        case FPE_FLTRES: return "FPE_FLTRES";
+        case FPE_FLTINV: return "FPE_FLTINV";
+        case FPE_FLTSUB: return "FPE_FLTSUB";
+      }
+      static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
+      break;
+    case SIGSEGV:
+      switch (code) {
+        case SEGV_MAPERR: return "SEGV_MAPERR";
+        case SEGV_ACCERR: return "SEGV_ACCERR";
+#if defined(SEGV_BNDERR)
+        case SEGV_BNDERR: return "SEGV_BNDERR";
+#endif
+#if defined(SEGV_PKUERR)
+        case SEGV_PKUERR: return "SEGV_PKUERR";
+#endif
+      }
+#if defined(SEGV_PKUERR)
+      static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code");
+#elif defined(SEGV_BNDERR)
+      static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
+#else
+      static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
+#endif
+      break;
+#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too.
+    case SIGSYS:
+      switch (code) {
+        case SYS_SECCOMP: return "SYS_SECCOMP";
+      }
+      static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
+      break;
+#endif
+    case SIGTRAP:
+      switch (code) {
+        case TRAP_BRKPT: return "TRAP_BRKPT";
+        case TRAP_TRACE: return "TRAP_TRACE";
+        case TRAP_BRANCH: return "TRAP_BRANCH";
+        case TRAP_HWBKPT: return "TRAP_HWBKPT";
+      }
+      if ((code & 0xff) == SIGTRAP) {
+        switch ((code >> 8) & 0xff) {
+          case PTRACE_EVENT_FORK:
+            return "PTRACE_EVENT_FORK";
+          case PTRACE_EVENT_VFORK:
+            return "PTRACE_EVENT_VFORK";
+          case PTRACE_EVENT_CLONE:
+            return "PTRACE_EVENT_CLONE";
+          case PTRACE_EVENT_EXEC:
+            return "PTRACE_EVENT_EXEC";
+          case PTRACE_EVENT_VFORK_DONE:
+            return "PTRACE_EVENT_VFORK_DONE";
+          case PTRACE_EVENT_EXIT:
+            return "PTRACE_EVENT_EXIT";
+          case PTRACE_EVENT_SECCOMP:
+            return "PTRACE_EVENT_SECCOMP";
+          case PTRACE_EVENT_STOP:
+            return "PTRACE_EVENT_STOP";
+        }
+      }
+      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
+      break;
+  }
+  // Then the other codes...
+  switch (code) {
+    case SI_USER: return "SI_USER";
+    case SI_KERNEL: return "SI_KERNEL";
+    case SI_QUEUE: return "SI_QUEUE";
+    case SI_TIMER: return "SI_TIMER";
+    case SI_MESGQ: return "SI_MESGQ";
+    case SI_ASYNCIO: return "SI_ASYNCIO";
+    case SI_SIGIO: return "SI_SIGIO";
+    case SI_TKILL: return "SI_TKILL";
+    case SI_DETHREAD: return "SI_DETHREAD";
+  }
+  // Then give up...
+  return "?";
+}
diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
deleted file mode 100644
index 09a64cd..0000000
--- a/debuggerd/libdebuggerd/x86/machine.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2006, 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.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  struct pt_regs r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:");
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  struct pt_regs r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, "    eax %08lx  ebx %08lx  ecx %08lx  edx %08lx\n",
-       r.eax, r.ebx, r.ecx, r.edx);
-  _LOG(log, logtype::REGISTERS, "    esi %08lx  edi %08lx\n",
-       r.esi, r.edi);
-  _LOG(log, logtype::REGISTERS, "    xcs %08x  xds %08x  xes %08x  xfs %08x  xss %08x\n",
-       r.xcs, r.xds, r.xes, r.xfs, r.xss);
-  _LOG(log, logtype::REGISTERS, "    eip %08lx  ebp %08lx  esp %08lx  flags %08lx\n",
-       r.eip, r.ebp, r.esp, r.eflags);
-}
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
deleted file mode 100644
index de1c268..0000000
--- a/debuggerd/libdebuggerd/x86_64/machine.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-** Copyright 2013, 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.
-*/
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/machine.h"
-
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/user.h>
-
-#include <backtrace/Backtrace.h>
-#include <log/log.h>
-
-#include "libdebuggerd/utility.h"
-
-void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
-  struct user_regs_struct r;
-  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
-
-  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
-}
-
-void dump_registers(log_t* log, pid_t tid) {
-  struct user_regs_struct r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    ALOGE("cannot get registers: %s\n", strerror(errno));
-    return;
-  }
-
-  _LOG(log, logtype::REGISTERS, "    rax %016lx  rbx %016lx  rcx %016lx  rdx %016lx\n",
-       r.rax, r.rbx, r.rcx, r.rdx);
-  _LOG(log, logtype::REGISTERS, "    rsi %016lx  rdi %016lx\n",
-       r.rsi, r.rdi);
-  _LOG(log, logtype::REGISTERS, "    r8  %016lx  r9  %016lx  r10 %016lx  r11 %016lx\n",
-       r.r8, r.r9, r.r10, r.r11);
-  _LOG(log, logtype::REGISTERS, "    r12 %016lx  r13 %016lx  r14 %016lx  r15 %016lx\n",
-       r.r12, r.r13, r.r14, r.r15);
-  _LOG(log, logtype::REGISTERS, "    cs  %016lx  ss  %016lx\n",
-       r.cs, r.ss);
-  _LOG(log, logtype::REGISTERS, "    rip %016lx  rbp %016lx  rsp %016lx  eflags %016lx\n",
-       r.rip, r.rbp, r.rsp, r.eflags);
-}
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 7e1961e..6903b0e 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -16,7 +16,10 @@
 
 #pragma once
 
+#include <signal.h>
 #include <stdint.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
 
 #include "dump_type.h"
 
@@ -76,3 +79,11 @@
   InterceptStatus status;
   char error_message[127];  // always null-terminated
 };
+
+// Sent from handler to crash_dump via pipe.
+struct __attribute__((__packed__)) CrashInfo {
+  uint32_t version;  // must be 1.
+  siginfo_t siginfo;
+  ucontext_t ucontext;
+  uintptr_t abort_msg_address;
+};
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 0bb07ac..50c5efc 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,8 +18,12 @@
 
 #include <sys/socket.h>
 
+#include <string>
 #include <utility>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include "protocol.h"
@@ -86,3 +90,15 @@
 
   return result;
 }
+
+std::string get_process_name(pid_t pid) {
+  std::string result = "<unknown>";
+  android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
+  return result;
+}
+
+std::string get_thread_name(pid_t tid) {
+  std::string result = "<unknown>";
+  android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result);
+  return android::base::Trim(result);
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 171e07a..8260b44 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <string>
+
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
@@ -42,3 +44,6 @@
 //   plus any errors returned by the underlying recvmsg.
 ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
                 android::base::unique_fd* _Nullable out_fd);
+
+std::string get_process_name(pid_t pid);
+std::string get_thread_name(pid_t tid);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ed165ed..f23150d 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -25,6 +25,9 @@
         "-Werror",
         "-Wno-unused-variable",
     ],
+    cppflags: [
+        "-std=gnu++1z",
+    ],
 }
 
 cc_library_static {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2c18a6d..34afed1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -23,6 +23,10 @@
 #include <sys/mount.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <utility>
+#include <vector>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -397,16 +401,17 @@
 }
 
 static std::string read_fstab_from_dt() {
-    std::string fstab;
     if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
-        return fstab;
+        return {};
     }
 
     std::string fstabdir_name = get_android_dt_dir() + "/fstab";
     std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
-    if (!fstabdir) return fstab;
+    if (!fstabdir) return {};
 
     dirent* dp;
+    // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
+    std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
     while ((dp = readdir(fstabdir.get())) != NULL) {
         // skip over name, compatible and .
         if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
@@ -427,41 +432,54 @@
         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
-        fstab_entry.push_back(android::base::StringPrintf("/%s", dp->d_name));
+
+        std::string mount_point;
+        file_name =
+            android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+        if (read_dt_file(file_name, &value)) {
+            LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
+            mount_point = value;
+        } else {
+            mount_point = android::base::StringPrintf("/%s", dp->d_name);
+        }
+        fstab_entry.push_back(mount_point);
 
         file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
 
         file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
 
         file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
-
-        fstab += android::base::Join(fstab_entry, " ");
-        fstab += '\n';
+        // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
+        fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
     }
 
-    return fstab;
+    // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
+    std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
+              [](const auto& a, const auto& b) { return a.first < b.first; });
+
+    std::string fstab_result;
+    for (const auto& [_, dt_entry] : fstab_dt_entries) {
+        fstab_result += dt_entry + "\n";
+    }
+    return fstab_result;
 }
 
 bool is_dt_compatible() {
diff --git a/init/Android.bp b/init/Android.bp
index 0ec348c..2fea359 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -95,6 +95,8 @@
         "libprocessgroup",
         "libfs_mgr",
         "libprotobuf-cpp-lite",
+        "libpropertyinfoserializer",
+        "libpropertyinfoparser",
     ],
     include_dirs: [
         "system/core/mkbootimg",
@@ -193,6 +195,7 @@
         "libselinux",
         "libcrypto",
         "libprotobuf-cpp-lite",
+        "libpropertyinfoparser",
     ],
 }
 
diff --git a/init/Android.mk b/init/Android.mk
index 516f1b3..5239366 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -84,6 +84,8 @@
     libavb \
     libkeyutils \
     libprotobuf-cpp-lite \
+    libpropertyinfoserializer \
+    libpropertyinfoparser \
 
 LOCAL_REQUIRED_MODULES := \
     e2fsdroid \
diff --git a/init/action.cpp b/init/action.cpp
index 5fa6bec..ab51eea 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -358,7 +358,7 @@
     Subcontext* action_subcontext = nullptr;
     if (subcontexts_) {
         for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
                 action_subcontext = &subcontext;
                 break;
             }
diff --git a/init/devices.cpp b/init/devices.cpp
index af6b50a..8d27f4f 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -127,7 +127,7 @@
 }
 
 bool Permissions::Match(const std::string& path) const {
-    if (prefix_) return StartsWith(path, name_.c_str());
+    if (prefix_) return StartsWith(path, name_);
     if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
     return path == name_;
 }
@@ -300,9 +300,9 @@
         static const std::string devices_platform_prefix = "/devices/platform/";
         static const std::string devices_prefix = "/devices/";
 
-        if (StartsWith(device, devices_platform_prefix.c_str())) {
+        if (StartsWith(device, devices_platform_prefix)) {
             device = device.substr(devices_platform_prefix.length());
-        } else if (StartsWith(device, devices_prefix.c_str())) {
+        } else if (StartsWith(device, devices_prefix)) {
             device = device.substr(devices_prefix.length());
         }
 
diff --git a/init/parser.cpp b/init/parser.cpp
index 6ddb09f..4c69bac 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -76,7 +76,7 @@
                 // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                 // uevent.
                 for (const auto& [prefix, callback] : line_callbacks_) {
-                    if (android::base::StartsWith(args[0], prefix.c_str())) {
+                    if (android::base::StartsWith(args[0], prefix)) {
                         end_section();
 
                         if (auto result = callback(std::move(args)); !result) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3cf3ab9..4b6c502 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -50,17 +50,27 @@
 #include <android-base/strings.h>
 #include <bootimg.h>
 #include <fs_mgr.h>
+#include <property_info_parser/property_info_parser.h>
+#include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
 #include "init.h"
 #include "persistent_properties.h"
+#include "space_tokenizer.h"
 #include "util.h"
 
+using android::base::ReadFileToString;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::Timer;
+using android::base::Trim;
+using android::base::WriteStringToFile;
+using android::properties::BuildTrie;
+using android::properties::PropertyInfoAreaFile;
+using android::properties::PropertyInfoEntry;
 
 #define RECOVERY_MOUNT_POINT "/recovery"
 
@@ -71,27 +81,29 @@
 
 static int property_set_fd = -1;
 
-static struct selabel_handle* sehandle_prop;
+static PropertyInfoAreaFile property_info_area;
+
+void CreateSerializedPropertyInfo();
 
 void property_init() {
+    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
+    CreateSerializedPropertyInfo();
     if (__system_property_area_init()) {
         LOG(FATAL) << "Failed to initialize property area";
     }
+    if (!property_info_area.LoadDefaultPath()) {
+        LOG(FATAL) << "Failed to load serialized property info file";
+    }
 }
-
 static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
-
     if (!sctx) {
       return false;
     }
 
-    if (!sehandle_prop) {
-      return false;
-    }
-
-    char* tctx = nullptr;
-    if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) {
-      return false;
+    const char* target_context = nullptr;
+    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
+    if (target_context == nullptr) {
+        return false;
     }
 
     property_audit_data audit_data;
@@ -99,9 +111,9 @@
     audit_data.name = name.c_str();
     audit_data.cr = cr;
 
-    bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0);
+    bool has_access =
+        (selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0);
 
-    freecon(tctx);
     return has_access;
 }
 
@@ -433,7 +445,7 @@
         std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
         std::string process_cmdline;
         std::string process_log_string;
-        if (android::base::ReadFileToString(cmdline_path, &process_cmdline)) {
+        if (ReadFileToString(cmdline_path, &process_cmdline)) {
           // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
           process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
         }
@@ -714,9 +726,80 @@
     return 0;
 }
 
-void start_property_service() {
-    sehandle_prop = selinux_android_prop_context_handle();
+Result<PropertyInfoEntry> ParsePropertyInfoLine(const std::string& line) {
+    auto tokenizer = SpaceTokenizer(line);
 
+    auto property = tokenizer.GetNext();
+    if (property.empty()) return Error() << "Did not find a property entry in '" << line << "'";
+
+    auto context = tokenizer.GetNext();
+    if (context.empty()) return Error() << "Did not find a context entry in '" << line << "'";
+
+    // It is not an error to not find these, as older files will not contain them.
+    auto exact_match = tokenizer.GetNext();
+    auto schema = tokenizer.GetRemaining();
+
+    return {property, context, schema, exact_match == "exact"};
+}
+
+bool LoadPropertyInfoFromFile(const std::string& filename,
+                              std::vector<PropertyInfoEntry>* property_infos) {
+    auto file_contents = std::string();
+    if (!ReadFileToString(filename, &file_contents)) {
+        PLOG(ERROR) << "Could not read properties from '" << filename << "'";
+        return false;
+    }
+
+    for (const auto& line : Split(file_contents, "\n")) {
+        auto trimmed_line = Trim(line);
+        if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
+            continue;
+        }
+
+        auto property_info = ParsePropertyInfoLine(line);
+        if (!property_info) {
+            LOG(ERROR) << "Could not read line from '" << filename << "': " << property_info.error();
+            continue;
+        }
+
+        property_infos->emplace_back(*property_info);
+    }
+    return true;
+}
+
+void CreateSerializedPropertyInfo() {
+    auto property_infos = std::vector<PropertyInfoEntry>();
+    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
+        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
+                                      &property_infos)) {
+            return;
+        }
+        // Don't check for failure here, so we always have a sane list of properties.
+        // E.g. In case of recovery, the vendor partition will not have mounted and we
+        // still need the system / platform properties to function.
+        LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts", &property_infos);
+    } else {
+        if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
+            return;
+        }
+        LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
+    }
+    auto serialized_contexts = std::string();
+    auto error = std::string();
+    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
+                   &error)) {
+        LOG(ERROR) << "Unable to serialize property contexts: " << error;
+        return;
+    }
+
+    constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
+    if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
+        PLOG(ERROR) << "Unable to write serialized property infos to file";
+    }
+    selinux_android_restorecon(kPropertyInfosPath, 0);
+}
+
+void start_property_service() {
     selinux_callback cb;
     cb.func_audit = SelinuxAuditCallback;
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
diff --git a/init/service.cpp b/init/service.cpp
index 331b859..a4e33f7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1125,7 +1125,7 @@
     Subcontext* restart_action_subcontext = nullptr;
     if (subcontexts_) {
         for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
                 restart_action_subcontext = &subcontext;
                 break;
             }
diff --git a/init/space_tokenizer.h b/init/space_tokenizer.h
new file mode 100644
index 0000000..e7e22c5
--- /dev/null
+++ b/init/space_tokenizer.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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 _INIT_SPACE_TOKENIZER_H
+#define _INIT_SPACE_TOKENIZER_H
+
+namespace android {
+namespace init {
+
+class SpaceTokenizer {
+  public:
+    SpaceTokenizer(const std::string& string)
+        : string_(string), it_(string_.begin()), end_(string_.end()) {}
+
+    std::string GetNext() {
+        auto next = std::string();
+        while (it_ != end_ && !isspace(*it_)) {
+            next.push_back(*it_++);
+        }
+        while (it_ != end_ && isspace(*it_)) {
+            it_++;
+        }
+        return next;
+    }
+
+    std::string GetRemaining() { return std::string(it_, end_); }
+
+  private:
+    std::string string_;
+    std::string::const_iterator it_;
+    std::string::const_iterator end_;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index e18dbf3..5bb6edc 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -142,22 +142,33 @@
 }
 
 std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
-  switch (error) {
-  case BACKTRACE_UNWIND_NO_ERROR:
-    return "No error";
-  case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
-    return "Setup failed";
-  case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
-    return "No map found";
-  case BACKTRACE_UNWIND_ERROR_INTERNAL:
-    return "Internal libbacktrace error, please submit a bugreport";
-  case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
-    return "Thread doesn't exist";
-  case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
-    return "Thread has not responded to signal in time";
-  case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
-    return "Attempt to use an unsupported feature";
-  case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
-    return "Attempt to do an offline unwind without a context";
+  switch (error.error_code) {
+    case BACKTRACE_UNWIND_NO_ERROR:
+      return "No error";
+    case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+      return "Setup failed";
+    case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+      return "No map found";
+    case BACKTRACE_UNWIND_ERROR_INTERNAL:
+      return "Internal libbacktrace error, please submit a bugreport";
+    case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+      return "Thread doesn't exist";
+    case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+      return "Thread has not responded to signal in time";
+    case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+      return "Attempt to use an unsupported feature";
+    case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+      return "Attempt to do an offline unwind without a context";
+    case BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT:
+      return "Exceed MAX_BACKTRACE_FRAMES limit";
+    case BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED:
+      return android::base::StringPrintf("Failed to read memory at addr 0x%" PRIx64,
+                                         error.error_info.addr);
+    case BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED:
+      return android::base::StringPrintf("Failed to read register %" PRIu64, error.error_info.regno);
+    case BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED:
+      return "Failed to find a function in debug sections";
+    case BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED:
+      return "Failed to execute dwarf instructions in debug sections";
   }
 }
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index fb76b85..474d099 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -67,11 +67,11 @@
 bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
-    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   if (ucontext) {
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
@@ -163,7 +163,7 @@
     BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
-    error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
     return false;
   }
 
@@ -171,9 +171,9 @@
     // Do not emit an error message, this might be expected. Set the
     // error and let the caller decide.
     if (errno == ESRCH) {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
-      error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
     }
 
     sigaction(THREAD_SIGNAL, &oldact, nullptr);
@@ -218,9 +218,9 @@
   } else {
     // Check to see if the thread has disappeared.
     if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
       BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
     }
   }
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 3041492..30845a2 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -174,11 +174,11 @@
 bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
   if (context == nullptr) {
     BACK_LOGW("The context is needed for offline backtracing.");
-    error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
     return false;
   }
   context_ = context;
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
 
   unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
   unw_cursor_t cursor;
@@ -186,25 +186,38 @@
   if (ret != 0) {
     BACK_LOGW("unw_init_remote failed %d", ret);
     unw_destroy_addr_space(addr_space);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
   size_t num_frames = 0;
-  do {
+  while (true) {
     unw_word_t pc;
     ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
     if (ret < 0) {
       BACK_LOGW("Failed to read IP %d", ret);
+      error_.error_code = BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED;
+      error_.error_info.regno = UNW_REG_IP;
       break;
     }
     unw_word_t sp;
     ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
     if (ret < 0) {
       BACK_LOGW("Failed to read SP %d", ret);
+      error_.error_code = BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED;
+      error_.error_info.regno = UNW_REG_SP;
       break;
     }
 
     if (num_ignore_frames == 0) {
+      backtrace_map_t map;
+      FillInMap(pc, &map);
+      if (map.start == 0 || (map.flags & PROT_EXEC) == 0) {
+        // .eh_frame and .ARM.exidx doesn't know how to unwind from instructions setting up or
+        // destroying stack frames. It can lead to wrong callchains, which may contain pcs outside
+        // executable mapping areas. Stop unwinding once this is detected.
+        error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+        break;
+      }
       frames_.resize(num_frames + 1);
       backtrace_frame_data_t* frame = &frames_[num_frames];
       frame->num = num_frames;
@@ -217,14 +230,24 @@
         prev->stack_size = frame->sp - prev->sp;
       }
       frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
-      FillInMap(frame->pc, &frame->map);
+      frame->map = map;
       num_frames++;
     } else {
       num_ignore_frames--;
     }
+    is_debug_frame_used_ = false;
     ret = unw_step(&cursor);
-  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
-
+    if (ret <= 0) {
+      if (error_.error_code == BACKTRACE_UNWIND_NO_ERROR) {
+        error_.error_code = BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED;
+      }
+      break;
+    }
+    if (num_frames == MAX_BACKTRACE_FRAMES) {
+      error_.error_code = BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT;
+      break;
+    }
+  }
   unw_destroy_addr_space(addr_space);
   context_ = nullptr;
   return true;
@@ -258,7 +281,17 @@
     return read_size;
   }
   read_size = stack_space_.Read(addr, buffer, bytes);
-  return read_size;
+  if (read_size != 0) {
+    return read_size;
+  }
+  // In some libraries (like /system/lib64/libskia.so), some CIE entries in .eh_frame use
+  // augmentation "P", which makes libunwind/libunwindstack try to read personality routine in
+  // memory. However, that is not available in offline unwinding. Work around this by returning
+  // all zero data.
+  error_.error_code = BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED;
+  error_.error_info.addr = addr;
+  memset(buffer, 0, bytes);
+  return bytes;
 }
 
 bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
@@ -266,13 +299,17 @@
   backtrace_map_t map;
   FillInMap(ip, &map);
   if (!BacktraceMap::IsValid(map)) {
+    error_.error_code = BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED;
     return false;
   }
   const std::string& filename = map.name;
   DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename);
   if (debug_frame == nullptr) {
+    error_.error_code = BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED;
     return false;
   }
+  // Each FindProcInfo() is a new attempt to unwind, so reset the reason.
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
 
   eh_frame_hdr_space_.Clear();
   eh_frame_space_.Clear();
@@ -289,6 +326,23 @@
   // entry, it thinks that an ip address hits an entry when (entry.addr <= ip < next_entry.addr).
   // To prevent ip addresses hit in .eh_frame/.debug_frame being regarded as addresses hit in
   // .ARM.exidx, we need to check .eh_frame/.debug_frame first.
+
+  // Check .debug_frame/.gnu_debugdata before .eh_frame, because .debug_frame can unwind from
+  // instructions setting up or destroying stack frames, while .eh_frame can't.
+  if (!is_debug_frame_used_ && (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata)) {
+    is_debug_frame_used_ = true;
+    unw_dyn_info_t di;
+    unw_word_t segbase = map.start - debug_frame->min_vaddr;
+    // TODO: http://b/32916571
+    // TODO: Do it ourselves is more efficient than calling libunwind functions.
+    int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
+    if (found == 1) {
+      int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
+      if (ret == 0) {
+        return true;
+      }
+    }
+  }
   if (debug_frame->has_eh_frame) {
     if (ip_vaddr >= debug_frame->eh_frame.min_func_vaddr &&
         ip_vaddr < debug_frame->text_end_vaddr) {
@@ -318,19 +372,6 @@
       }
     }
   }
-  if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
-    unw_dyn_info_t di;
-    unw_word_t segbase = map.start - debug_frame->min_vaddr;
-    // TODO: http://b/32916571
-    // TODO: Do it ourselves is more efficient than calling libunwind functions.
-    int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
-    if (found == 1) {
-      int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
-      if (ret == 0) {
-        return true;
-      }
-    }
-  }
 
   if (debug_frame->has_arm_exidx) {
     auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
@@ -365,6 +406,7 @@
       }
     }
   }
+  error_.error_code = BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED;
   return false;
 }
 
@@ -546,6 +588,10 @@
   UNUSED(value);
   result = false;
 #endif
+  if (!result) {
+    error_.error_code = BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED;
+    error_.error_info.regno = reg;
+  }
   return result;
 }
 
@@ -584,14 +630,14 @@
   return debug_frame;
 }
 
-static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p) {
+static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p, bool is_elf64) {
   if (encode == DW_EH_PE_omit) {
     return 0;
   }
   uint8_t format = encode & 0x0f;
   switch (format) {
     case DW_EH_PE_ptr:
-      p += sizeof(unw_word_t);
+      p += is_elf64 ? 8 : 4;
       break;
     case DW_EH_PE_uleb128:
     case DW_EH_PE_sleb128:
@@ -619,7 +665,7 @@
 }
 
 static bool GetFdeTableOffsetInEhFrameHdr(const std::vector<uint8_t>& data,
-                                          uint64_t* table_offset_in_eh_frame_hdr) {
+                                          uint64_t* table_offset_in_eh_frame_hdr, bool is_elf64) {
   const uint8_t* p = data.data();
   const uint8_t* end = p + data.size();
   if (p + 4 > end) {
@@ -637,7 +683,8 @@
     return false;
   }
 
-  if (!OmitEncodedValue(eh_frame_ptr_encode, p) || !OmitEncodedValue(fde_count_encode, p)) {
+  if (!OmitEncodedValue(eh_frame_ptr_encode, p, is_elf64) ||
+      !OmitEncodedValue(fde_count_encode, p, is_elf64)) {
     return false;
   }
   if (p >= end) {
@@ -647,11 +694,214 @@
   return true;
 }
 
+static uint64_t ReadFromBuffer(const uint8_t*& p, size_t size) {
+  uint64_t result = 0;
+  int shift = 0;
+  while (size-- > 0) {
+    uint64_t tmp = *p++;
+    result |= tmp << shift;
+    shift += 8;
+  }
+  return result;
+}
+
+static uint64_t ReadSignValueFromBuffer(const uint8_t*& p, size_t size) {
+  uint64_t result = 0;
+  int shift = 0;
+  for (size_t i = 0; i < size; ++i) {
+    uint64_t tmp = *p++;
+    result |= tmp << shift;
+    shift += 8;
+  }
+  if (*(p - 1) & 0x80) {
+    result |= (-1ULL) << (size * 8);
+  }
+  return result;
+}
+
+static const char* ReadStrFromBuffer(const uint8_t*& p) {
+  const char* result = reinterpret_cast<const char*>(p);
+  p += strlen(result) + 1;
+  return result;
+}
+
+static int64_t ReadLEB128FromBuffer(const uint8_t*& p) {
+  int64_t result = 0;
+  int64_t tmp;
+  int shift = 0;
+  while (*p & 0x80) {
+    tmp = *p & 0x7f;
+    result |= tmp << shift;
+    shift += 7;
+    p++;
+  }
+  tmp = *p;
+  result |= tmp << shift;
+  if (*p & 0x40) {
+    result |= -((tmp & 0x40) << shift);
+  }
+  p++;
+  return result;
+}
+
+static uint64_t ReadULEB128FromBuffer(const uint8_t*& p) {
+  uint64_t result = 0;
+  uint64_t tmp;
+  int shift = 0;
+  while (*p & 0x80) {
+    tmp = *p & 0x7f;
+    result |= tmp << shift;
+    shift += 7;
+    p++;
+  }
+  tmp = *p;
+  result |= tmp << shift;
+  p++;
+  return result;
+}
+
+static uint64_t ReadEhEncoding(const uint8_t*& p, uint8_t encoding, bool is_elf64,
+                               uint64_t section_vaddr, const uint8_t* section_begin) {
+  const uint8_t* init_addr = p;
+  uint64_t result = 0;
+  switch (encoding & 0x0f) {
+    case DW_EH_PE_absptr:
+      result = ReadFromBuffer(p, is_elf64 ? 8 : 4);
+      break;
+    case DW_EH_PE_omit:
+      result = 0;
+      break;
+    case DW_EH_PE_uleb128:
+      result = ReadULEB128FromBuffer(p);
+      break;
+    case DW_EH_PE_udata2:
+      result = ReadFromBuffer(p, 2);
+      break;
+    case DW_EH_PE_udata4:
+      result = ReadFromBuffer(p, 4);
+      break;
+    case DW_EH_PE_udata8:
+      result = ReadFromBuffer(p, 8);
+      break;
+    case DW_EH_PE_sleb128:
+      result = ReadLEB128FromBuffer(p);
+      break;
+    case DW_EH_PE_sdata2:
+      result = ReadSignValueFromBuffer(p, 2);
+      break;
+    case DW_EH_PE_sdata4:
+      result = ReadSignValueFromBuffer(p, 4);
+      break;
+    case DW_EH_PE_sdata8:
+      result = ReadSignValueFromBuffer(p, 8);
+      break;
+  }
+  switch (encoding & 0xf0) {
+    case DW_EH_PE_pcrel:
+      result += init_addr - section_begin + section_vaddr;
+      break;
+    case DW_EH_PE_datarel:
+      result += section_vaddr;
+      break;
+  }
+  return result;
+}
+
+static bool BuildEhFrameHdr(DebugFrameInfo* info, bool is_elf64) {
+  // For each fde entry, collect its (func_vaddr, fde_vaddr) pair.
+  std::vector<std::pair<uint64_t, uint64_t>> index_table;
+  // Map form cie_offset to fde encoding.
+  std::unordered_map<size_t, uint8_t> cie_map;
+  const uint8_t* eh_frame_begin = info->eh_frame.data.data();
+  const uint8_t* eh_frame_end = eh_frame_begin + info->eh_frame.data.size();
+  const uint8_t* p = eh_frame_begin;
+  uint64_t eh_frame_vaddr = info->eh_frame.vaddr;
+  while (p < eh_frame_end) {
+    const uint8_t* unit_begin = p;
+    uint64_t unit_len = ReadFromBuffer(p, 4);
+    size_t secbytes = 4;
+    if (unit_len == 0xffffffff) {
+      unit_len = ReadFromBuffer(p, 8);
+      secbytes = 8;
+    }
+    const uint8_t* unit_end = p + unit_len;
+    uint64_t cie_id = ReadFromBuffer(p, secbytes);
+    if (cie_id == 0) {
+      // This is a CIE.
+      // Read version
+      uint8_t version = *p++;
+      // Read augmentation
+      const char* augmentation = ReadStrFromBuffer(p);
+      if (version >= 4) {
+        // Read address size and segment size
+        p += 2;
+      }
+      // Read code alignment factor
+      ReadULEB128FromBuffer(p);
+      // Read data alignment factor
+      ReadLEB128FromBuffer(p);
+      // Read return address register
+      if (version == 1) {
+        p++;
+      } else {
+        ReadULEB128FromBuffer(p);
+      }
+      uint8_t fde_pointer_encoding = 0;
+      if (augmentation[0] == 'z') {
+        // Read augmentation length.
+        ReadULEB128FromBuffer(p);
+        for (int i = 1; augmentation[i] != '\0'; ++i) {
+          char c = augmentation[i];
+          if (c == 'R') {
+            fde_pointer_encoding = *p++;
+          } else if (c == 'P') {
+            // Read personality handler
+            uint8_t encoding = *p++;
+            OmitEncodedValue(encoding, p, is_elf64);
+          } else if (c == 'L') {
+            // Read lsda encoding
+            p++;
+          }
+        }
+      }
+      cie_map[unit_begin - eh_frame_begin] = fde_pointer_encoding;
+    } else {
+      // This is an FDE.
+      size_t cie_offset = p - secbytes - eh_frame_begin - cie_id;
+      auto it = cie_map.find(cie_offset);
+      if (it != cie_map.end()) {
+        uint8_t fde_pointer_encoding = it->second;
+        uint64_t initial_location =
+            ReadEhEncoding(p, fde_pointer_encoding, is_elf64, eh_frame_vaddr, eh_frame_begin);
+        uint64_t fde_vaddr = unit_begin - eh_frame_begin + eh_frame_vaddr;
+        index_table.push_back(std::make_pair(initial_location, fde_vaddr));
+      }
+    }
+    p = unit_end;
+  }
+  if (index_table.empty()) {
+    return false;
+  }
+  std::sort(index_table.begin(), index_table.end());
+  info->eh_frame.hdr_vaddr = 0;
+  info->eh_frame.hdr_data.resize(index_table.size() * 8);
+  uint32_t* ptr = reinterpret_cast<uint32_t*>(info->eh_frame.hdr_data.data());
+  for (auto& pair : index_table) {
+    *ptr++ = static_cast<uint32_t>(pair.first - info->eh_frame.hdr_vaddr);
+    *ptr++ = static_cast<uint32_t>(pair.second - info->eh_frame.hdr_vaddr);
+  }
+  info->eh_frame.fde_table_offset = 0;
+  info->eh_frame.min_func_vaddr = index_table[0].first;
+  return true;
+}
+
 template <class ELFT>
 DebugFrameInfo* ReadDebugFrameFromELFFile(const llvm::object::ELFFile<ELFT>* elf) {
   DebugFrameInfo* result = new DebugFrameInfo;
+  result->eh_frame.hdr_vaddr = 0;
   result->text_end_vaddr = std::numeric_limits<uint64_t>::max();
 
+  bool is_elf64 = (elf->getHeader()->getFileClass() == llvm::ELF::ELFCLASS64);
   bool has_eh_frame_hdr = false;
   bool has_eh_frame = false;
 
@@ -671,8 +921,7 @@
               data->data(), data->data() + data->size());
 
           uint64_t fde_table_offset;
-          if (GetFdeTableOffsetInEhFrameHdr(result->eh_frame.hdr_data,
-                                             &fde_table_offset)) {
+          if (GetFdeTableOffsetInEhFrameHdr(result->eh_frame.hdr_data, &fde_table_offset, is_elf64)) {
             result->eh_frame.fde_table_offset = fde_table_offset;
             // Make sure we have at least one entry in fde_table.
             if (fde_table_offset + 2 * sizeof(int32_t) <= data->size()) {
@@ -729,6 +978,18 @@
     }
   }
 
+  if (has_eh_frame) {
+    if (!has_eh_frame_hdr) {
+      // Some libraries (like /vendor/lib64/egl/eglSubDriverAndroid.so) contain empty
+      // .eh_frame_hdr.
+      if (BuildEhFrameHdr(result, is_elf64)) {
+        has_eh_frame_hdr = true;
+      }
+    }
+    if (has_eh_frame_hdr) {
+      result->has_eh_frame = true;
+    }
+  }
   if (has_eh_frame_hdr && has_eh_frame) {
     result->has_eh_frame = true;
   }
diff --git a/libbacktrace/BacktraceOffline.h b/libbacktrace/BacktraceOffline.h
index c0b686e..fcde379 100644
--- a/libbacktrace/BacktraceOffline.h
+++ b/libbacktrace/BacktraceOffline.h
@@ -32,9 +32,7 @@
   uint64_t end;
   const uint8_t* data;
 
-  Space() {
-    Clear();
-  }
+  Space() { Clear(); }
 
   void Clear();
   size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
@@ -48,7 +46,8 @@
                    bool cache_file)
       : Backtrace(pid, tid, map),
         cache_file_(cache_file),
-        context_(nullptr) {
+        context_(nullptr),
+        is_debug_frame_used_(false) {
     stack_space_.start = stack.start;
     stack_space_.end = stack.end;
     stack_space_.data = stack.data;
@@ -78,6 +77,14 @@
   Space arm_extab_space_;
   Space arm_exidx_space_;
   Space stack_space_;
+
+  // is_debug_frame_used_ is to make sure we can try both .debug_frame and .ARM.exidx in
+  // FindProcInfo() on ARM. One example is EsxContext::Clear() in
+  // vendor/lib/egl/libGLESv2_adreno.so. EsxContext::Clear() appears in both .debug_frame and
+  // .ARM.exidx. However, libunwind fails to execute debug_frame instruction
+  // "DW_CFA_offset_extended: r265 at cfa-48". So we need to try .ARM.exidx to unwind that
+  // function.
+  bool is_debug_frame_used_;
 };
 
 #endif  // _LIBBACKTRACE_BACKTRACE_OFFLINE_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 2c87fa8..3ccf13c 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -81,7 +81,7 @@
     int ret = unw_getcontext(&context_);
     if (ret < 0) {
       BACK_LOGW("unw_getcontext failed %d", ret);
-      error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
       return false;
     }
   } else {
@@ -93,7 +93,7 @@
   int ret = unw_init_local(cursor.get(), &context_);
   if (ret < 0) {
     BACK_LOGW("unw_init_local failed %d", ret);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
   initialized_ = true;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 87282ef..2155b8a 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -62,7 +62,7 @@
   addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
   if (!addr_space_) {
     BACK_LOGW("unw_create_addr_space failed.");
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
@@ -72,7 +72,7 @@
   upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
   if (!upt_info_) {
     BACK_LOGW("Failed to create upt info.");
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
@@ -82,15 +82,15 @@
 bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
-    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
 
   if (ucontext) {
     BACK_LOGW("Unwinding from a specified context not supported yet.");
-    error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
     return false;
   }
 
@@ -102,7 +102,7 @@
   int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
   if (ret < 0) {
     BACK_LOGW("unw_init_remote failed %d", ret);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 56a6c68..2a555afc 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -102,7 +102,7 @@
     regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
   return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names);
 }
@@ -122,7 +122,7 @@
     regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr);
 }
 
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index d1b44a1..e92bc61 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -252,13 +252,41 @@
         return false;
       }
       HexStringToRawData(&line[pos], &testdata->unw_context, size);
-#if defined(__arm__)
     } else if (android::base::StartsWith(line, "regs:")) {
-      uint64_t pc;
-      uint64_t sp;
-      sscanf(line.c_str(), "regs: pc: %" SCNx64 " sp: %" SCNx64, &pc, &sp);
-      testdata->unw_context.regs[13] = sp;
-      testdata->unw_context.regs[15] = pc;
+      std::vector<std::string> strs = android::base::Split(line.substr(6), " ");
+      if (strs.size() % 2 != 0) {
+        return false;
+      }
+      std::vector<std::pair<std::string, uint64_t>> items;
+      for (size_t i = 0; i + 1 < strs.size(); i += 2) {
+        if (!android::base::EndsWith(strs[i], ":")) {
+          return false;
+        }
+        uint64_t value = std::stoul(strs[i + 1], nullptr, 16);
+        items.push_back(std::make_pair(strs[i].substr(0, strs[i].size() - 1), value));
+      }
+#if defined(__arm__)
+      for (auto& item : items) {
+        if (item.first == "sp") {
+          testdata->unw_context.regs[13] = item.second;
+        } else if (item.first == "pc") {
+          testdata->unw_context.regs[15] = item.second;
+        } else {
+          return false;
+        }
+      }
+#elif defined(__aarch64__)
+      for (auto& item : items) {
+        if (item.first == "pc") {
+          testdata->unw_context.uc_mcontext.pc = item.second;
+        } else if (item.first == "sp") {
+          testdata->unw_context.uc_mcontext.sp = item.second;
+        } else if (item.first == "x29") {
+          testdata->unw_context.uc_mcontext.regs[UNW_AARCH64_X29] = item.second;
+        } else {
+          return false;
+        }
+      }
 #endif
     } else if (android::base::StartsWith(line, "stack:")) {
       size_t size;
@@ -357,68 +385,24 @@
   BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
 }
 
-// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
-// overlap with each other, which appears in /system/lib/libart.so.
-TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
-  // TODO: For now, only run on the given arch.
-  if (std::string(ABI_STRING) != "arm") {
+static void LibUnwindingTest(const std::string& arch, const std::string& testdata_name,
+                             const std::string& testlib_name) {
+  if (std::string(ABI_STRING) != arch) {
     GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
                      << " isn't supported.";
     return;
   }
-  const std::string testlib_path(GetTestPath("libart.so"));
+  const std::string testlib_path(GetTestPath(testlib_name));
   struct stat st;
   ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
 
-  const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libart"));
+  const std::string offline_testdata_path(GetTestPath(testdata_name));
   OfflineTestData testdata;
   ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
 
-  // Fix path of /system/lib/libart.so.
+  // Fix path of the testlib.
   for (auto& map : testdata.maps) {
-    if (map.name.find("libart.so") != std::string::npos) {
-      map.name = testlib_path;
-    }
-  }
-
-  // Do offline backtrace.
-  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
-  ASSERT_TRUE(map != nullptr);
-
-  std::unique_ptr<Backtrace> backtrace(
-      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
-  ASSERT_TRUE(backtrace != nullptr);
-
-  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
-  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
-
-  // The last frame is outside of libart.so
-  ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
-  for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
-    uintptr_t vaddr_in_file =
-        backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
-    std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
-    ASSERT_EQ(name, testdata.symbols[i].name);
-  }
-}
-
-TEST(libbacktrace, offline_debug_frame_with_load_bias) {
-  if (std::string(ABI_STRING) != "arm") {
-    GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
-                     << " isn't supported.";
-    return;
-  }
-  const std::string testlib_path(GetTestPath("libandroid_runtime.so"));
-  struct stat st;
-  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
-
-  const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libandroid_runtime"));
-  OfflineTestData testdata;
-  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
-
-  // Fix path of /system/lib/libandroid_runtime.so.
-  for (auto& map : testdata.maps) {
-    if (map.name.find("libandroid_runtime.so") != std::string::npos) {
+    if (map.name.find(testlib_name) != std::string::npos) {
       map.name = testlib_path;
     }
   }
@@ -441,4 +425,38 @@
     std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
     ASSERT_EQ(name, testdata.symbols[i].name);
   }
+  ASSERT_TRUE(backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED ||
+              backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING);
+}
+
+// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
+// overlap with each other, which appears in /system/lib/libart.so.
+TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+  LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
+}
+
+TEST(libbacktrace, offline_debug_frame_with_load_bias) {
+  LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
+}
+
+TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
+  LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
+}
+
+TEST(libbacktrace, offline_cie_with_P_augmentation) {
+  // Make sure we can unwind through functions with CIE entry containing P augmentation, which
+  // makes unwinding library reading personality handler from memory. One example is
+  // /system/lib64/libskia.so.
+  LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
+}
+
+TEST(libbacktrace, offline_empty_eh_frame_hdr) {
+  // Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
+  // /vendor/lib64/egl/eglSubDriverAndroid.so.
+  LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
+}
+
+TEST(libbacktrace, offline_max_frames_limit) {
+  // The length of callchain can reach 256 when recording an application.
+  ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
 }
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 890ab3f..57b7553 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -189,7 +189,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 }
@@ -211,7 +211,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 }
@@ -241,7 +241,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   ASSERT_TRUE(backtrace->NumFrames() != 0);
   for (const auto& frame : *backtrace ) {
@@ -292,19 +292,19 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign1(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
@@ -340,7 +340,7 @@
       std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
       ASSERT_TRUE(backtrace.get() != nullptr);
       ASSERT_TRUE(backtrace->Unwind(0));
-      ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+      ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
       if (ReadyFunc(backtrace.get())) {
         VerifyFunc(backtrace.get(), create_func, map_create_func);
         verified = true;
@@ -389,12 +389,12 @@
   std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
 
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
@@ -480,7 +480,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 }
@@ -493,7 +493,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 }
@@ -535,7 +535,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 
@@ -575,17 +575,17 @@
   std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
 
@@ -616,7 +616,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 
@@ -713,21 +713,21 @@
   Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
   ASSERT_TRUE(back1 != nullptr);
   EXPECT_TRUE(back1->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError().error_code);
   delete back1;
   delete map1;
 
   Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
   ASSERT_TRUE(back2 != nullptr);
   EXPECT_TRUE(back2->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError().error_code);
   delete back2;
   delete map2;
 
   Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
   ASSERT_TRUE(back3 != nullptr);
   EXPECT_TRUE(back3->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError().error_code);
   delete back3;
   delete map3;
 }
@@ -1331,7 +1331,7 @@
                                                          BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   size_t frame_num;
   ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
@@ -1388,7 +1388,7 @@
     std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
     ASSERT_TRUE(backtrace.get() != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
     size_t frame_num;
     if (FindFuncFrameInBacktrace(backtrace.get(),
@@ -1417,7 +1417,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_FALSE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
 }
 
 TEST(libbacktrace, local_get_function_name_before_unwind) {
@@ -1785,7 +1785,7 @@
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
     delete backtrace;
   }
   size_t stable_pss = GetPssBytes();
@@ -1796,7 +1796,7 @@
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
     delete backtrace;
   }
   size_t new_pss = GetPssBytes();
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index e073533..5922664 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -34,7 +34,7 @@
 typedef uint32_t word_t;
 #endif
 
-enum BacktraceUnwindError : uint32_t {
+enum BacktraceUnwindErrorCode : uint32_t {
   BACKTRACE_UNWIND_NO_ERROR,
   // Something failed while trying to perform the setup to begin the unwind.
   BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
@@ -50,6 +50,29 @@
   BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
   // Attempt to do an offline unwind without a context.
   BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+  // The count of frames exceed MAX_BACKTRACE_FRAMES.
+  BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT,
+  // Failed to read memory.
+  BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED,
+  // Failed to read registers.
+  BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED,
+  // Failed to find a function in debug sections.
+  BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED,
+  // Failed to execute dwarf instructions in debug sections.
+  BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED,
+};
+
+struct BacktraceUnwindError {
+  enum BacktraceUnwindErrorCode error_code;
+
+  union {
+    // for BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED
+    uint64_t addr;
+    // for BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED
+    uint64_t regno;
+  } error_info;
+
+  BacktraceUnwindError() : error_code(BACKTRACE_UNWIND_NO_ERROR) {}
 };
 
 struct backtrace_frame_data_t {
diff --git a/libbacktrace/include/backtrace/backtrace_constants.h b/libbacktrace/include/backtrace/backtrace_constants.h
index 373a1e5..1a2da36 100644
--- a/libbacktrace/include/backtrace/backtrace_constants.h
+++ b/libbacktrace/include/backtrace/backtrace_constants.h
@@ -25,6 +25,6 @@
 // current thread of the specified pid.
 #define BACKTRACE_CURRENT_THREAD (-1)
 
-#define MAX_BACKTRACE_FRAMES 64
+#define MAX_BACKTRACE_FRAMES 256
 
 #endif // _BACKTRACE_BACKTRACE_CONSTANTS_H
diff --git a/libbacktrace/testdata/arm/libGLESv2_adreno.so b/libbacktrace/testdata/arm/libGLESv2_adreno.so
new file mode 100644
index 0000000..871f6dc
--- /dev/null
+++ b/libbacktrace/testdata/arm/libGLESv2_adreno.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno b/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno
new file mode 100644
index 0000000..1f96834
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libGLESv2_adreno
@@ -0,0 +1,6 @@
+pid: 7288 tid: 31656
+regs: pc: cc416235 sp: cc17f000
+map: start: cc361000 end: cc758000 offset: 0 load_bias: 9000 flags: 5 name: /vendor/lib/egl/libGLESv2_adreno.so
+stack: start: cc17f254 end: cc17f258 size: 4 b36141cc
+function: start: be1f0 end: be304 name: EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)
+function: start: be058 end: be1f0 name: EsxContext::ClearBuffersForDebug()
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
index 03e1df5..db9bf8d 100644
--- a/libbacktrace/testdata/arm/offline_testdata_for_libart
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -1,7 +1,7 @@
 pid: 32232 tid: 32233
 registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
 map: start: e9380000 end: e9766000 offset: 0 load_bias: b000 flags: 5 name: /system/lib/libart.so
-stack: start: ffd12dc0 end: ffd16000 size: 12864 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d8952431abed6fac33576fb438d1ff030000007800502400000000a0005024060000007893476f00908eec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004489bd6f78005024d00c5024a0005024a431d1ff2c31d1ff9b99aa71a4d49c6f30d8952400000000e8d895244489bd6fa8e5bc6fc8b895240100000000000000b033d1ff56000000637171e900000000d00c5024c8b89524000000000100000000000000b033d1ff560000006431d1ffa431d1ff000000009fb671e9b033d1ff00000000a431d1ff6431d1ffc431d1ff000000000000000081b771e9b033d1ff00000000c431d1ff8431d1ff01000000020000002429000001000000dc501b002033d1ff0100000018f9736f0100000000908eec58d8952440f180e9a8ec01245b215ce8a4d49c6f00908eec0832d1ffb033d1ff040000008c908eeca832d1ffabc141e9b033d1ff5b215ce82832d1ffb033d1ff080000008c908eec000000000035d1ff0834d1ffa832d1ffa4d49c6f04000000cca312e800908eec6832d1ffb033d1ff0834d1ff6bc354e9b033d1ff5b215ce8cca312e800908eec8832d1ffb033d1ff0834d1ff6bc354e900908eeca4d49c6f44b8bfeb1833d1ff000000006832d1ffb033d1ff478054e9b033d1ff1b8054e90834d1ffa4d49c6f0000000000000000000000000000000008000000000000000834d1ff0000000000000000000000000000000000000000000000000000000058d895240000000000000000000000000000000000000000000000000000000058d89524b17e54e98c56af6f00000000a4d49c6f288944e800908eec00000000d032d1ff0000000000000000000000000000000000000000000000007e8ea6c358a58cec00f580e90834d1ffa4d49c6f58d8952400908eecb033d1ffe9100000da8844e8833c70e9e9100000b033d1ff0200000058d8952408b1796f0200000000908eecda8844e82c34d1ff00908eece9100000005d70e9070000007d1300006034d1ff98d170e9b033d1ff0834d1ff148844e800908eecb033d1ffa034d1ffa833d1ff0100000044b8bfeb41f252e9e91fdeeaa491deea000000004700000001000000d9c4ddea0000000000000000b834d1ff00b051ff0834d1ff00908eecf833d1ffa034d1ff148844e800000000020000004d4c53e900000000000000000000000000908eec44b8bfeb0834d1ff3835d1ff148844e85035d1ffbb936fe90000000044b8bfebb033d1ffda8844e8148844e8000000000d0000005a0000007d137d13d00c502400000000600480240400000070048024f80c5024170000000100000002000000000000000040000000000000d0018024d00c502400000000600480240000000070048024f80c5024000000000000000000000000000000000000000000000000d001802465906fe97b2e5ce8000000000300000000000000881388131a00000001000000000000004cdd76e9010000007b2e5ce8020000009835d1ff5835d1ffc435d1ff010000000000000000000000010000000000000000dd76e90834d1ff0d0000000100000000000000000000005035d1ff9036d1ff00000000a435d1ff7e8ea6c3080000000000000000000000000000000000000038cb7e7044b8bfeb7d2e5ce800000000c037d1ff5600000000908eec00000000cc35d1ff55af71e9e0285a6f040000000800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e00000018eb01243173d870040000007d2e5ce800000000c037d1ff5600000000000000cc35d1ff637171e90000000018eb012402000000010000007d2e5ce800000000c037d1ff560000004436d1ff000000000000000081b771e9c037d1ff000000004436d1ff0436d1ff00e68dec0800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e000000adf861e918eb01243173d870040000007b2e5ce844b8bfeb00908eeca836d1ffc037d1ff040000008c908eec7c37d1ffd5c141e9c037d1ff7b2e5ce80000000000908eecd036d1ff00000000b038d1ff183ad1ff0000000044b8bfeb1038d1ff7c37d1ff6c37d1ffc037d1ff7b2e5ce8000000007b2e5ce8610d67e9c037d1ff7b2e5ce8280477e99835456f960300009a35456f10aa5e6f9a35456f9835456f68b85e6f881e77e9b30a47e9e81382e94c95b4ec7100000000908eec9c908eec30528bec1038d1ff7b2e5ce800000000c78469e91038d1ff0aeb3b52208989ec150645e9010000001038d1ff6c37d1ff44b8bfeb6c37d1ff00000000d837d1ff1038d1ff7b2e5ce8000000006c38d1ff8f0b67e97b2e5ce818eb012400000000000000000838d1ff7b2e5ce802000000040000007c37d1ff18eb01249835456f00000000901e77e9180000000300000000908eec480000004800000043000000640477e97669747954687265070000001a00000060eb0124000000000000000000000000a500000044b8bfeb1038d1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce96c38d1ff18eb012400908eeceeac73e943000000640477e9000059008bc95ce900908eec30528bec409181e900908eec430000005900000000528bec409181e900004300710000000300000030528bec89c75ce944b8bfebe2050000103dd1ff03000000a3f6a7eb89c75ce96c38d1ff7e8ea6c389c75ce997f5a7eb710000000000000030528bec7e8ea6c3e83cd1ff2079002488beba6ff0ca726f5600000000908eec000000005439d1ff8b1db8aa803a89ec7e8ea6c3000000009173d870ec55af6f00000000010000004892796f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003801802488beba6ff0ca726f56000000000000005439d1ff9d3daa71cc55af6f7039d1ff0b0000006839d1ff7d2e5ce800000000483bd1ff637171e900000000207900240b00000058a58cec40f180e9010000007d2e5ce800000000483bd1ff56000000cc39d1ff000000000000000081b771e9483bd1ff00000000cc39d1ff8c39d1ff05000000050000000c000000040000000100000012000000958c00000100000074d73500483bd1ff01000000e880746f0100000000908eec903ad1ff40f180e97e02000000908eec2079002400000000383ad1ff7b2e5ce8cc55af6f00908eec303ad1ff483bd1ff040000008c908eec043bd1ffd5c141e9483bd1ff7b2e5ce840f180e900908eec583ad1ff00000000000000000000000000000000cc55af6f983bd1ff043bd1fff43ad1ff483bd1ff7b2e5ce8000000007b2e5ce8610d67e9483bd1ff7b2e5ce8280477e94892796f860100004e92796f00a088ec4e92796f4892796f18a688ec881e77e9b30a47e978e388ec4c95b4ec2100000000908eec9c908eec30528bec983bd1ff7b2e5ce800000000c78469e9983bd1ff06b005fdf0298aec150645e901000000983bd1fff43ad1ffcc55af6ff43ad1ff00000000603bd1ff983bd1ff7b2e5ce800000000f43bd1ff8f0b67e97b2e5ce8e00864e80000000000000000903bd1ff7b2e5ce80200000004000000043bd1ff207900249c908eec04000000583bd1ff603bd1ff4892796f04000000ac3bd1ff01000000901e77e917885ee9010000004d5cb1eb485cb1eb00908eec4892796f00000000000000000000000000004300cc55af6f983bd1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce9f43bd1ff55000000ac3bd1ffeeac73e943000000640477e900005900e3225ce900908eec30528bec409181e900908eec430000005900000000528bec409181e9000043005500000078e388ec2100000009215ce901000000ce3fb8aae83cd1ff40420f00a3f6a7eb09215ce9f43bd1ff7e8ea6c309215ce9ed0ea8eb2100000075270000003289ec0000000030528becef665c74db0e42e911ac58e99daf58e9103dd1ff010000007e8ea6c31b000000385cd1ff385cd1ff02000000103dd1ff0300000087e26deae43cd1ff0200000001000000a31eb8aa020000007c3cd1ff18ac89ec1dac89ec0f000000fc94b4ec7c3cd1ff18ac89ec7e8ea6c3e83cd1ff884dd1ff741ab8aaa81ab8aa000000000700000004000000e43cd1ff3b19b8aa000000000000000000000000000000000000000000000000884dd1ff0000000001000000844dd1ff7e8ea6c3f065b4ec00fd0000205db8aa308789ec010000000000000004000000b8e78aec18ac89ec005db8aa2ceab2eb101082e935000000000000000800000001100000ba5bd1ff99000000b8e78aec205db8aa508789ec030000000000000004000000e2050000108789ec00000000d991aeece583aeec10d0acec10d0acec50d0acec6170705f70726f63657373333200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080a4ec00000000001000009dfe6feb00000000975673eb000000002516b8aa844dd1ff08000000a84dd1ff0000000000000000000000007c4dd1ff3b996feb00000000000000000000000000000000000000005015b8aad45cb8aadc5cb8aae85cb8aa804dd1ff0000000015b8aeec08000000ba5bd1ffd45bd1ffe05bd1ffee5bd1ff0f5cd1ff335cd1ff355cd1ff385cd1ff00000000535cd1ff6f5cd1ff825cd1ff9d5cd1ffbf5cd1ffd45cd1ffee5cd1ff015dd1ff1c5dd1ffe35ed1fffc5ed1ff465fd1ffc55fd1ff0000000010000000d6b0270006000000001000001100000064000000030000003400b8aa040000002000000005000000090000000700000000d0adec080000000000000009000000ec14b8aa0b000000752700000c000000752700000d000000752700000e000000752700001700000000000000190000007c4ed1ff1a0000001f0000001f000000de5fd1ff0f0000008c4ed1ff00000000000000000000000086da76325883c1a6b44d586d68c7843576386c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000636f6d2e6578616d706c652e7375646f67616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3d2f73797374656d2f62696e2f6170705f70726f63657373333200414e44524f49445f444154413d2f6461746100444f574e4c4f41445f43414348453d2f646174612f636163686500414e44524f49445f534f434b45545f7a79676f74655f7365636f6e646172793d3900414e44524f49445f524f4f543d2f73797374656d00415345435f4d4f554e54504f494e543d2f6d6e742f6173656300414e44524f49445f424f4f544c4f474f3d3100414e44524f49445f4153534554533d2f73797374656d2f61707000424f4f54434c415353504154483d2f73797374656d2f6672616d65776f726b2f636f72652d6f6a2e6a61723a2f73797374656d2f6672616d65776f726b2f636f72652d6c69626172742e6a61723a2f73797374656d2f6672616d65776f726b2f636f6e7363727970742e6a61723a2f73797374656d2f6672616d65776f726b2f6f6b687474702e6a61723a2f73797374656d2f6672616d65776f726b2f6c65676163792d746573742e6a61723a2f73797374656d2f6672616d65776f726b2f626f756e6379636173746c652e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6f72672e6170616368652e687474702e6c65676163792e626f6f742e6a617200414e44524f49445f53544f524147453d2f73746f7261676500504154483d2f7362696e3a2f73797374656d2f7362696e3a2f73797374656d2f62696e3a2f73797374656d2f7862696e3a2f76656e646f722f62696e3a2f76656e646f722f7862696e0053595354454d534552564552434c415353504154483d2f73797374656d2f6672616d65776f726b2f73657276696365732e6a61723a2f73797374656d2f6672616d65776f726b2f65746865726e65742d736572766963652e6a61723a2f73797374656d2f6672616d65776f726b2f776966692d736572766963652e6a61720045585445524e414c5f53544f524147453d2f736463617264002f73797374656d2f62696e2f6170705f70726f636573733332000000000000000000
+stack: start: ffd12dc0 end: ffd1306c size: 684 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d89524
 function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
 function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
 function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
diff --git a/libbacktrace/testdata/arm64/eglSubDriverAndroid.so b/libbacktrace/testdata/arm64/eglSubDriverAndroid.so
new file mode 100644
index 0000000..10ce06b
--- /dev/null
+++ b/libbacktrace/testdata/arm64/eglSubDriverAndroid.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/libskia.so b/libbacktrace/testdata/arm64/libskia.so
new file mode 100644
index 0000000..ef1a6a1
--- /dev/null
+++ b/libbacktrace/testdata/arm64/libskia.so
Binary files differ
diff --git a/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid b/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid
new file mode 100644
index 0000000..dfad172
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata_for_eglSubDriverAndroid
@@ -0,0 +1,6 @@
+pid: 12276 tid: 12303
+regs: pc: 7b8c027f64 sp: 7b8c157010 x29: 7b8c157040
+map: start: 7b8c01e000 end: 7b8c030000 offset: 0 load_bias: 0 flags: 5 name: /vendor/lib64/egl/eglSubDriverAndroid.so
+stack: start: 7b8c157048 end: 7b8c157050 size: 8 547e028c7b000000
+function: start: 9ed8 end: a1b0 name: EglAndroidWindowSurface::Initialize(EglAndroidConfig*, int const*)
+function: start: 9dcc end: 9ed8 name: EglAndroidWindowSurface::Create(ANativeWindow*, EglAndroidConfig*, EglAndroidWindowSurface**, int const*)
diff --git a/libbacktrace/testdata/arm64/offline_testdata_for_libskia b/libbacktrace/testdata/arm64/offline_testdata_for_libskia
new file mode 100644
index 0000000..1027c55
--- /dev/null
+++ b/libbacktrace/testdata/arm64/offline_testdata_for_libskia
@@ -0,0 +1,6 @@
+pid: 32232 tid: 32233
+regs: pc: 7c25189a0c sp: 7b8c154b50 x29: 7b8c154bb0
+map: start: 7c24c80000 end: 7c25413000 offset: 0 load_bias: 5f000 flags: 5 name: /system/lib64/libskia.so
+stack: start: 7b8c154bb8 end: 7b8c154bc0 size: 8 ec43f2247c000000
+function: start: 568970 end: 568c08 name: SkScalerContext_FreeType::generateImage(SkGlyph const&)
+function: start: 30330c end: 3044b0 name: SkScalerContext::getImage(SkGlyph const&)
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index 391adb6..d5dc66a 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -81,7 +81,7 @@
         }
 
         // check if path is <partition>/
-        if (android::base::StartsWith(path, prefix.c_str())) {
+        if (android::base::StartsWith(path, prefix)) {
             // rebuild path to be system/<partition>/... to check for alias
             path = alternate + path.substr(prefix.size());
             for (second = 0; second < paths.size(); ++second) {
@@ -97,7 +97,7 @@
         }
 
         // check if path is system/<partition>/
-        if (android::base::StartsWith(path, alternate.c_str())) {
+        if (android::base::StartsWith(path, alternate)) {
             // rebuild path to be <partition>/... to check for alias
             path = prefix + path.substr(alternate.size());
             for (second = 0; second < paths.size(); ++second) {
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index 427f2b4..7b64433 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Logging Library test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5d160ee..e9f0c0f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -24,17 +24,24 @@
 #include "cutils/properties.h"
 #include "log/log.h"
 #endif
+#include <dirent.h>
+#include <sys/types.h>
 #include "nativebridge/native_bridge.h"
 
 #include <algorithm>
-#include <vector>
-#include <string>
+#include <memory>
 #include <mutex>
+#include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/strings.h>
 
+#ifdef __BIONIC__
+#include <android-base/properties.h>
+#endif
+
 #define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
                                              "%s:%d: %s CHECK '" #predicate "' failed.",\
                                              __FILE__, __LINE__, __FUNCTION__)
@@ -78,15 +85,20 @@
   native_bridge_namespace_t* native_bridge_ns_;
 };
 
-static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot =
-                                  "/etc/public.libraries.txt";
-static constexpr const char* kPublicNativeLibrariesVendorConfig =
-                                  "/vendor/etc/public.libraries.txt";
-static constexpr const char* kLlndkNativeLibrariesSystemConfigPathFromRoot =
-                                  "/etc/llndk.libraries.txt";
-static constexpr const char* kVndkspNativeLibrariesSystemConfigPathFromRoot =
-                                  "/etc/vndksp.libraries.txt";
-
+static constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] =
+    "/etc/public.libraries.txt";
+static constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
+static constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
+    sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
+static constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
+static constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
+    sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
+static constexpr const char kPublicNativeLibrariesVendorConfig[] =
+    "/vendor/etc/public.libraries.txt";
+static constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] =
+    "/etc/llndk.libraries.txt";
+static constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] =
+    "/etc/vndksp.libraries.txt";
 
 // The device may be configured to have the vendor libraries loaded to a separate namespace.
 // For historical reasons this namespace was named sphal but effectively it is intended
@@ -110,6 +122,28 @@
   return std::string(debuggable) == "1";
 }
 
+static std::string vndk_version_str() {
+#ifdef __BIONIC__
+  std::string version = android::base::GetProperty("ro.vndk.version", "");
+  if (version != "" && version != "current") {
+    return "." + version;
+  }
+#endif
+  return "";
+}
+
+static void insert_vndk_version_str(std::string* file_name) {
+  CHECK(file_name != nullptr);
+  size_t insert_pos = file_name->find_last_of(".");
+  if (insert_pos == std::string::npos) {
+    insert_pos = file_name->length();
+  }
+  file_name->insert(insert_pos, vndk_version_str());
+}
+
+static const std::function<bool(const std::string&, std::string*)> always_true =
+    [](const std::string&, std::string*) { return true; };
+
 class LibraryNamespaces {
  public:
   LibraryNamespaces() : initialized_(false) { }
@@ -314,9 +348,58 @@
             root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
 
     std::string error_msg;
-    LOG_ALWAYS_FATAL_IF(!ReadConfig(public_native_libraries_system_config, &sonames, &error_msg),
-                        "Error reading public native library list from \"%s\": %s",
-                        public_native_libraries_system_config.c_str(), error_msg.c_str());
+    LOG_ALWAYS_FATAL_IF(
+        !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
+        "Error reading public native library list from \"%s\": %s",
+        public_native_libraries_system_config.c_str(), error_msg.c_str());
+
+    // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
+    // system libs that are exposed to apps. The libs in the txt files must be
+    // named as lib<name>.<companyname>.so.
+    std::string dirname = base::Dirname(public_native_libraries_system_config);
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname.c_str()), closedir);
+    if (dir != nullptr) {
+      // Failing to opening the dir is not an error, which can happen in
+      // webview_zygote.
+      struct dirent* ent;
+      while ((ent = readdir(dir.get())) != nullptr) {
+        if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+          continue;
+        }
+        const std::string filename(ent->d_name);
+        if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
+            android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
+          const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
+          const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
+          const std::string company_name = filename.substr(start, end - start);
+          const std::string config_file_path = dirname + "/" + filename;
+          LOG_ALWAYS_FATAL_IF(
+              company_name.empty(),
+              "Error extracting company name from public native library list file path \"%s\"",
+              config_file_path.c_str());
+          LOG_ALWAYS_FATAL_IF(
+              !ReadConfig(
+                  config_file_path, &sonames,
+                  [&company_name](const std::string& soname, std::string* error_msg) {
+                    if (android::base::StartsWith(soname, "lib") &&
+                        android::base::EndsWith(soname, "." + company_name + ".so")) {
+                      return true;
+                    } else {
+                      *error_msg = "Library name \"" + soname +
+                                   "\" does not end with the company name: " + company_name + ".";
+                      return false;
+                    }
+                  },
+                  &error_msg),
+              "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
+              error_msg.c_str());
+        }
+      }
+    }
+
+    // Insert VNDK version to llndk and vndksp config file names.
+    insert_vndk_version_str(&llndk_native_libraries_system_config);
+    insert_vndk_version_str(&vndksp_native_libraries_system_config);
 
     // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
     // variable to add libraries to the list. This is intended for platform tests only.
@@ -347,16 +430,16 @@
     system_public_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
-    ReadConfig(kLlndkNativeLibrariesSystemConfigPathFromRoot, &sonames);
+    ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
     system_llndk_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
-    ReadConfig(kVndkspNativeLibrariesSystemConfigPathFromRoot, &sonames);
+    ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
     system_vndksp_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
     // This file is optional, quietly ignore if the file does not exist.
-    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
 
     vendor_public_libraries_ = base::Join(sonames, ':');
   }
@@ -367,6 +450,8 @@
 
  private:
   bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
+                  const std::function<bool(const std::string& /* soname */,
+                                           std::string* /* error_msg */)>& check_soname,
                   std::string* error_msg = nullptr) {
     // Read list of public native libraries from the config file.
     std::string file_content;
@@ -403,7 +488,11 @@
         trimmed_line.resize(space_pos);
       }
 
-      sonames->push_back(trimmed_line);
+      if (check_soname(trimmed_line, error_msg)) {
+        sonames->push_back(trimmed_line);
+      } else {
+        return false;
+      }
     }
 
     return true;
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
new file mode 100644
index 0000000..2d33704
--- /dev/null
+++ b/libnativeloader/test/Android.bp
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_library {
+    name: "libfoo.oem1",
+    srcs: ["test.cpp"],
+    cflags : ["-DLIBNAME=\"libfoo.oem1.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+cc_library {
+    name: "libbar.oem1",
+    srcs: ["test.cpp"],
+    cflags : ["-DLIBNAME=\"libbar.oem1.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+cc_library {
+    name: "libfoo.oem2",
+    srcs: ["test.cpp"],
+    cflags : ["-DLIBNAME=\"libfoo.oem2.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+cc_library {
+    name: "libbar.oem2",
+    srcs: ["test.cpp"],
+    cflags : ["-DLIBNAME=\"libbar.oem2.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
new file mode 100644
index 0000000..4c3da4a
--- /dev/null
+++ b/libnativeloader/test/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem2.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
new file mode 100644
index 0000000..f9433e2
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem1.txt
@@ -0,0 +1,2 @@
+libfoo.oem1.so
+libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
new file mode 100644
index 0000000..de6bdb0
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem2.txt
@@ -0,0 +1,2 @@
+libfoo.oem2.so
+libbar.oem2.so
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.h b/libnativeloader/test/test.cpp
similarity index 69%
rename from debuggerd/libdebuggerd/test/ptrace_fake.h
rename to libnativeloader/test/test.cpp
index fdbb663..b166928 100644
--- a/debuggerd/libdebuggerd/test/ptrace_fake.h
+++ b/libnativeloader/test/test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -13,12 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "oemlib"
+#include <android-base/logging.h>
 
-#ifndef _DEBUGGERD_TEST_PTRACE_FAKE_H
-#define _DEBUGGERD_TEST_PTRACE_FAKE_H
-
-#include <signal.h>
-
-void ptrace_set_fake_getsiginfo(const siginfo_t&);
-
-#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H
+static __attribute__((constructor)) void test_lib_init() {
+  LOG(DEBUG) << LIBNAME << " loaded";
+}
diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c
index 96e1c10..09fc061 100644
--- a/libsuspend/autosuspend.c
+++ b/libsuspend/autosuspend.c
@@ -28,8 +28,7 @@
 static bool autosuspend_enabled;
 static bool autosuspend_inited;
 
-static int autosuspend_init(void)
-{
+static int autosuspend_init(void) {
     if (autosuspend_inited) {
         return 0;
     }
@@ -40,19 +39,18 @@
     }
 
     if (!autosuspend_ops) {
-        ALOGE("failed to initialize autosuspend\n");
+        ALOGE("failed to initialize autosuspend");
         return -1;
     }
 
 out:
     autosuspend_inited = true;
 
-    ALOGV("autosuspend initialized\n");
+    ALOGV("autosuspend initialized");
     return 0;
 }
 
-int autosuspend_enable(void)
-{
+int autosuspend_enable(void) {
     int ret;
 
     ret = autosuspend_init();
@@ -60,7 +58,7 @@
         return ret;
     }
 
-    ALOGV("autosuspend_enable\n");
+    ALOGV("autosuspend_enable");
 
     if (autosuspend_enabled) {
         return 0;
@@ -75,8 +73,7 @@
     return 0;
 }
 
-int autosuspend_disable(void)
-{
+int autosuspend_disable(void) {
     int ret;
 
     ret = autosuspend_init();
@@ -84,7 +81,7 @@
         return ret;
     }
 
-    ALOGV("autosuspend_disable\n");
+    ALOGV("autosuspend_disable");
 
     if (!autosuspend_enabled) {
         return 0;
@@ -98,3 +95,16 @@
     autosuspend_enabled = false;
     return 0;
 }
+
+void autosuspend_set_wakeup_callback(void (*func)(bool success)) {
+    int ret;
+
+    ret = autosuspend_init();
+    if (ret) {
+        return;
+    }
+
+    ALOGV("set_wakeup_callback");
+
+    autosuspend_ops->set_wakeup_callback(func);
+}
diff --git a/libsuspend/autosuspend_ops.h b/libsuspend/autosuspend_ops.h
index 698e25b..2f435d9 100644
--- a/libsuspend/autosuspend_ops.h
+++ b/libsuspend/autosuspend_ops.h
@@ -20,10 +20,9 @@
 struct autosuspend_ops {
     int (*enable)(void);
     int (*disable)(void);
+    void (*set_wakeup_callback)(void (*func)(bool success));
 };
 
-struct autosuspend_ops *autosuspend_autosleep_init(void);
-struct autosuspend_ops *autosuspend_earlysuspend_init(void);
 struct autosuspend_ops *autosuspend_wakeup_count_init(void);
 
 #endif
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 2da204a..81cb44c 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -42,7 +42,7 @@
 static int wakeup_count_fd;
 static pthread_t suspend_thread;
 static sem_t suspend_lockout;
-static const char *sleep_state = "mem";
+static const char* sleep_state = "mem";
 static void (*wakeup_func)(bool success) = NULL;
 static int sleep_time = BASE_SLEEP_TIME;
 
@@ -55,8 +55,7 @@
     sleep_time = MIN(sleep_time * 2, 60000000);
 }
 
-static void *suspend_thread_func(void *arg __attribute__((unused)))
-{
+static void* suspend_thread_func(void* arg __attribute__((unused))) {
     char buf[80];
     char wakeup_count[20];
     int wakeup_count_len;
@@ -67,36 +66,36 @@
         update_sleep_time(success);
         usleep(sleep_time);
         success = false;
-        ALOGV("%s: read wakeup_count\n", __func__);
+        ALOGV("%s: read wakeup_count", __func__);
         lseek(wakeup_count_fd, 0, SEEK_SET);
         wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
                 sizeof(wakeup_count)));
         if (wakeup_count_len < 0) {
             strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+            ALOGE("Error reading from %s: %s", SYS_POWER_WAKEUP_COUNT, buf);
             wakeup_count_len = 0;
             continue;
         }
         if (!wakeup_count_len) {
-            ALOGE("Empty wakeup count\n");
+            ALOGE("Empty wakeup count");
             continue;
         }
 
-        ALOGV("%s: wait\n", __func__);
+        ALOGV("%s: wait", __func__);
         ret = sem_wait(&suspend_lockout);
         if (ret < 0) {
             strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error waiting on semaphore: %s\n", buf);
+            ALOGE("Error waiting on semaphore: %s", buf);
             continue;
         }
 
-        ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
+        ALOGV("%s: write %*s to wakeup_count", __func__, wakeup_count_len, wakeup_count);
         ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
         if (ret < 0) {
             strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+            ALOGE("Error writing to %s: %s", SYS_POWER_WAKEUP_COUNT, buf);
         } else {
-            ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
+            ALOGV("%s: write %s to %s", __func__, sleep_state, SYS_POWER_STATE);
             ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
             if (ret >= 0) {
                 success = true;
@@ -107,56 +106,53 @@
             }
         }
 
-        ALOGV("%s: release sem\n", __func__);
+        ALOGV("%s: release sem", __func__);
         ret = sem_post(&suspend_lockout);
         if (ret < 0) {
             strerror_r(errno, buf, sizeof(buf));
-            ALOGE("Error releasing semaphore: %s\n", buf);
+            ALOGE("Error releasing semaphore: %s", buf);
         }
     }
     return NULL;
 }
 
-static int autosuspend_wakeup_count_enable(void)
-{
+static int autosuspend_wakeup_count_enable(void) {
     char buf[80];
     int ret;
 
-    ALOGV("autosuspend_wakeup_count_enable\n");
+    ALOGV("autosuspend_wakeup_count_enable");
 
     ret = sem_post(&suspend_lockout);
 
     if (ret < 0) {
         strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error changing semaphore: %s\n", buf);
+        ALOGE("Error changing semaphore: %s", buf);
     }
 
-    ALOGV("autosuspend_wakeup_count_enable done\n");
+    ALOGV("autosuspend_wakeup_count_enable done");
 
     return ret;
 }
 
-static int autosuspend_wakeup_count_disable(void)
-{
+static int autosuspend_wakeup_count_disable(void) {
     char buf[80];
     int ret;
 
-    ALOGV("autosuspend_wakeup_count_disable\n");
+    ALOGV("autosuspend_wakeup_count_disable");
 
     ret = sem_wait(&suspend_lockout);
 
     if (ret < 0) {
         strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error changing semaphore: %s\n", buf);
+        ALOGE("Error changing semaphore: %s", buf);
     }
 
-    ALOGV("autosuspend_wakeup_count_disable done\n");
+    ALOGV("autosuspend_wakeup_count_disable done");
 
     return ret;
 }
 
-void set_wakeup_callback(void (*func)(bool success))
-{
+static void autosuspend_set_wakeup_callback(void (*func)(bool success)) {
     if (wakeup_func != NULL) {
         ALOGE("Duplicate wakeup callback applied, keeping original");
         return;
@@ -165,43 +161,43 @@
 }
 
 struct autosuspend_ops autosuspend_wakeup_count_ops = {
-        .enable = autosuspend_wakeup_count_enable,
-        .disable = autosuspend_wakeup_count_disable,
+    .enable = autosuspend_wakeup_count_enable,
+    .disable = autosuspend_wakeup_count_disable,
+    .set_wakeup_callback = autosuspend_set_wakeup_callback,
 };
 
-struct autosuspend_ops *autosuspend_wakeup_count_init(void)
-{
+struct autosuspend_ops* autosuspend_wakeup_count_init(void) {
     int ret;
     char buf[80];
 
     state_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_STATE, O_RDWR));
     if (state_fd < 0) {
         strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf);
+        ALOGE("Error opening %s: %s", SYS_POWER_STATE, buf);
         goto err_open_state;
     }
 
     wakeup_count_fd = TEMP_FAILURE_RETRY(open(SYS_POWER_WAKEUP_COUNT, O_RDWR));
     if (wakeup_count_fd < 0) {
         strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf);
+        ALOGE("Error opening %s: %s", SYS_POWER_WAKEUP_COUNT, buf);
         goto err_open_wakeup_count;
     }
 
     ret = sem_init(&suspend_lockout, 0, 0);
     if (ret < 0) {
         strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error creating semaphore: %s\n", buf);
+        ALOGE("Error creating semaphore: %s", buf);
         goto err_sem_init;
     }
     ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);
     if (ret) {
         strerror_r(ret, buf, sizeof(buf));
-        ALOGE("Error creating thread: %s\n", buf);
+        ALOGE("Error creating thread: %s", buf);
         goto err_pthread_create;
     }
 
-    ALOGI("Selected wakeup count\n");
+    ALOGI("Selected wakeup count");
     return &autosuspend_wakeup_count_ops;
 
 err_pthread_create:
diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h
index 59188a8..e130ca3 100644
--- a/libsuspend/include/suspend/autosuspend.h
+++ b/libsuspend/include/suspend/autosuspend.h
@@ -51,7 +51,7 @@
  * success is true if the suspend was sucessful and false if the suspend
  * aborted due to some reason.
  */
-void set_wakeup_callback(void (*func)(bool success));
+void autosuspend_set_wakeup_callback(void (*func)(bool success));
 
 __END_DECLS
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 4125b12..133f3b9 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -64,6 +64,8 @@
         "RegsArm64.cpp",
         "RegsX86.cpp",
         "RegsX86_64.cpp",
+        "RegsMips.cpp",
+        "RegsMips64.cpp",
         "Unwinder.cpp",
         "Symbols.cpp",
     ],
@@ -86,6 +88,12 @@
         x86_64: {
             srcs: ["AsmGetRegsX86_64.S"],
         },
+        mips: {
+            srcs: ["AsmGetRegsMips.S"],
+        },
+        mips64: {
+            srcs: ["AsmGetRegsMips64.S"],
+        },
     },
 
     shared_libs: [
@@ -128,6 +136,7 @@
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
+        "tests/MemoryOfflineTest.cpp",
         "tests/MemoryRangeTest.cpp",
         "tests/MemoryRemoteTest.cpp",
         "tests/MemoryTest.cpp",
@@ -159,6 +168,7 @@
     data: [
         "tests/files/elf32.xz",
         "tests/files/elf64.xz",
+        "tests/files/offline/gnu_debugdata_arm32/*",
         "tests/files/offline/straddle_arm32/*",
         "tests/files/offline/straddle_arm64/*",
     ],
@@ -205,6 +215,15 @@
     ],
 }
 
+cc_binary {
+    name: "unwind_for_offline",
+    defaults: ["libunwindstack_tools"],
+
+    srcs: [
+        "tools/unwind_for_offline.cpp",
+    ],
+}
+
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
diff --git a/libunwindstack/AsmGetRegsMips.S b/libunwindstack/AsmGetRegsMips.S
new file mode 100644
index 0000000..183d0a9
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+  .text
+  .type     AsmGetRegs, %function
+  .globl    AsmGetRegs
+  .ent      AsmGetRegs
+  .balign   16
+AsmGetRegs:
+  .cfi_startproc
+  .cfi_def_cfa $sp, 0
+  .set push
+  .set noreorder
+  .cpload $t9
+  sw   $zero, 0($a0)
+  .set noat
+  sw   $at, 4($a0)
+  .set at
+  sw   $v0, 8($a0)
+  sw   $v1, 12($a0)
+  sw   $a0, 16($a0)
+  sw   $a1, 20($a0)
+  sw   $a2, 24($a0)
+  sw   $a3, 28($a0)
+  sw   $t0, 32($a0)
+  sw   $t1, 36($a0)
+  sw   $t2, 40($a0)
+  sw   $t3, 44($a0)
+  sw   $t4, 48($a0)
+  sw   $t5, 52($a0)
+  sw   $t6, 56($a0)
+  sw   $t7, 60($a0)
+  sw   $s0, 64($a0)
+  sw   $s1, 68($a0)
+  sw   $s2, 72($a0)
+  sw   $s3, 76($a0)
+  sw   $s4, 80($a0)
+  sw   $s5, 84($a0)
+  sw   $s6, 88($a0)
+  sw   $s7, 92($a0)
+  sw   $t8, 96($a0)
+  sw   $t9, 100($a0)
+  sw   $k0, 104($a0)
+  sw   $k1, 108($a0)
+  sw   $gp, 112($a0)
+  sw   $sp, 116($a0)
+  sw   $s8, 120($a0)
+  sw   $ra, 124($a0)
+  jalr $zero, $ra
+  sw   $ra, 128($a0)   // set PC to the calling function
+
+  .set pop
+  .cfi_endproc
+  .size     AsmGetRegs, .-AsmGetRegs
+  .end      AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsMips64.S b/libunwindstack/AsmGetRegsMips64.S
new file mode 100644
index 0000000..7a244f6
--- /dev/null
+++ b/libunwindstack/AsmGetRegsMips64.S
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+  .text
+  .type     AsmGetRegs, %function
+  .globl    AsmGetRegs
+  .ent      AsmGetRegs
+  .balign    16
+AsmGetRegs:
+  .cfi_startproc
+  .cfi_def_cfa $sp, 0
+  .set push
+  .set noreorder
+  .cpload $t9
+  sd   $zero, 0($a0)
+  .set noat
+  sd   $at, 8($a0)
+  .set at
+  sd   $v0, 16($a0)
+  sd   $v1, 24($a0)
+  sd   $a0, 32($a0)
+  sd   $a1, 40($a0)
+  sd   $a2, 48($a0)
+  sd   $a3, 56($a0)
+  sd   $a4, 64($a0)
+  sd   $a5, 72($a0)
+  sd   $a6, 80($a0)
+  sd   $a7, 88($a0)
+  sd   $t0, 96($a0)
+  sd   $t1, 104($a0)
+  sd   $t2, 112($a0)
+  sd   $t3, 120($a0)
+  sd   $s0, 128($a0)
+  sd   $s1, 136($a0)
+  sd   $s2, 144($a0)
+  sd   $s3, 152($a0)
+  sd   $s4, 160($a0)
+  sd   $s5, 168($a0)
+  sd   $s6, 176($a0)
+  sd   $s7, 184($a0)
+  sd   $t8, 192($a0)
+  sd   $t9, 200($a0)
+  sd   $k0, 208($a0)
+  sd   $k1, 216($a0)
+  sd   $gp, 224($a0)
+  sd   $sp, 232($a0)
+  sd   $s8, 240($a0)
+  sd   $ra, 248($a0)
+  jalr $zero, $ra
+  sd   $ra, 256($a0)   // set PC to the calling function
+
+  .set pop
+  .cfi_endproc
+  .size AsmGetRegs, .-AsmGetRegs
+  .end      AsmGetRegs
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 025429f..5ec4a3d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -79,6 +79,7 @@
   uint64_t load_bias;
   if (gnu->Init(&load_bias)) {
     gnu->InitHeaders();
+    interface_->SetGnuDebugdataInterface(gnu);
   } else {
     // Free all of the memory associated with the gnu_debugdata section.
     gnu_debugdata_memory_.reset(nullptr);
@@ -115,17 +116,9 @@
     return true;
   }
 
-  // Adjust the load bias to get the real relative pc.
-  if (adjusted_rel_pc < load_bias_) {
-    return false;
-  }
-  adjusted_rel_pc -= load_bias_;
-
   // Lock during the step which can update information in the object.
   std::lock_guard<std::mutex> guard(lock_);
-  return interface_->Step(adjusted_rel_pc, regs, process_memory, finished) ||
-         (gnu_debugdata_interface_ &&
-          gnu_debugdata_interface_->Step(adjusted_rel_pc, regs, process_memory, finished));
+  return interface_->Step(adjusted_rel_pc, load_bias_, regs, process_memory, finished);
 }
 
 bool Elf::IsValidElf(Memory* memory) {
@@ -189,9 +182,12 @@
     } else if (e_machine == EM_386) {
       arch_ = ARCH_X86;
       interface.reset(new ElfInterface32(memory));
+    } else if (e_machine == EM_MIPS) {
+      arch_ = ARCH_MIPS;
+      interface.reset(new ElfInterface32(memory));
     } else {
       // Unsupported.
-      ALOGI("32 bit elf that is neither arm nor x86: e_machine = %d\n", e_machine);
+      ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
       return nullptr;
     }
   } else if (class_type_ == ELFCLASS64) {
@@ -205,9 +201,12 @@
       arch_ = ARCH_ARM64;
     } else if (e_machine == EM_X86_64) {
       arch_ = ARCH_X86_64;
+    } else if (e_machine == EM_MIPS) {
+      arch_ = ARCH_MIPS64;
     } else {
       // Unsupported.
-      ALOGI("64 bit elf that is neither aarch64 nor x86_64: e_machine = %d\n", e_machine);
+      ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
+            e_machine);
       return nullptr;
     }
     interface.reset(new ElfInterface64(memory));
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 334cf76..df1642e 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -386,16 +386,29 @@
   return false;
 }
 
-bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+                        bool* finished) {
+  // Adjust the load bias to get the real relative pc.
+  if (pc < load_bias) {
+    return false;
+  }
+  uint64_t adjusted_pc = pc - load_bias;
+
   // Try the eh_frame first.
   DwarfSection* eh_frame = eh_frame_.get();
-  if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
+  if (eh_frame != nullptr && eh_frame->Step(adjusted_pc, regs, process_memory, finished)) {
     return true;
   }
 
   // Try the debug_frame next.
   DwarfSection* debug_frame = debug_frame_.get();
-  if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
+  if (debug_frame != nullptr && debug_frame->Step(adjusted_pc, regs, process_memory, finished)) {
+    return true;
+  }
+
+  // Finally try the gnu_debugdata interface, but always use a zero load bias.
+  if (gnu_debugdata_interface_ != nullptr &&
+      gnu_debugdata_interface_->Step(pc, 0, regs, process_memory, finished)) {
     return true;
   }
   return false;
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 9841e24..5d99bd7 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -92,16 +92,24 @@
   return true;
 }
 
-bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+bool ElfInterfaceArm::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+                           bool* finished) {
   // Dwarf unwind information is precise about whether a pc is covered or not,
   // but arm unwind information only has ranges of pc. In order to avoid
   // incorrectly doing a bad unwind using arm unwind information for a
   // different function, always try and unwind with the dwarf information first.
-  return ElfInterface32::Step(pc, regs, process_memory, finished) ||
-         StepExidx(pc, regs, process_memory, finished);
+  return ElfInterface32::Step(pc, load_bias, regs, process_memory, finished) ||
+         StepExidx(pc, load_bias, regs, process_memory, finished);
 }
 
-bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+                                bool* finished) {
+  // Adjust the load bias to get the real relative pc.
+  if (pc < load_bias) {
+    return false;
+  }
+  pc -= load_bias;
+
   RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
   uint64_t entry_offset;
   if (!FindEntry(pc, &entry_offset)) {
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index eeb2e17..9c067ba 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,9 +70,11 @@
 
   bool HandleType(uint64_t offset, uint32_t type, uint64_t load_bias) override;
 
-  bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
+  bool Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+            bool* finished) override;
 
-  bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
+  bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+                 bool* finished);
 
   uint64_t start_offset() { return start_offset_; }
 
diff --git a/libunwindstack/MachineMips.h b/libunwindstack/MachineMips.h
new file mode 100644
index 0000000..2dfb1e9
--- /dev/null
+++ b/libunwindstack/MachineMips.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_MIPS_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum MipsReg : uint16_t {
+  MIPS_REG_R0 = 0,
+  MIPS_REG_R1,
+  MIPS_REG_R2,
+  MIPS_REG_R3,
+  MIPS_REG_R4,
+  MIPS_REG_R5,
+  MIPS_REG_R6,
+  MIPS_REG_R7,
+  MIPS_REG_R8,
+  MIPS_REG_R9,
+  MIPS_REG_R10,
+  MIPS_REG_R11,
+  MIPS_REG_R12,
+  MIPS_REG_R13,
+  MIPS_REG_R14,
+  MIPS_REG_R15,
+  MIPS_REG_R16,
+  MIPS_REG_R17,
+  MIPS_REG_R18,
+  MIPS_REG_R19,
+  MIPS_REG_R20,
+  MIPS_REG_R21,
+  MIPS_REG_R22,
+  MIPS_REG_R23,
+  MIPS_REG_R24,
+  MIPS_REG_R25,
+  MIPS_REG_R26,
+  MIPS_REG_R27,
+  MIPS_REG_R28,
+  MIPS_REG_R29,
+  MIPS_REG_R30,
+  MIPS_REG_R31,
+  MIPS_REG_PC,
+  MIPS_REG_LAST,
+
+  MIPS_REG_SP = MIPS_REG_R29,
+  MIPS_REG_RA = MIPS_REG_R31,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_MIPS_H
\ No newline at end of file
diff --git a/libunwindstack/MachineMips64.h b/libunwindstack/MachineMips64.h
new file mode 100644
index 0000000..34addf2
--- /dev/null
+++ b/libunwindstack/MachineMips64.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_MACHINE_MIPS64_H
+#define _LIBUNWINDSTACK_MACHINE_MIPS64_H
+
+#include <stdint.h>
+
+namespace unwindstack {
+
+enum Mips64Reg : uint16_t {
+  MIPS64_REG_R0 = 0,
+  MIPS64_REG_R1,
+  MIPS64_REG_R2,
+  MIPS64_REG_R3,
+  MIPS64_REG_R4,
+  MIPS64_REG_R5,
+  MIPS64_REG_R6,
+  MIPS64_REG_R7,
+  MIPS64_REG_R8,
+  MIPS64_REG_R9,
+  MIPS64_REG_R10,
+  MIPS64_REG_R11,
+  MIPS64_REG_R12,
+  MIPS64_REG_R13,
+  MIPS64_REG_R14,
+  MIPS64_REG_R15,
+  MIPS64_REG_R16,
+  MIPS64_REG_R17,
+  MIPS64_REG_R18,
+  MIPS64_REG_R19,
+  MIPS64_REG_R20,
+  MIPS64_REG_R21,
+  MIPS64_REG_R22,
+  MIPS64_REG_R23,
+  MIPS64_REG_R24,
+  MIPS64_REG_R25,
+  MIPS64_REG_R26,
+  MIPS64_REG_R27,
+  MIPS64_REG_R28,
+  MIPS64_REG_R29,
+  MIPS64_REG_R30,
+  MIPS64_REG_R31,
+  MIPS64_REG_PC,
+  MIPS64_REG_LAST,
+
+  MIPS64_REG_SP = MIPS64_REG_R29,
+  MIPS64_REG_RA = MIPS64_REG_R31,
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_MACHINE_MIPS64_H
\ No newline at end of file
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 51bce8e..89fe038 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -122,13 +122,21 @@
 }
 
 uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+  uint64_t cur_load_bias = load_bias.load();
+  if (cur_load_bias != static_cast<uint64_t>(-1)) {
+    return cur_load_bias;
+  }
+
   {
     // Make sure no other thread is trying to add the elf to this map.
     std::lock_guard<std::mutex> guard(mutex_);
     if (elf != nullptr) {
       if (elf->valid()) {
-        return elf->GetLoadBias();
+        cur_load_bias = elf->GetLoadBias();
+        load_bias = cur_load_bias;
+        return cur_load_bias;
       } else {
+        load_bias = 0;
         return 0;
       }
     }
@@ -137,7 +145,9 @@
   // Call lightweight static function that will only read enough of the
   // elf data to get the load bias.
   std::unique_ptr<Memory> memory(CreateMemory(process_memory));
-  return Elf::GetLoadBias(memory.get());
+  cur_load_bias = Elf::GetLoadBias(memory.get());
+  load_bias = cur_load_bias;
+  return cur_load_bias;
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 56370c1..4c16212 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -202,6 +202,13 @@
   return return_value;
 }
 
+void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+               const std::string& name, uint64_t load_bias) {
+  MapInfo* map_info = new MapInfo(start, end, offset, flags, name);
+  map_info->load_bias = load_bias;
+  maps_.push_back(map_info);
+}
+
 Maps::~Maps() {
   for (auto& map : maps_) {
     delete map;
@@ -235,61 +242,4 @@
   return "/proc/" + std::to_string(pid_) + "/maps";
 }
 
-bool OfflineMaps::Parse() {
-  // Format of maps information:
-  //   <uint64_t> StartOffset
-  //   <uint64_t> EndOffset
-  //   <uint64_t> offset
-  //   <uint16_t> flags
-  //   <uint16_t> MapNameLength
-  //   <VariableLengthValue> MapName
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_.c_str(), O_RDONLY)));
-  if (fd == -1) {
-    return false;
-  }
-
-  std::vector<char> name;
-  while (true) {
-    uint64_t start;
-    ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &start, sizeof(start)));
-    if (bytes == 0) {
-      break;
-    }
-    if (bytes == -1 || bytes != sizeof(start)) {
-      return false;
-    }
-    uint64_t end;
-    bytes = TEMP_FAILURE_RETRY(read(fd, &end, sizeof(end)));
-    if (bytes == -1 || bytes != sizeof(end)) {
-      return false;
-    }
-    uint64_t offset;
-    bytes = TEMP_FAILURE_RETRY(read(fd, &offset, sizeof(offset)));
-    if (bytes == -1 || bytes != sizeof(offset)) {
-      return false;
-    }
-    uint16_t flags;
-    bytes = TEMP_FAILURE_RETRY(read(fd, &flags, sizeof(flags)));
-    if (bytes == -1 || bytes != sizeof(flags)) {
-      return false;
-    }
-    uint16_t len;
-    bytes = TEMP_FAILURE_RETRY(read(fd, &len, sizeof(len)));
-    if (bytes == -1 || bytes != sizeof(len)) {
-      return false;
-    }
-    if (len > 0) {
-      name.resize(len);
-      bytes = TEMP_FAILURE_RETRY(read(fd, name.data(), len));
-      if (bytes == -1 || bytes != len) {
-        return false;
-      }
-      maps_.push_back(new MapInfo(start, end, offset, flags, std::string(name.data(), len)));
-    } else {
-      maps_.push_back(new MapInfo(start, end, offset, flags, ""));
-    }
-  }
-  return true;
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1f3c6c3..285f879 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -35,10 +35,6 @@
 namespace unwindstack {
 
 static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
-  struct iovec dst_iov = {
-      .iov_base = dst,
-      .iov_len = len,
-  };
 
   // Split up the remote read across page boundaries.
   // From the manpage:
@@ -49,39 +45,49 @@
   //   perform a partial transfer that splits a single iovec element.
   constexpr size_t kMaxIovecs = 64;
   struct iovec src_iovs[kMaxIovecs];
-  size_t iovecs_used = 0;
 
   uint64_t cur = remote_src;
+  size_t total_read = 0;
   while (len > 0) {
-    if (iovecs_used == kMaxIovecs) {
-      errno = EINVAL;
-      return 0;
+    struct iovec dst_iov = {
+        .iov_base = &reinterpret_cast<uint8_t*>(dst)[total_read], .iov_len = len,
+    };
+
+    size_t iovecs_used = 0;
+    while (len > 0) {
+      if (iovecs_used == kMaxIovecs) {
+        break;
+      }
+
+      // struct iovec uses void* for iov_base.
+      if (cur >= UINTPTR_MAX) {
+        errno = EFAULT;
+        return total_read;
+      }
+
+      src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
+
+      uintptr_t misalignment = cur & (getpagesize() - 1);
+      size_t iov_len = getpagesize() - misalignment;
+      iov_len = std::min(iov_len, len);
+
+      len -= iov_len;
+      if (__builtin_add_overflow(cur, iov_len, &cur)) {
+        errno = EFAULT;
+        return total_read;
+      }
+
+      src_iovs[iovecs_used].iov_len = iov_len;
+      ++iovecs_used;
     }
 
-    // struct iovec uses void* for iov_base.
-    if (cur >= UINTPTR_MAX) {
-      errno = EFAULT;
-      return 0;
+    ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
+    if (rc == -1) {
+      return total_read;
     }
-
-    src_iovs[iovecs_used].iov_base = reinterpret_cast<void*>(cur);
-
-    uintptr_t misalignment = cur & (getpagesize() - 1);
-    size_t iov_len = getpagesize() - misalignment;
-    iov_len = std::min(iov_len, len);
-
-    len -= iov_len;
-    if (__builtin_add_overflow(cur, iov_len, &cur)) {
-      errno = EFAULT;
-      return 0;
-    }
-
-    src_iovs[iovecs_used].iov_len = iov_len;
-    ++iovecs_used;
+    total_read += rc;
   }
-
-  ssize_t rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0);
-  return rc == -1 ? 0 : rc;
+  return total_read;
 }
 
 static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) {
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 29dbf9d..7feafad 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -27,16 +27,20 @@
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
 
 #include "UserArm.h"
 #include "UserArm64.h"
 #include "UserX86.h"
 #include "UserX86_64.h"
+#include "UserMips.h"
+#include "UserMips64.h"
 
 namespace unwindstack {
 
 // The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(arm64_user_regs) + 10;
+constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
 
 // This function assumes that reg_data is already aligned to a 64 bit value.
 // If not this could crash with an unaligned access.
@@ -60,6 +64,10 @@
     return RegsArm::Read(buffer.data());
   case sizeof(arm64_user_regs):
     return RegsArm64::Read(buffer.data());
+  case sizeof(mips_user_regs):
+    return RegsMips::Read(buffer.data());
+  case sizeof(mips64_user_regs):
+    return RegsMips64::Read(buffer.data());
   }
   return nullptr;
 }
@@ -74,6 +82,10 @@
       return RegsArm::CreateFromUcontext(ucontext);
     case ARCH_ARM64:
       return RegsArm64::CreateFromUcontext(ucontext);
+    case ARCH_MIPS:
+      return RegsMips::CreateFromUcontext(ucontext);
+    case ARCH_MIPS64:
+      return RegsMips64::CreateFromUcontext(ucontext);
     case ARCH_UNKNOWN:
     default:
       return nullptr;
@@ -89,6 +101,10 @@
   return ARCH_X86;
 #elif defined(__x86_64__)
   return ARCH_X86_64;
+#elif defined(__mips__) && !defined(__LP64__)
+  return ARCH_MIPS;
+#elif defined(__mips__) && defined(__LP64__)
+  return ARCH_MIPS64;
 #else
   abort();
 #endif
@@ -104,6 +120,10 @@
   regs = new RegsX86();
 #elif defined(__x86_64__)
   regs = new RegsX86_64();
+#elif defined(__mips__) && !defined(__LP64__)
+  regs = new RegsMips();
+#elif defined(__mips__) && defined(__LP64__)
+  regs = new RegsMips64();
 #else
   abort();
 #endif
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
new file mode 100644
index 0000000..44cde05
--- /dev/null
+++ b/libunwindstack/RegsMips.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips.h>
+
+#include "MachineMips.h"
+#include "UcontextMips.h"
+#include "UserMips.h"
+
+namespace unwindstack {
+
+RegsMips::RegsMips()
+    : RegsImpl<uint32_t>(MIPS_REG_LAST, MIPS_REG_SP, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
+
+ArchEnum RegsMips::Arch() {
+  return ARCH_MIPS;
+}
+
+uint64_t RegsMips::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return rel_pc;
+  }
+
+  // For now, just assuming no compact branches
+  if (rel_pc < 8) {
+    return rel_pc;
+  }
+  return rel_pc - 8;
+}
+
+void RegsMips::SetFromRaw() {
+  set_pc(regs_[MIPS_REG_PC]);
+  set_sp(regs_[MIPS_REG_SP]);
+}
+
+bool RegsMips::SetPcFromReturnAddress(Memory*) {
+  if (pc() == regs_[MIPS_REG_RA]) {
+    return false;
+  }
+
+  set_pc(regs_[MIPS_REG_RA]);
+  return true;
+}
+
+void RegsMips::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[MIPS_REG_R0]);
+  fn("r1", regs_[MIPS_REG_R1]);
+  fn("r2", regs_[MIPS_REG_R2]);
+  fn("r3", regs_[MIPS_REG_R3]);
+  fn("r4", regs_[MIPS_REG_R4]);
+  fn("r5", regs_[MIPS_REG_R5]);
+  fn("r6", regs_[MIPS_REG_R6]);
+  fn("r7", regs_[MIPS_REG_R7]);
+  fn("r8", regs_[MIPS_REG_R8]);
+  fn("r9", regs_[MIPS_REG_R9]);
+  fn("r10", regs_[MIPS_REG_R10]);
+  fn("r11", regs_[MIPS_REG_R11]);
+  fn("r12", regs_[MIPS_REG_R12]);
+  fn("r13", regs_[MIPS_REG_R13]);
+  fn("r14", regs_[MIPS_REG_R14]);
+  fn("r15", regs_[MIPS_REG_R15]);
+  fn("r16", regs_[MIPS_REG_R16]);
+  fn("r17", regs_[MIPS_REG_R17]);
+  fn("r18", regs_[MIPS_REG_R18]);
+  fn("r19", regs_[MIPS_REG_R19]);
+  fn("r20", regs_[MIPS_REG_R20]);
+  fn("r21", regs_[MIPS_REG_R21]);
+  fn("r22", regs_[MIPS_REG_R22]);
+  fn("r23", regs_[MIPS_REG_R23]);
+  fn("r24", regs_[MIPS_REG_R24]);
+  fn("r25", regs_[MIPS_REG_R25]);
+  fn("r26", regs_[MIPS_REG_R26]);
+  fn("r27", regs_[MIPS_REG_R27]);
+  fn("r28", regs_[MIPS_REG_R28]);
+  fn("sp", regs_[MIPS_REG_SP]);
+  fn("r30", regs_[MIPS_REG_R30]);
+  fn("ra", regs_[MIPS_REG_RA]);
+  fn("pc", regs_[MIPS_REG_PC]);
+}
+
+Regs* RegsMips::Read(void* remote_data) {
+  mips_user_regs* user = reinterpret_cast<mips_user_regs*>(remote_data);
+  RegsMips* regs = new RegsMips();
+  uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
+
+  memcpy(regs->RawData(), &user->regs[MIPS32_EF_R0], (MIPS_REG_R31 + 1) * sizeof(uint32_t));
+
+  reg_data[MIPS_REG_PC] = user->regs[MIPS32_EF_CP0_EPC];
+  regs->SetFromRaw();
+  return regs;
+}
+
+Regs* RegsMips::CreateFromUcontext(void* ucontext) {
+  mips_ucontext_t* mips_ucontext = reinterpret_cast<mips_ucontext_t*>(ucontext);
+
+  RegsMips* regs = new RegsMips();
+  // Copy 64 bit sc_regs over to 32 bit regs
+  for (int i = 0; i < 32; i++) {
+      (*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i];
+  }
+  (*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc;
+  regs->SetFromRaw();
+  return regs;
+}
+
+bool RegsMips::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  uint64_t offset = 0;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  // Look for the kernel sigreturn functions.
+  // __vdso_rt_sigreturn:
+  // 0x24021061     li  v0, 0x1061
+  // 0x0000000c     syscall
+  // __vdso_sigreturn:
+  // 0x24021017     li  v0, 0x1017
+  // 0x0000000c     syscall
+  if (data == 0x0000000c24021061ULL) {
+    // vdso_rt_sigreturn => read rt_sigframe
+    // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+    offset = 24 + 128 + 24 + 8;
+  } else if (data == 0x0000000c24021017LL) {
+    // vdso_sigreturn => read sigframe
+    // offset = sigcontext offset + sc_pc offset
+    offset = 24 + 8;
+  } else {
+    return false;
+  }
+
+  // read sc_pc and sc_regs[32] from stack
+  uint64_t values[MIPS_REG_LAST];
+  if (!process_memory->Read(sp() + offset, values, sizeof(values))) {
+    return false;
+  }
+
+  // Copy 64 bit sc_pc over to 32 bit regs_[MIPS_REG_PC]
+  regs_[MIPS_REG_PC] = values[0];
+
+  // Copy 64 bit sc_regs over to 32 bit regs
+  for (int i = 0; i < 32; i++) {
+      regs_[MIPS_REG_R0 + i] = values[1 + i];
+  }
+
+  SetFromRaw();
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
new file mode 100644
index 0000000..b4e5246
--- /dev/null
+++ b/libunwindstack/RegsMips64.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/RegsMips64.h>
+
+#include "MachineMips64.h"
+#include "UcontextMips64.h"
+#include "UserMips64.h"
+
+namespace unwindstack {
+
+RegsMips64::RegsMips64()
+    : RegsImpl<uint64_t>(MIPS64_REG_LAST, MIPS64_REG_SP,
+                         Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
+
+ArchEnum RegsMips64::Arch() {
+  return ARCH_MIPS64;
+}
+
+uint64_t RegsMips64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+  if (!elf->valid()) {
+    return rel_pc;
+  }
+
+  // For now, just assuming no compact branches
+  if (rel_pc < 8) {
+    return rel_pc;
+  }
+  return rel_pc - 8;
+}
+
+void RegsMips64::SetFromRaw() {
+  set_pc(regs_[MIPS64_REG_PC]);
+  set_sp(regs_[MIPS64_REG_SP]);
+}
+
+bool RegsMips64::SetPcFromReturnAddress(Memory*) {
+  if (pc() == regs_[MIPS64_REG_RA]) {
+    return false;
+  }
+
+  set_pc(regs_[MIPS64_REG_RA]);
+  return true;
+}
+
+void RegsMips64::IterateRegisters(std::function<void(const char*, uint64_t)> fn) {
+  fn("r0", regs_[MIPS64_REG_R0]);
+  fn("r1", regs_[MIPS64_REG_R1]);
+  fn("r2", regs_[MIPS64_REG_R2]);
+  fn("r3", regs_[MIPS64_REG_R3]);
+  fn("r4", regs_[MIPS64_REG_R4]);
+  fn("r5", regs_[MIPS64_REG_R5]);
+  fn("r6", regs_[MIPS64_REG_R6]);
+  fn("r7", regs_[MIPS64_REG_R7]);
+  fn("r8", regs_[MIPS64_REG_R8]);
+  fn("r9", regs_[MIPS64_REG_R9]);
+  fn("r10", regs_[MIPS64_REG_R10]);
+  fn("r11", regs_[MIPS64_REG_R11]);
+  fn("r12", regs_[MIPS64_REG_R12]);
+  fn("r13", regs_[MIPS64_REG_R13]);
+  fn("r14", regs_[MIPS64_REG_R14]);
+  fn("r15", regs_[MIPS64_REG_R15]);
+  fn("r16", regs_[MIPS64_REG_R16]);
+  fn("r17", regs_[MIPS64_REG_R17]);
+  fn("r18", regs_[MIPS64_REG_R18]);
+  fn("r19", regs_[MIPS64_REG_R19]);
+  fn("r20", regs_[MIPS64_REG_R20]);
+  fn("r21", regs_[MIPS64_REG_R21]);
+  fn("r22", regs_[MIPS64_REG_R22]);
+  fn("r23", regs_[MIPS64_REG_R23]);
+  fn("r24", regs_[MIPS64_REG_R24]);
+  fn("r25", regs_[MIPS64_REG_R25]);
+  fn("r26", regs_[MIPS64_REG_R26]);
+  fn("r27", regs_[MIPS64_REG_R27]);
+  fn("r28", regs_[MIPS64_REG_R28]);
+  fn("sp", regs_[MIPS64_REG_SP]);
+  fn("r30", regs_[MIPS64_REG_R30]);
+  fn("ra", regs_[MIPS64_REG_RA]);
+  fn("pc", regs_[MIPS64_REG_PC]);
+}
+
+Regs* RegsMips64::Read(void* remote_data) {
+  mips64_user_regs* user = reinterpret_cast<mips64_user_regs*>(remote_data);
+  RegsMips64* regs = new RegsMips64();
+  uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+
+  memcpy(regs->RawData(), &user->regs[MIPS64_EF_R0], (MIPS64_REG_R31 + 1) * sizeof(uint64_t));
+
+  reg_data[MIPS64_REG_PC] = user->regs[MIPS64_EF_CP0_EPC];
+  regs->SetFromRaw();
+  return regs;
+}
+
+Regs* RegsMips64::CreateFromUcontext(void* ucontext) {
+  mips64_ucontext_t* mips64_ucontext = reinterpret_cast<mips64_ucontext_t*>(ucontext);
+
+  RegsMips64* regs = new RegsMips64();
+  // Copy 64 bit sc_regs over to 64 bit regs
+  memcpy(regs->RawData(), &mips64_ucontext->uc_mcontext.sc_regs[0], 32 * sizeof(uint64_t));
+  (*regs)[MIPS64_REG_PC] = mips64_ucontext->uc_mcontext.sc_pc;
+  regs->SetFromRaw();
+  return regs;
+}
+
+bool RegsMips64::StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) {
+  uint64_t data;
+  Memory* elf_memory = elf->memory();
+  // Read from elf memory since it is usually more expensive to read from
+  // process memory.
+  if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+    return false;
+  }
+
+  // Look for the kernel sigreturn function.
+  // __vdso_rt_sigreturn:
+  // 0x2402145b     li  v0, 0x145b
+  // 0x0000000c     syscall
+  if (data != 0x0000000c2402145bULL) {
+    return false;
+  }
+
+  // vdso_rt_sigreturn => read rt_sigframe
+  // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset
+  // read 64 bit sc_regs[32] from stack into 64 bit regs_
+  if (!process_memory->Read(sp() + 24 + 128 + 40, regs_.data(),
+                            sizeof(uint64_t) * (MIPS64_REG_LAST - 1))) {
+    return false;
+  }
+
+  // offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
+  // read 64 bit sc_pc from stack into 64 bit regs_[MIPS64_REG_PC]
+  if (!process_memory->Read(sp() + 24 + 128 + 40 + 576, &regs_[MIPS64_REG_PC],
+                            sizeof(uint64_t))) {
+    return false;
+  }
+
+  SetFromRaw();
+  return true;
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/UcontextMips.h b/libunwindstack/UcontextMips.h
new file mode 100644
index 0000000..27185e7
--- /dev/null
+++ b/libunwindstack/UcontextMips.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+
+#include <stdint.h>
+
+#include "MachineMips.h"
+
+namespace unwindstack {
+
+struct mips_stack_t {
+  uint32_t ss_sp;    // void __user*
+  uint32_t ss_size;  // size_t
+  int32_t ss_flags;  // int
+};
+
+struct mips_mcontext_t {
+  uint32_t sc_regmask;
+  uint32_t sc_status;
+  uint64_t sc_pc;
+  uint64_t sc_regs[32];
+  // Nothing else is used, so don't define it.
+};
+
+struct mips_ucontext_t {
+  uint32_t uc_flags;  // unsigned long
+  uint32_t uc_link;   // struct ucontext*
+  mips_stack_t uc_stack;
+  mips_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS_H
diff --git a/libunwindstack/UcontextMips64.h b/libunwindstack/UcontextMips64.h
new file mode 100644
index 0000000..623bf3a
--- /dev/null
+++ b/libunwindstack/UcontextMips64.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+
+#include <stdint.h>
+
+#include "MachineMips64.h"
+
+namespace unwindstack {
+
+struct mips64_stack_t {
+  uint64_t ss_sp;    // void __user*
+  uint64_t ss_size;  // size_t
+  int32_t ss_flags;  // int
+};
+
+struct mips64_mcontext_t {
+  uint64_t sc_regs[32];
+  uint64_t sc_fpregs[32];
+  uint64_t sc_mdhi;
+  uint64_t sc_hi1;
+  uint64_t sc_hi2;
+  uint64_t sc_hi3;
+  uint64_t sc_mdlo;
+  uint64_t sc_lo1;
+  uint64_t sc_lo2;
+  uint64_t sc_lo3;
+  uint64_t sc_pc;
+  // Nothing else is used, so don't define it.
+};
+
+struct mips64_ucontext_t {
+  uint64_t uc_flags;  // unsigned long
+  uint64_t uc_link;   // struct ucontext*
+  mips64_stack_t uc_stack;
+  mips64_mcontext_t uc_mcontext;
+  // Nothing else is used, so don't define it.
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_UCONTEXT_MIPS6464_H
diff --git a/libunwindstack/UcontextX86_64.h b/libunwindstack/UcontextX86_64.h
index d689796..2b8bdc4 100644
--- a/libunwindstack/UcontextX86_64.h
+++ b/libunwindstack/UcontextX86_64.h
@@ -38,6 +38,7 @@
 struct x86_64_stack_t {
   uint64_t ss_sp;    // void __user*
   int32_t ss_flags;  // int
+  int32_t pad;
   uint64_t ss_size;  // size_t
 };
 
diff --git a/libunwindstack/UserMips.h b/libunwindstack/UserMips.h
new file mode 100644
index 0000000..184be4f
--- /dev/null
+++ b/libunwindstack/UserMips.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS_H
+#define _LIBUNWINDSTACK_USER_MIPS_H
+
+namespace unwindstack {
+
+enum Mips32UserReg : uint16_t {
+  MIPS32_EF_R0 = 6,
+  MIPS32_EF_CP0_EPC = 40,
+};
+
+struct mips_user_regs {
+  uint32_t regs[45];
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_MIPS_H
diff --git a/libunwindstack/UserMips64.h b/libunwindstack/UserMips64.h
new file mode 100644
index 0000000..c46befd
--- /dev/null
+++ b/libunwindstack/UserMips64.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LIBUNWINDSTACK_USER_MIPS64_H
+#define _LIBUNWINDSTACK_USER_MIPS64_H
+
+namespace unwindstack {
+
+enum Mips64UserReg : uint16_t {
+  MIPS64_EF_R0 = 0,
+  MIPS64_EF_CP0_EPC = 34,
+};
+
+struct mips64_user_regs {
+  uint64_t regs[45];
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_USER_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index d27727b..a85e5f4 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -42,6 +42,8 @@
   ARCH_ARM64,
   ARCH_X86,
   ARCH_X86_64,
+  ARCH_MIPS,
+  ARCH_MIPS64,
 };
 
 class Elf {
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5cfe74d..5d3cd5e 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -60,7 +60,8 @@
   virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
                                uint64_t* offset) = 0;
 
-  virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
+  virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
+                    bool* finished);
 
   Memory* CreateGnuDebugdataMemory();
 
@@ -68,6 +69,8 @@
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads() { return pt_loads_; }
 
+  void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
+
   uint64_t dynamic_offset() { return dynamic_offset_; }
   uint64_t dynamic_size() { return dynamic_size_; }
   uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
@@ -134,6 +137,8 @@
 
   std::unique_ptr<DwarfSection> eh_frame_;
   std::unique_ptr<DwarfSection> debug_frame_;
+  // The Elf object owns the gnu_debugdata interface object.
+  ElfInterface* gnu_debugdata_interface_ = nullptr;
 
   std::vector<Symbols*> symbols_;
 };
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 6f8ceca..22e48f7 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <atomic>
 #include <mutex>
 #include <string>
 
@@ -33,7 +34,12 @@
   MapInfo() = default;
   MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
   MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
-      : start(start), end(end), offset(offset), flags(flags), name(name) {}
+      : start(start),
+        end(end),
+        offset(offset),
+        flags(flags),
+        name(name),
+        load_bias(static_cast<uint64_t>(-1)) {}
   ~MapInfo() { delete elf; }
 
   uint64_t start = 0;
@@ -48,6 +54,8 @@
   // instead of a portion of the file.
   uint64_t elf_offset = 0;
 
+  std::atomic_uint64_t load_bias;
+
   // This function guarantees it will never return nullptr.
   Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
 
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 34fef7f..17a2d28 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -42,6 +42,9 @@
 
   virtual const std::string GetMapsFile() const { return ""; }
 
+  void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
+           uint64_t load_bias);
+
   typedef std::vector<MapInfo*>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
@@ -100,14 +103,6 @@
   const std::string file_;
 };
 
-class OfflineMaps : public FileMaps {
- public:
-  OfflineMaps(const std::string& file) : FileMaps(file) {}
-  virtual ~OfflineMaps() = default;
-
-  bool Parse() override;
-};
-
 }  // namespace unwindstack
 
 #endif  // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index c59e081..557eace 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -87,7 +87,7 @@
   regs->SetFromRaw();
 }
 
-#elif defined(__i386__) || defined(__x86_64__)
+#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
 
 extern "C" void AsmGetRegs(void* regs);
 
@@ -97,11 +97,6 @@
   regs->SetFromRaw();
 }
 
-#elif defined(__mips__)
-
-// Stub to allow mips to build.
-void RegsGetLocal(Regs*) {}
-
 #endif
 
 }  // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
new file mode 100644
index 0000000..3fe6a9f
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_REGS_MIPS_H
+#define _LIBUNWINDSTACK_REGS_MIPS_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips : public RegsImpl<uint32_t> {
+ public:
+  RegsMips();
+  virtual ~RegsMips() = default;
+
+  virtual ArchEnum Arch() override final;
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_MIPS_H
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
new file mode 100644
index 0000000..6b4bcdf
--- /dev/null
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_REGS_MIPS64_H
+#define _LIBUNWINDSTACK_REGS_MIPS64_H
+
+#include <stdint.h>
+
+#include <functional>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Memory;
+
+class RegsMips64 : public RegsImpl<uint64_t> {
+ public:
+  RegsMips64();
+  virtual ~RegsMips64() = default;
+
+  virtual ArchEnum Arch() override final;
+
+  uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+  void SetFromRaw() override;
+
+  bool SetPcFromReturnAddress(Memory* process_memory) override;
+
+  bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
+
+  virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+  static Regs* Read(void* data);
+
+  static Regs* CreateFromUcontext(void* ucontext);
+};
+
+}  // namespace unwindstack
+
+#endif  // _LIBUNWINDSTACK_REGS_MIPS64_H
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index b94a8a4..68de797 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -43,7 +43,7 @@
   return true;
 }
 
-bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
   if (steps_.empty()) {
     return false;
   }
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 565b13f..abf9927 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,8 +68,7 @@
 
   bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
 
-  bool Step(uint64_t, Regs*, Memory*, bool*) override;
-
+  bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
 
   static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
   static void FakePushStepData(const StepData data) { steps_.push_back(data); }
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 5f7cf60..e6763ab 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -302,7 +302,7 @@
 
   // FindEntry fails.
   bool finished;
-  ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, 0, nullptr, nullptr, &finished));
 
   // ExtractEntry should fail.
   interface.FakeSetStartOffset(0x1000);
@@ -315,20 +315,26 @@
   regs[ARM_REG_LR] = 0x20000;
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
-  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
 
   // Eval should fail.
   memory_.SetData32(0x1004, 0x81000000);
-  ASSERT_FALSE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_FALSE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
 
   // Everything should pass.
   memory_.SetData32(0x1004, 0x80b0b0b0);
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x1000U, regs.sp());
   ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
   ASSERT_EQ(0x20000U, regs.pc());
   ASSERT_EQ(0x20000U, regs[ARM_REG_PC]);
+
+  // Load bias is non-zero.
+  ASSERT_TRUE(interface.StepExidx(0x8000, 0x1000, &regs, &process_memory_, &finished));
+
+  // Pc too small.
+  ASSERT_FALSE(interface.StepExidx(0x8000, 0x9000, &regs, &process_memory_, &finished));
 }
 
 TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
@@ -349,7 +355,7 @@
 
   // Everything should pass.
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
   ASSERT_FALSE(finished);
   ASSERT_EQ(0x10004U, regs.sp());
   ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
@@ -372,7 +378,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
   ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
@@ -394,7 +400,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
   ASSERT_TRUE(finished);
   ASSERT_EQ(0x10000U, regs.sp());
   ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
@@ -420,7 +426,7 @@
   regs.set_pc(0x1234);
 
   bool finished;
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
 
@@ -432,7 +438,7 @@
   regs.set_sp(regs[ARM_REG_SP]);
   regs.set_pc(0x1234);
 
-  ASSERT_TRUE(interface.StepExidx(0x7000, &regs, &process_memory_, &finished));
+  ASSERT_TRUE(interface.StepExidx(0x7000, 0, &regs, &process_memory_, &finished));
   ASSERT_TRUE(finished);
   ASSERT_EQ(0U, regs.pc());
 }
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 00192f1..5e808ef 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -145,7 +145,7 @@
   ASSERT_FALSE(elf.Init(false));
 
   ASSERT_EQ("", GetFakeLogBuf());
-  ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86: e_machine = 20\n\n",
+  ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
             GetFakeLogPrint());
 }
 
@@ -158,7 +158,7 @@
   ASSERT_FALSE(elf.Init(false));
 
   ASSERT_EQ("", GetFakeLogBuf());
-  ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64: e_machine = 21\n\n",
+  ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
             GetFakeLogPrint());
 }
 
@@ -174,6 +174,18 @@
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
+TEST_F(ElfTest, elf_mips) {
+  Elf elf(memory_);
+
+  InitElf32(EM_MIPS);
+
+  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.valid());
+  ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+  ASSERT_EQ(ELFCLASS32, elf.class_type());
+  ASSERT_TRUE(elf.interface() != nullptr);
+}
+
 TEST_F(ElfTest, elf_x86) {
   Elf elf(memory_);
 
@@ -210,6 +222,18 @@
   ASSERT_TRUE(elf.interface() != nullptr);
 }
 
+TEST_F(ElfTest, elf_mips64) {
+  Elf elf(memory_);
+
+  InitElf64(EM_MIPS);
+
+  ASSERT_TRUE(elf.Init(false));
+  ASSERT_TRUE(elf.valid());
+  ASSERT_EQ(static_cast<uint32_t>(EM_MIPS), elf.machine_type());
+  ASSERT_EQ(ELFCLASS64, elf.class_type());
+  ASSERT_TRUE(elf.interface() != nullptr);
+}
+
 TEST_F(ElfTest, gnu_debugdata_init_fail32) {
   TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
                                                [&](uint64_t offset, const void* ptr, size_t size) {
@@ -322,7 +346,7 @@
   void InitHeaders() override {}
   bool GetSoname(std::string*) override { return false; }
   bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
-  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
 };
 
 TEST_F(ElfTest, step_in_interface) {
@@ -337,7 +361,7 @@
   MemoryFake process_memory;
 
   bool finished;
-  EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
+  EXPECT_CALL(*interface, Step(0x1000, 0, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
   ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
@@ -358,7 +382,7 @@
   bool finished;
   ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
 
-  EXPECT_CALL(*interface, Step(0x3300, &regs, &process_memory, &finished))
+  EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
   ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &process_memory, &finished));
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 44a73a8..631036b 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -68,12 +68,23 @@
   EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
 }
 
+TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
+  map_info_->elf = elf_container_.release();
+
+  elf_->FakeSetLoadBias(0);
+  EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+
+  elf_->FakeSetLoadBias(0x1000);
+  EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
+}
+
 TEST_F(MapInfoGetLoadBiasTest, elf_exists) {
   map_info_->elf = elf_container_.release();
 
   elf_->FakeSetLoadBias(0);
   EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
 
+  map_info_->load_bias = static_cast<uint64_t>(-1);
   elf_->FakeSetLoadBias(0x1000);
   EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
 }
@@ -141,6 +152,15 @@
   EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
 }
 
+TEST_F(MapInfoGetLoadBiasTest, elf_exists_in_memory_cached) {
+  InitElfData(memory_, map_info_->start);
+
+  EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+
+  memory_->Clear();
+  EXPECT_EQ(0xe000U, map_info_->GetLoadBias(process_memory_));
+}
+
 TEST_F(MapInfoGetLoadBiasTest, multiple_thread_elf_exists_in_memory) {
   InitElfData(memory_, map_info_->start);
 
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 8dc884f..9622ba5 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -44,6 +44,24 @@
   }
 }
 
+TEST(MapsTest, map_add) {
+  Maps maps;
+
+  maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+  maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+  maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+  ASSERT_EQ(3U, maps.Total());
+  MapInfo* info = maps.Get(0);
+  ASSERT_EQ(0x1000U, info->start);
+  ASSERT_EQ(0x2000U, info->end);
+  ASSERT_EQ(0U, info->offset);
+  ASSERT_EQ(PROT_READ, info->flags);
+  ASSERT_EQ("fake_map", info->name);
+  ASSERT_EQ(0U, info->elf_offset);
+  ASSERT_EQ(0U, info->load_bias.load());
+}
+
 TEST(MapsTest, verify_parse_line) {
   MapInfo info;
 
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
new file mode 100644
index 0000000..14d58e6
--- /dev/null
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryOfflineTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    for (size_t i = 0; i < 1024; ++i) {
+      data.push_back(i & 0xff);
+    }
+
+    ASSERT_TRUE(android::base::WriteFully(temp_file.fd, &offset, sizeof(offset)));
+    ASSERT_TRUE(android::base::WriteFully(temp_file.fd, data.data(), data.size()));
+
+    memory = std::make_unique<MemoryOffline>();
+    ASSERT_TRUE(memory != nullptr);
+
+    ASSERT_TRUE(memory->Init(temp_file.path, 0));
+  }
+
+  TemporaryFile temp_file;
+  uint64_t offset = 4096;
+  std::vector<char> data;
+  std::unique_ptr<MemoryOffline> memory;
+};
+
+TEST_F(MemoryOfflineTest, read_boundaries) {
+  char buf = '\0';
+  ASSERT_EQ(0U, memory->Read(offset - 1, &buf, 1));
+  ASSERT_EQ(0U, memory->Read(offset + data.size(), &buf, 1));
+  ASSERT_EQ(1U, memory->Read(offset, &buf, 1));
+  ASSERT_EQ(buf, data.front());
+  ASSERT_EQ(1U, memory->Read(offset + data.size() - 1, &buf, 1));
+  ASSERT_EQ(buf, data.back());
+}
+
+TEST_F(MemoryOfflineTest, read_values) {
+  std::vector<char> buf;
+  buf.resize(2 * data.size());
+  ASSERT_EQ(data.size(), memory->Read(offset, buf.data(), buf.size()));
+  buf.resize(data.size());
+  ASSERT_EQ(buf, data);
+}
+
+}  // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index f5492a2..fb56e8a 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -79,6 +79,35 @@
   ASSERT_TRUE(Detach(pid));
 }
 
+TEST_F(MemoryRemoteTest, read_large) {
+  static constexpr size_t kTotalPages = 245;
+  std::vector<uint8_t> src(kTotalPages * getpagesize());
+  for (size_t i = 0; i < kTotalPages; i++) {
+    memset(&src[i * getpagesize()], i, getpagesize());
+  }
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+  TestScopedPidReaper reap(pid);
+
+  ASSERT_TRUE(Attach(pid));
+
+  MemoryRemote remote(pid);
+
+  std::vector<uint8_t> dst(kTotalPages * getpagesize());
+  ASSERT_TRUE(remote.ReadFully(reinterpret_cast<uint64_t>(src.data()), dst.data(), src.size()));
+  for (size_t i = 0; i < kTotalPages * getpagesize(); i++) {
+    ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(Detach(pid));
+}
+
 TEST_F(MemoryRemoteTest, read_partial) {
   char* mapping = static_cast<char*>(
       mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 0cb70ba..8b5b31f 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -29,11 +29,15 @@
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
 
 #include "MachineArm.h"
 #include "MachineArm64.h"
 #include "MachineX86.h"
 #include "MachineX86_64.h"
+#include "MachineMips.h"
+#include "MachineMips64.h"
 
 namespace unwindstack {
 
@@ -152,7 +156,87 @@
   return result;
 }
 
-using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64>;
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips>() {
+  std::vector<Register> result;
+  result.push_back({"r0", MIPS_REG_R0});
+  result.push_back({"r1", MIPS_REG_R1});
+  result.push_back({"r2", MIPS_REG_R2});
+  result.push_back({"r3", MIPS_REG_R3});
+  result.push_back({"r4", MIPS_REG_R4});
+  result.push_back({"r5", MIPS_REG_R5});
+  result.push_back({"r6", MIPS_REG_R6});
+  result.push_back({"r7", MIPS_REG_R7});
+  result.push_back({"r8", MIPS_REG_R8});
+  result.push_back({"r9", MIPS_REG_R9});
+  result.push_back({"r10", MIPS_REG_R10});
+  result.push_back({"r11", MIPS_REG_R11});
+  result.push_back({"r12", MIPS_REG_R12});
+  result.push_back({"r13", MIPS_REG_R13});
+  result.push_back({"r14", MIPS_REG_R14});
+  result.push_back({"r15", MIPS_REG_R15});
+  result.push_back({"r16", MIPS_REG_R16});
+  result.push_back({"r17", MIPS_REG_R17});
+  result.push_back({"r18", MIPS_REG_R18});
+  result.push_back({"r19", MIPS_REG_R19});
+  result.push_back({"r20", MIPS_REG_R20});
+  result.push_back({"r21", MIPS_REG_R21});
+  result.push_back({"r22", MIPS_REG_R22});
+  result.push_back({"r23", MIPS_REG_R23});
+  result.push_back({"r24", MIPS_REG_R24});
+  result.push_back({"r25", MIPS_REG_R25});
+  result.push_back({"r26", MIPS_REG_R26});
+  result.push_back({"r27", MIPS_REG_R27});
+  result.push_back({"r28", MIPS_REG_R28});
+  result.push_back({"sp", MIPS_REG_SP});
+  result.push_back({"r30", MIPS_REG_R30});
+  result.push_back({"ra", MIPS_REG_RA});
+  result.push_back({"pc", MIPS_REG_PC});
+
+  return result;
+}
+
+template<>
+std::vector<Register> ExpectedRegisters<RegsMips64>() {
+  std::vector<Register> result;
+  result.push_back({"r0", MIPS64_REG_R0});
+  result.push_back({"r1", MIPS64_REG_R1});
+  result.push_back({"r2", MIPS64_REG_R2});
+  result.push_back({"r3", MIPS64_REG_R3});
+  result.push_back({"r4", MIPS64_REG_R4});
+  result.push_back({"r5", MIPS64_REG_R5});
+  result.push_back({"r6", MIPS64_REG_R6});
+  result.push_back({"r7", MIPS64_REG_R7});
+  result.push_back({"r8", MIPS64_REG_R8});
+  result.push_back({"r9", MIPS64_REG_R9});
+  result.push_back({"r10", MIPS64_REG_R10});
+  result.push_back({"r11", MIPS64_REG_R11});
+  result.push_back({"r12", MIPS64_REG_R12});
+  result.push_back({"r13", MIPS64_REG_R13});
+  result.push_back({"r14", MIPS64_REG_R14});
+  result.push_back({"r15", MIPS64_REG_R15});
+  result.push_back({"r16", MIPS64_REG_R16});
+  result.push_back({"r17", MIPS64_REG_R17});
+  result.push_back({"r18", MIPS64_REG_R18});
+  result.push_back({"r19", MIPS64_REG_R19});
+  result.push_back({"r20", MIPS64_REG_R20});
+  result.push_back({"r21", MIPS64_REG_R21});
+  result.push_back({"r22", MIPS64_REG_R22});
+  result.push_back({"r23", MIPS64_REG_R23});
+  result.push_back({"r24", MIPS64_REG_R24});
+  result.push_back({"r25", MIPS64_REG_R25});
+  result.push_back({"r26", MIPS64_REG_R26});
+  result.push_back({"r27", MIPS64_REG_R27});
+  result.push_back({"r28", MIPS64_REG_R28});
+  result.push_back({"sp", MIPS64_REG_SP});
+  result.push_back({"r30", MIPS64_REG_R30});
+  result.push_back({"ra", MIPS64_REG_RA});
+  result.push_back({"pc", MIPS64_REG_PC});
+
+  return result;
+}
+
+using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
 TYPED_TEST_CASE(RegsIterateTest, RegTypes);
 
 TYPED_TEST(RegsIterateTest, iterate) {
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index ae57caf..ef9e61c 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -23,11 +23,15 @@
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
 
 #include "MachineArm.h"
 #include "MachineArm64.h"
 #include "MachineX86.h"
 #include "MachineX86_64.h"
+#include "MachineMips.h"
+#include "MachineMips64.h"
 
 #include "MemoryFake.h"
 
@@ -204,4 +208,64 @@
   EXPECT_EQ(0x150U, regs.pc());
 }
 
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_non_rt) {
+  uint64_t addr = 0x1000;
+  RegsMips regs;
+  regs[MIPS_REG_PC] = 0x8000;
+  regs[MIPS_REG_SP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL);
+
+  for (uint64_t index = 0; index <= 50; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x220U, regs[MIPS_REG_SP]);
+  EXPECT_EQ(0x040U, regs[MIPS_REG_PC]);
+  EXPECT_EQ(0x220U, regs.sp());
+  EXPECT_EQ(0x040U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips_step_if_signal_handler_rt) {
+  uint64_t addr = 0x1000;
+  RegsMips regs;
+  regs[MIPS_REG_PC] = 0x8000;
+  regs[MIPS_REG_SP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x350U, regs[MIPS_REG_SP]);
+  EXPECT_EQ(0x170U, regs[MIPS_REG_PC]);
+  EXPECT_EQ(0x350U, regs.sp());
+  EXPECT_EQ(0x170U, regs.pc());
+}
+
+TEST_F(RegsStepIfSignalHandlerTest, mips64_step_if_signal_handler) {
+  uint64_t addr = 0x1000;
+  RegsMips64 regs;
+  regs[MIPS64_REG_PC] = 0x8000;
+  regs[MIPS64_REG_SP] = addr;
+  regs.SetFromRaw();
+
+  elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL);
+
+  for (uint64_t index = 0; index <= 100; index++) {
+    process_memory_.SetData64(addr + index * 8, index * 0x10);
+  }
+
+  ASSERT_TRUE(regs.StepIfSignalHandler(0x8000, elf_.get(), &process_memory_));
+  EXPECT_EQ(0x350U, regs[MIPS64_REG_SP]);
+  EXPECT_EQ(0x600U, regs[MIPS64_REG_PC]);
+  EXPECT_EQ(0x350U, regs.sp());
+  EXPECT_EQ(0x600U, regs.pc());
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index a932973..3f84890 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -25,6 +25,8 @@
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
 
 #include "ElfFake.h"
 #include "MemoryFake.h"
@@ -112,6 +114,30 @@
   ASSERT_EQ(0x1U,  x86_64.GetAdjustedPc(0x2, elf_.get()));
   ASSERT_EQ(0x0U,  x86_64.GetAdjustedPc(0x1, elf_.get()));
   ASSERT_EQ(0x0U,  x86_64.GetAdjustedPc(0x0, elf_.get()));
+
+  RegsMips mips;
+  ASSERT_EQ(0x8U, mips.GetAdjustedPc(0x10, elf_.get()));
+  ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x8, elf_.get()));
+  ASSERT_EQ(0x7U, mips.GetAdjustedPc(0x7, elf_.get()));
+  ASSERT_EQ(0x6U, mips.GetAdjustedPc(0x6, elf_.get()));
+  ASSERT_EQ(0x5U, mips.GetAdjustedPc(0x5, elf_.get()));
+  ASSERT_EQ(0x4U, mips.GetAdjustedPc(0x4, elf_.get()));
+  ASSERT_EQ(0x3U, mips.GetAdjustedPc(0x3, elf_.get()));
+  ASSERT_EQ(0x2U, mips.GetAdjustedPc(0x2, elf_.get()));
+  ASSERT_EQ(0x1U, mips.GetAdjustedPc(0x1, elf_.get()));
+  ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x0, elf_.get()));
+
+  RegsMips64 mips64;
+  ASSERT_EQ(0x8U, mips64.GetAdjustedPc(0x10, elf_.get()));
+  ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x8, elf_.get()));
+  ASSERT_EQ(0x7U, mips64.GetAdjustedPc(0x7, elf_.get()));
+  ASSERT_EQ(0x6U, mips64.GetAdjustedPc(0x6, elf_.get()));
+  ASSERT_EQ(0x5U, mips64.GetAdjustedPc(0x5, elf_.get()));
+  ASSERT_EQ(0x4U, mips64.GetAdjustedPc(0x4, elf_.get()));
+  ASSERT_EQ(0x3U, mips64.GetAdjustedPc(0x3, elf_.get()));
+  ASSERT_EQ(0x2U, mips64.GetAdjustedPc(0x2, elf_.get()));
+  ASSERT_EQ(0x1U, mips64.GetAdjustedPc(0x1, elf_.get()));
+  ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x0, elf_.get()));
 }
 
 TEST_F(RegsTest, rel_pc_arm) {
@@ -154,6 +180,8 @@
   RegsArm64 regs_arm64;
   RegsX86 regs_x86;
   RegsX86_64 regs_x86_64;
+  RegsMips regs_mips;
+  RegsMips64 regs_mips64;
   MapInfo map_info(0x1000, 0x2000);
   Elf* invalid_elf = new Elf(new MemoryFake);
   map_info.elf = invalid_elf;
@@ -173,6 +201,14 @@
   regs_x86_64.set_pc(0x1800);
   EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
   EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf));
+
+  regs_mips.set_pc(0x1900);
+  EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
+  EXPECT_EQ(0x900U, regs_mips.GetAdjustedPc(0x900U, invalid_elf));
+
+  regs_mips64.set_pc(0x1a00);
+  EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
+  EXPECT_EQ(0xa00U, regs_mips64.GetAdjustedPc(0xa00U, invalid_elf));
 }
 
 TEST_F(RegsTest, arm_set_from_raw) {
@@ -215,6 +251,26 @@
   EXPECT_EQ(0x4900000000U, x86_64.pc());
 }
 
+TEST_F(RegsTest, mips_set_from_raw) {
+  RegsMips mips;
+  uint32_t* regs = reinterpret_cast<uint32_t*>(mips.RawData());
+  regs[29] = 0x100;
+  regs[32] = 0x200;
+  mips.SetFromRaw();
+  EXPECT_EQ(0x100U, mips.sp());
+  EXPECT_EQ(0x200U, mips.pc());
+}
+
+TEST_F(RegsTest, mips64_set_from_raw) {
+  RegsMips64 mips64;
+  uint64_t* regs = reinterpret_cast<uint64_t*>(mips64.RawData());
+  regs[29] = 0xb100000000ULL;
+  regs[32] = 0xc200000000ULL;
+  mips64.SetFromRaw();
+  EXPECT_EQ(0xb100000000U, mips64.sp());
+  EXPECT_EQ(0xc200000000U, mips64.pc());
+}
+
 TEST_F(RegsTest, machine_type) {
   RegsArm arm_regs;
   EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
@@ -227,6 +283,12 @@
 
   RegsX86_64 x86_64_regs;
   EXPECT_EQ(ARCH_X86_64, x86_64_regs.Arch());
+
+  RegsMips mips_regs;
+  EXPECT_EQ(ARCH_MIPS, mips_regs.Arch());
+
+  RegsMips64 mips64_regs;
+  EXPECT_EQ(ARCH_MIPS64, mips64_regs.Arch());
 }
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 962f744..8f28036 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -96,6 +96,54 @@
       frame_info);
 }
 
+TEST(UnwindOfflineTest, pc_in_gnu_debugdata_arm32) {
+  std::string dir(TestGetFileDirectory() + "offline/gnu_debugdata_arm32/");
+
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init((dir + "stack.data").c_str(), 0));
+
+  FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  RegsArm regs;
+  uint64_t reg_value;
+  ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", &reg_value));
+  regs[ARM_REG_PC] = reg_value;
+  ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", &reg_value));
+  regs[ARM_REG_SP] = reg_value;
+  regs.SetFromRaw();
+  fclose(fp);
+
+  fp = fopen((dir + "maps.txt").c_str(), "r");
+  ASSERT_TRUE(fp != nullptr);
+  // The file is guaranteed to be less than 4096 bytes.
+  std::vector<char> buffer(4096);
+  ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+  fclose(fp);
+
+  BufferMaps maps(buffer.data());
+  ASSERT_TRUE(maps.Parse());
+
+  ASSERT_EQ(ARCH_ARM, regs.Arch());
+
+  std::shared_ptr<Memory> process_memory(memory);
+
+  char* cwd = getcwd(nullptr, 0);
+  ASSERT_EQ(0, chdir(dir.c_str()));
+  Unwinder unwinder(128, &maps, &regs, process_memory);
+  unwinder.Unwind();
+  ASSERT_EQ(0, chdir(cwd));
+  free(cwd);
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0006dc49  libandroid_runtime.so "
+      "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
+      "  #01 pc 0006dce5  libandroid_runtime.so "
+      "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
+      frame_info);
+}
+
 TEST(UnwindOfflineTest, pc_straddle_arm64) {
   std::string dir(TestGetFileDirectory() + "offline/straddle_arm64/");
 
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2034191..cd46807 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -32,6 +32,8 @@
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
 #include <unwindstack/Unwinder.h>
 
 #include "ElfFake.h"
@@ -733,6 +735,16 @@
   x86_64->set_sp(0x10000);
   reg_list.push_back(x86_64);
 
+  RegsMips* mips = new RegsMips;
+  mips->set_pc(0x2300);
+  mips->set_sp(0x10000);
+  reg_list.push_back(mips);
+
+  RegsMips64* mips64 = new RegsMips64;
+  mips64->set_pc(0x2300);
+  mips64->set_sp(0x10000);
+  reg_list.push_back(mips64);
+
   for (auto regs : reg_list) {
     ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
 
@@ -744,10 +756,12 @@
     switch (regs->Arch()) {
       case ARCH_ARM:
       case ARCH_X86:
+      case ARCH_MIPS:
         expected = "  #00 pc 00001300  /system/fake/libc.so (Frame0+10)";
         break;
       case ARCH_ARM64:
       case ARCH_X86_64:
+      case ARCH_MIPS64:
         expected = "  #00 pc 0000000000001300  /system/fake/libc.so (Frame0+10)";
         break;
       default:
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so
new file mode 100644
index 0000000..e4283e6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/libandroid_runtime.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt
new file mode 100644
index 0000000..1bcddb6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/maps.txt
@@ -0,0 +1 @@
+f1f10000-f2049000 r-xp 00000000 00:00 0   libandroid_runtime.so
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt
new file mode 100644
index 0000000..c6a93dc
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/regs.txt
@@ -0,0 +1,2 @@
+pc: f1f6dc49
+sp: d8fe6930
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data
new file mode 100644
index 0000000..19cdf2d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/gnu_debugdata_arm32/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 7896279..81bedb7 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -76,6 +76,12 @@
     case unwindstack::ARCH_X86_64:
       printf("x86_64");
       break;
+    case unwindstack::ARCH_MIPS:
+      printf("mips");
+      break;
+    case unwindstack::ARCH_MIPS64:
+      printf("mips64");
+      break;
     default:
       printf("unknown\n");
       return;
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
new file mode 100644
index 0000000..d64ef8f
--- /dev/null
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include <android-base/stringprintf.h>
+
+struct map_info_t {
+  uint64_t start;
+  uint64_t end;
+  uint64_t offset;
+  std::string name;
+};
+
+static bool Attach(pid_t pid) {
+  if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+    return false;
+  }
+
+  // Allow at least 1 second to attach properly.
+  for (size_t i = 0; i < 1000; i++) {
+    siginfo_t si;
+    if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+      return true;
+    }
+    usleep(1000);
+  }
+  printf("%d: Failed to stop.\n", pid);
+  return false;
+}
+
+bool SaveRegs(unwindstack::Regs* regs) {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "w+"), &fclose);
+  if (fp == nullptr) {
+    printf("Failed to create file regs.txt.\n");
+    return false;
+  }
+  regs->IterateRegisters([&fp](const char* name, uint64_t value) {
+    fprintf(fp.get(), "%s: %" PRIx64 "\n", name, value);
+  });
+
+  return true;
+}
+
+bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("stack.data", "w+"), &fclose);
+  if (fp == nullptr) {
+    printf("Failed to create stack.data.\n");
+    return false;
+  }
+
+  size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+  if (bytes != sizeof(sp_start)) {
+    perror("Failed to write all data.");
+    return false;
+  }
+
+  std::vector<uint8_t> buffer(sp_end - sp_start);
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+    printf("Unable to read stack data.\n");
+    return false;
+  }
+
+  bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+  if (bytes != buffer.size()) {
+    printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+    return 1;
+  }
+
+  return true;
+}
+
+bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
+  std::string cur_name;
+  if (info->name.empty()) {
+    cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start);
+  } else {
+    cur_name = basename(info->name.c_str());
+    cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start);
+  }
+
+  std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+  if (output == nullptr) {
+    printf("Cannot create %s\n", cur_name.c_str());
+    return false;
+  }
+  std::vector<uint8_t> buffer(info->end - info->start);
+  // If this is a mapped in file, it might not be possible to read the entire
+  // map, so read all that is readable.
+  size_t bytes = memory->Read(info->start, buffer.data(), buffer.size());
+  if (bytes == 0) {
+    printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
+    return false;
+  }
+  size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
+  if (bytes_written != bytes) {
+    printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
+    return false;
+  }
+
+  // Replace the name with the new name.
+  info->name = cur_name;
+
+  return true;
+}
+
+bool CopyElfFromFile(map_info_t* info) {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
+  if (fp == nullptr) {
+    return false;
+  }
+
+  std::string cur_name = basename(info->name.c_str());
+  std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+  if (output == nullptr) {
+    printf("Cannot create file %s\n", cur_name.c_str());
+    return false;
+  }
+  std::vector<uint8_t> buffer(10000);
+  size_t bytes;
+  while ((bytes = fread(buffer.data(), 1, buffer.size(), fp.get())) > 0) {
+    size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
+    if (bytes_written != bytes) {
+      printf("Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
+             bytes_written);
+      return false;
+    }
+  }
+
+  // Replace the name with the new name.
+  info->name = cur_name;
+
+  return true;
+}
+
+int SaveData(pid_t pid) {
+  unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+  if (regs == nullptr) {
+    printf("Unable to get remote reg data.\n");
+    return 1;
+  }
+
+  unwindstack::RemoteMaps maps(pid);
+  if (!maps.Parse()) {
+    printf("Unable to parse maps.\n");
+    return 1;
+  }
+
+  // Save the current state of the registers.
+  if (!SaveRegs(regs)) {
+    return 1;
+  }
+
+  // Do an unwind so we know how much of the stack to save, and what
+  // elf files are involved.
+  uint64_t sp = regs->sp();
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+  unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
+  unwinder.Unwind();
+
+  std::unordered_map<uint64_t, map_info_t> maps_by_start;
+  uint64_t last_sp;
+  for (auto frame : unwinder.frames()) {
+    last_sp = frame.sp;
+    if (maps_by_start.count(frame.map_start) == 0) {
+      auto info = &maps_by_start[frame.map_start];
+      info->start = frame.map_start;
+      info->end = frame.map_end;
+      info->offset = frame.map_offset;
+      info->name = frame.map_name;
+      if (!CopyElfFromFile(info)) {
+        // Try to create the elf from memory, this will handle cases where
+        // the data only exists in memory such as vdso data on x86.
+        if (!CreateElfFromMemory(process_memory, info)) {
+          return 1;
+        }
+      }
+    }
+  }
+
+  if (!SaveStack(pid, sp, last_sp)) {
+    return 1;
+  }
+
+  std::vector<std::pair<uint64_t, map_info_t>> sorted_maps(maps_by_start.begin(),
+                                                           maps_by_start.end());
+  std::sort(sorted_maps.begin(), sorted_maps.end(),
+            [](auto& a, auto& b) { return a.first < b.first; });
+
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
+  if (fp == nullptr) {
+    printf("Failed to create maps.txt.\n");
+    return false;
+  }
+
+  for (auto& element : sorted_maps) {
+    map_info_t& map = element.second;
+    fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
+            map.offset);
+    if (!map.name.empty()) {
+      fprintf(fp.get(), "   %s", map.name.c_str());
+    }
+    fprintf(fp.get(), "\n");
+  }
+
+  return 0;
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    printf("Usage: unwind_for_offline <PID>\n");
+    return 1;
+  }
+
+  pid_t pid = atoi(argv[1]);
+  if (!Attach(pid)) {
+    printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+    return 1;
+  }
+
+  int return_code = SaveData(pid);
+
+  ptrace(PTRACE_DETACH, pid, 0, 0);
+
+  return return_code;
+}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a00b2ee..7f2d11d 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -87,7 +87,7 @@
   for (const DwarfFde* fde : *section) {
     // Sometimes there are entries that have empty length, skip those since
     // they don't contain any interesting information.
-    if (fde->pc_start == fde->pc_end) {
+    if (fde == nullptr || fde->pc_start == fde->pc_end) {
       continue;
     }
     printf("\n  PC 0x%" PRIx64, fde->pc_start + load_bias);
diff --git a/libutils/include/utils/Atomic.h b/libutils/include/utils/Atomic.h
index 7eb476c..0f592fe 100644
--- a/libutils/include/utils/Atomic.h
+++ b/libutils/include/utils/Atomic.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UTILS_ATOMIC_H
 #define ANDROID_UTILS_ATOMIC_H
 
+// DO NOT USE: Please instead use std::atomic
+
 #include <cutils/atomic.h>
 
 #endif // ANDROID_UTILS_ATOMIC_H
diff --git a/libutils/include/utils/BitSet.h b/libutils/include/utils/BitSet.h
index 8c61293..8abfb1a 100644
--- a/libutils/include/utils/BitSet.h
+++ b/libutils/include/utils/BitSet.h
@@ -22,6 +22,8 @@
 
 /*
  * Contains some bit manipulation helpers.
+ *
+ * DO NOT USE: std::bitset<32> or std::bitset<64> preferred
  */
 
 namespace android {
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
index 3019a21..9bf82eb 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -34,6 +34,8 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+// DO NOT USE: please use std::condition_variable instead.
+
 /*
  * Condition variable class.  The implementation is system-dependent.
  *
diff --git a/libutils/include/utils/Debug.h b/libutils/include/utils/Debug.h
index 08893bd..4cbb462 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -29,20 +29,12 @@
 #define COMPILE_TIME_ASSERT(_exp) \
     template class CompileTimeAssert< (_exp) >;
 #endif
+
+// DO NOT USE: Please use static_assert instead
 #define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
     CompileTimeAssert<( _exp )>();
 
 // ---------------------------------------------------------------------------
-
-#ifdef __cplusplus
-template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse;
-template<typename LHS, typename RHS> 
-struct CompileTimeIfElse<true,  LHS, RHS> { typedef LHS TYPE; };
-template<typename LHS, typename RHS> 
-struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; };
-#endif
-
-// ---------------------------------------------------------------------------
 }; // namespace android
 
 #endif // ANDROID_UTILS_DEBUG_H
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 070c710..675e211 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -33,13 +33,13 @@
 public:
     template<size_t N>
     static size_t align(size_t size) {
-        COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+        static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
         return (size + (N-1)) & ~(N-1);
     }
 
     template<size_t N>
     static size_t align(void const*& buffer) {
-        COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) );
+        static_assert(!(N & (N - 1)), "Can only align to a power of 2.");
         uintptr_t b = uintptr_t(buffer);
         buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));
         return size_t(uintptr_t(buffer) - b);
diff --git a/libutils/include/utils/Functor.h b/libutils/include/utils/Functor.h
index 09ea614..3182a9c 100644
--- a/libutils/include/utils/Functor.h
+++ b/libutils/include/utils/Functor.h
@@ -21,6 +21,10 @@
 
 namespace  android {
 
+// DO NOT USE: please use
+// - C++ lambda
+// - class with well-defined and specific functionality and semantics
+
 class Functor {
 public:
     Functor() {}
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
index f93ad6e..03bfe27 100644
--- a/libutils/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -30,6 +30,8 @@
 
 namespace android {
 
+// DO NOT USE: please use std::map
+
 template <typename KEY, typename VALUE>
 class KeyedVector
 {
diff --git a/libutils/include/utils/List.h b/libutils/include/utils/List.h
index 403cd7f..daca016 100644
--- a/libutils/include/utils/List.h
+++ b/libutils/include/utils/List.h
@@ -37,6 +37,8 @@
  *
  * Objects added to the list are copied using the assignment operator,
  * so this must be defined.
+ *
+ * DO NOT USE: please use std::list<T>
  */
 template<typename T> 
 class List 
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index 9afedd4..bc47a5c 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -39,6 +39,11 @@
 #pragma clang diagnostic ignored "-Wundefined-var-template"
 #endif
 
+// DO NOT USE: Please use scoped static initialization. For instance:
+//     MyClass& getInstance() {
+//         static MyClass gInstance(...);
+//         return gInstance;
+//     }
 template <typename TYPE>
 class ANDROID_API Singleton
 {
diff --git a/libutils/include/utils/SortedVector.h b/libutils/include/utils/SortedVector.h
index 5b2a232..47c1376 100644
--- a/libutils/include/utils/SortedVector.h
+++ b/libutils/include/utils/SortedVector.h
@@ -30,6 +30,8 @@
 
 namespace android {
 
+// DO NOT USE: please use std::set
+
 template <class TYPE>
 class SortedVector : private SortedVectorImpl
 {
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 15ed19f..5f0ce06 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -37,6 +37,8 @@
 
 class String8;
 
+// DO NOT USE: please use std::u16string
+
 //! This is a string holding UTF-16 characters.
 class String16
 {
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 0225c6b..94ac32f 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -32,6 +32,8 @@
 
 class String16;
 
+// DO NOT USE: please use std::string
+
 //! This is a string holding UTF-8 characters. Does not allow the value more
 // than 0x10FFFF, which is not valid unicode codepoint.
 class String8
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index a261fc8..598298d 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -36,6 +36,8 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+// DO NOT USE: please use std::thread
+
 class Thread : virtual public RefBase
 {
 public:
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index 7e00123..a1a0234 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -49,6 +49,8 @@
  * The main templated vector class ensuring type safety
  * while making use of VectorImpl.
  * This is the class users want to use.
+ *
+ * DO NOT USE: please use std::vector
  */
 
 template <class TYPE>
diff --git a/libutils/include/utils/misc.h b/libutils/include/utils/misc.h
index 6cccec3..af5ea02 100644
--- a/libutils/include/utils/misc.h
+++ b/libutils/include/utils/misc.h
@@ -22,7 +22,9 @@
 
 #include <utils/Endian.h>
 
-/* get #of elements in a static array */
+/* get #of elements in a static array
+ * DO NOT USE: please use std::vector/std::array instead
+ */
 #ifndef NELEM
 # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
 #endif
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index cd3e164..46aa5a6 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -84,4 +84,4 @@
 }
 BENCHMARK(Iterate_all_files);
 
-BENCHMARK_MAIN()
+BENCHMARK_MAIN();
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index 8704611..84f0764 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Logging Daemon test cases">
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
diff --git a/property_service/.clang-format b/property_service/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/property_service/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/property_service/Android.bp b/property_service/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/property_service/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
new file mode 100644
index 0000000..3e732b5
--- /dev/null
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -0,0 +1,16 @@
+cc_library_static {
+    name: "libpropertyinfoparser",
+    srcs: ["property_info_parser.cpp"],
+
+    cpp_std: "experimental",
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+    },
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    stl: "none",
+    export_include_dirs: ["include"],
+}
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
new file mode 100644
index 0000000..8c3507e
--- /dev/null
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -0,0 +1,224 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_PARSER_H
+#define PROPERTY_INFO_PARSER_H
+
+#include <stdint.h>
+
+namespace android {
+namespace properties {
+
+// The below structs intentionally do not end with char name[0] or other tricks to allocate
+// with a dynamic size, such that they can be added onto in the future without breaking
+// backwards compatibility.
+struct PropertyEntry {
+  uint32_t name_offset;
+  uint32_t namelen;
+
+  // This is the context match for this node_; ~0u if it doesn't correspond to any.
+  uint32_t context_index;
+  // This is the schema for this node_; ~0u if it doesn't correspond to any.
+  uint32_t schema_index;
+};
+
+struct TrieNodeInternal {
+  // This points to a property entry struct, which includes the name for this node
+  uint32_t property_entry;
+
+  // Children are a sorted list of child nodes_; binary search them.
+  uint32_t num_child_nodes;
+  uint32_t child_nodes;
+
+  // Prefixes are terminating prefix matches at this node, sorted longest to smallest
+  // Take the first match sequentially found with StartsWith().
+  uint32_t num_prefixes;
+  uint32_t prefix_entries;
+
+  // Exact matches are a sorted list of exact matches at this node_; binary search them.
+  uint32_t num_exact_matches;
+  uint32_t exact_match_entries;
+};
+
+struct PropertyInfoAreaHeader {
+  // The current version of this data as created by property service.
+  uint32_t current_version;
+  // The lowest version of libc that can properly parse this data.
+  uint32_t minimum_supported_version;
+  uint32_t size;
+  uint32_t contexts_offset;
+  uint32_t schemas_offset;
+  uint32_t root_offset;
+};
+
+class SerializedData {
+ public:
+  uint32_t size() const {
+    return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base_)->size;
+  }
+
+  const char* c_string(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return nullptr;
+    return static_cast<const char*>(data_base_ + offset);
+  }
+
+  const uint32_t* uint32_array(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return nullptr;
+    return reinterpret_cast<const uint32_t*>(data_base_ + offset);
+  }
+
+  uint32_t uint32(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return ~0u;
+    return *reinterpret_cast<const uint32_t*>(data_base_ + offset);
+  }
+
+  const char* data_base() const { return data_base_; }
+
+ private:
+  const char data_base_[0];
+};
+
+class TrieNode {
+ public:
+  TrieNode() : serialized_data_(nullptr), trie_node_base_(nullptr) {}
+  TrieNode(const SerializedData* data_base, const TrieNodeInternal* trie_node_base)
+      : serialized_data_(data_base), trie_node_base_(trie_node_base) {}
+
+  const char* name() const {
+    return serialized_data_->c_string(node_property_entry()->name_offset);
+  }
+
+  uint32_t context_index() const { return node_property_entry()->context_index; }
+  uint32_t schema_index() const { return node_property_entry()->schema_index; }
+
+  uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }
+  TrieNode child_node(int n) const {
+    uint32_t child_node_offset = serialized_data_->uint32_array(trie_node_base_->child_nodes)[n];
+    const TrieNodeInternal* trie_node_base =
+        reinterpret_cast<const TrieNodeInternal*>(serialized_data_->data_base() + child_node_offset);
+    return TrieNode(serialized_data_, trie_node_base);
+  }
+
+  bool FindChildForString(const char* input, uint32_t namelen, TrieNode* child) const;
+
+  uint32_t num_prefixes() const { return trie_node_base_->num_prefixes; }
+  const PropertyEntry* prefix(int n) const {
+    uint32_t prefix_entry_offset =
+        serialized_data_->uint32_array(trie_node_base_->prefix_entries)[n];
+    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+                                                  prefix_entry_offset);
+  }
+
+  uint32_t num_exact_matches() const { return trie_node_base_->num_exact_matches; }
+  const PropertyEntry* exact_match(int n) const {
+    uint32_t exact_match_entry_offset =
+        serialized_data_->uint32_array(trie_node_base_->exact_match_entries)[n];
+    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+                                                  exact_match_entry_offset);
+  }
+
+ private:
+  const PropertyEntry* node_property_entry() const {
+    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
+                                                  trie_node_base_->property_entry);
+  }
+
+  const SerializedData* serialized_data_;
+  const TrieNodeInternal* trie_node_base_;
+};
+
+class PropertyInfoArea : private SerializedData {
+ public:
+  void GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
+                              uint32_t* schema_index) const;
+  void GetPropertyInfo(const char* property, const char** context, const char** schema) const;
+
+  int FindContextIndex(const char* context) const;
+  int FindSchemaIndex(const char* schema) const;
+
+  const char* context(uint32_t index) const {
+    uint32_t context_array_size_offset = contexts_offset();
+    const uint32_t* context_array = uint32_array(context_array_size_offset + sizeof(uint32_t));
+    return data_base() + context_array[index];
+  }
+
+  const char* schema(uint32_t index) const {
+    uint32_t schema_array_size_offset = schemas_offset();
+    const uint32_t* schema_array = uint32_array(schema_array_size_offset + sizeof(uint32_t));
+    return data_base() + schema_array[index];
+  }
+
+  uint32_t current_version() const { return header()->current_version; }
+  uint32_t minimum_supported_version() const { return header()->minimum_supported_version; }
+
+  uint32_t size() const { return SerializedData::size(); }
+
+  uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }
+  uint32_t num_schemas() const { return uint32_array(schemas_offset())[0]; }
+
+  TrieNode root_node() const { return trie(header()->root_offset); }
+
+ private:
+  void CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+                        uint32_t* context_index, uint32_t* schema_index) const;
+
+  const PropertyInfoAreaHeader* header() const {
+    return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());
+  }
+  uint32_t contexts_offset() const { return header()->contexts_offset; }
+  uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }
+  uint32_t schemas_offset() const { return header()->schemas_offset; }
+  uint32_t schemas_array_offset() const { return schemas_offset() + sizeof(uint32_t); }
+
+  TrieNode trie(uint32_t offset) const {
+    if (offset != 0 && offset > size()) return TrieNode();
+    const TrieNodeInternal* trie_node_base =
+        reinterpret_cast<const TrieNodeInternal*>(data_base() + offset);
+    return TrieNode(this, trie_node_base);
+  }
+};
+
+// This is essentially a smart pointer for read only mmap region for property contexts.
+class PropertyInfoAreaFile {
+ public:
+  PropertyInfoAreaFile() : mmap_base_(nullptr), mmap_size_(0) {}
+  ~PropertyInfoAreaFile() { Reset(); }
+
+  PropertyInfoAreaFile(const PropertyInfoAreaFile&) = delete;
+  void operator=(const PropertyInfoAreaFile&) = delete;
+  PropertyInfoAreaFile(PropertyInfoAreaFile&&) = default;
+  PropertyInfoAreaFile& operator=(PropertyInfoAreaFile&&) = default;
+
+  bool LoadDefaultPath();
+  bool LoadPath(const char* filename);
+
+  const PropertyInfoArea* operator->() const {
+    return reinterpret_cast<const PropertyInfoArea*>(mmap_base_);
+  }
+
+  explicit operator bool() const { return mmap_base_ != nullptr; }
+
+  void Reset();
+
+ private:
+  void* mmap_base_;
+  size_t mmap_size_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoparser/property_info_parser.cpp b/property_service/libpropertyinfoparser/property_info_parser.cpp
new file mode 100644
index 0000000..a8f6636
--- /dev/null
+++ b/property_service/libpropertyinfoparser/property_info_parser.cpp
@@ -0,0 +1,246 @@
+//
+// Copyright (C) 2017 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 "property_info_parser/property_info_parser.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace properties {
+
+namespace {
+
+// Binary search to find index of element in an array compared via f(search).
+template <typename F>
+int Find(uint32_t array_length, F&& f) {
+  int bottom = 0;
+  int top = array_length - 1;
+  while (top >= bottom) {
+    int search = (top + bottom) / 2;
+
+    auto cmp = f(search);
+
+    if (cmp == 0) return search;
+    if (cmp < 0) bottom = search + 1;
+    if (cmp > 0) top = search - 1;
+  }
+  return -1;
+}
+
+}  // namespace
+
+// Binary search the list of contexts to find the index of a given context string.
+// Only should be used for TrieSerializer to construct the Trie.
+int PropertyInfoArea::FindContextIndex(const char* context) const {
+  return Find(num_contexts(), [this, context](auto array_offset) {
+    auto string_offset = uint32_array(contexts_array_offset())[array_offset];
+    return strcmp(c_string(string_offset), context);
+  });
+}
+
+// Binary search the list of schemas to find the index of a given schema string.
+// Only should be used for TrieSerializer to construct the Trie.
+int PropertyInfoArea::FindSchemaIndex(const char* schema) const {
+  return Find(num_schemas(), [this, schema](auto array_offset) {
+    auto string_offset = uint32_array(schemas_array_offset())[array_offset];
+    return strcmp(c_string(string_offset), schema);
+  });
+}
+
+// Binary search the list of children nodes to find a TrieNode for a given property piece.
+// Used to traverse the Trie in GetPropertyInfoIndexes().
+bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {
+  auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {
+    const char* child_name = child_node(array_offset).name();
+    int cmp = strncmp(child_name, name, namelen);
+    if (cmp == 0 && child_name[namelen] != '\0') {
+      // We use strncmp() since name isn't null terminated, but we don't want to match only a
+      // prefix of a child node's name, so we check here if we did only match a prefix and
+      // return 1, to indicate to the binary search to search earlier in the array for the real
+      // match.
+      return 1;
+    }
+    return cmp;
+  });
+
+  if (node_index == -1) {
+    return false;
+  }
+  *child = child_node(node_index);
+  return true;
+}
+
+void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+                                        uint32_t* context_index, uint32_t* schema_index) const {
+  const uint32_t remaining_name_size = strlen(remaining_name);
+  for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
+    auto prefix_len = trie_node.prefix(i)->namelen;
+    if (prefix_len > remaining_name_size) continue;
+
+    if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {
+      if (trie_node.prefix(i)->context_index != ~0u) {
+        *context_index = trie_node.prefix(i)->context_index;
+      }
+      if (trie_node.prefix(i)->schema_index != ~0u) {
+        *schema_index = trie_node.prefix(i)->schema_index;
+      }
+      return;
+    }
+  }
+}
+
+void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
+                                              uint32_t* schema_index) const {
+  uint32_t return_context_index = ~0u;
+  uint32_t return_schema_index = ~0u;
+  const char* remaining_name = name;
+  auto trie_node = root_node();
+  while (true) {
+    const char* sep = strchr(remaining_name, '.');
+
+    // Apply prefix match for prefix deliminated with '.'
+    if (trie_node.context_index() != ~0u) {
+      return_context_index = trie_node.context_index();
+    }
+    if (trie_node.schema_index() != ~0u) {
+      return_schema_index = trie_node.schema_index();
+    }
+
+    // Check prefixes at this node.  This comes after the node check since these prefixes are by
+    // definition longer than the node itself.
+    CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+
+    if (sep == nullptr) {
+      break;
+    }
+
+    const uint32_t substr_size = sep - remaining_name;
+    TrieNode child_node;
+    if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {
+      break;
+    }
+
+    trie_node = child_node;
+    remaining_name = sep + 1;
+  }
+
+  // We've made it to a leaf node, so check contents and return appropriately.
+  // Check exact matches
+  for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {
+    if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {
+      if (context_index != nullptr) {
+        if (trie_node.exact_match(i)->context_index != ~0u) {
+          *context_index = trie_node.exact_match(i)->context_index;
+        } else {
+          *context_index = return_context_index;
+        }
+      }
+      if (schema_index != nullptr) {
+        if (trie_node.exact_match(i)->schema_index != ~0u) {
+          *schema_index = trie_node.exact_match(i)->schema_index;
+        } else {
+          *schema_index = return_schema_index;
+        }
+      }
+      return;
+    }
+  }
+  // Check prefix matches for prefixes not deliminated with '.'
+  CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+  // Return previously found prefix match.
+  if (context_index != nullptr) *context_index = return_context_index;
+  if (schema_index != nullptr) *schema_index = return_schema_index;
+  return;
+}
+
+void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,
+                                       const char** schema) const {
+  uint32_t context_index;
+  uint32_t schema_index;
+  GetPropertyInfoIndexes(property, &context_index, &schema_index);
+  if (context != nullptr) {
+    if (context_index == ~0u) {
+      *context = nullptr;
+    } else {
+      *context = this->context(context_index);
+    }
+  }
+  if (schema != nullptr) {
+    if (schema_index == ~0u) {
+      *schema = nullptr;
+    } else {
+      *schema = this->schema(schema_index);
+    }
+  }
+}
+
+bool PropertyInfoAreaFile::LoadDefaultPath() {
+  return LoadPath("/dev/__properties__/property_info");
+}
+
+bool PropertyInfoAreaFile::LoadPath(const char* filename) {
+  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
+
+  struct stat fd_stat;
+  if (fstat(fd, &fd_stat) < 0) {
+    close(fd);
+    return false;
+  }
+
+  if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
+      ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
+      (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
+    close(fd);
+    return false;
+  }
+
+  auto mmap_size = fd_stat.st_size;
+
+  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
+  if (map_result == MAP_FAILED) {
+    close(fd);
+    return false;
+  }
+
+  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
+  if (property_info_area->minimum_supported_version() > 1 ||
+      property_info_area->size() != mmap_size) {
+    munmap(map_result, mmap_size);
+    close(fd);
+    return false;
+  }
+
+  close(fd);
+  mmap_base_ = map_result;
+  mmap_size_ = mmap_size;
+  return true;
+}
+
+void PropertyInfoAreaFile::Reset() {
+  if (mmap_size_ > 0) {
+    munmap(mmap_base_, mmap_size_);
+  }
+  mmap_base_ = nullptr;
+  mmap_size_ = 0;
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
new file mode 100644
index 0000000..20e5e13
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -0,0 +1,38 @@
+cc_defaults {
+    name: "propertyinfoserializer_defaults",
+    cpp_std: "experimental",
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+    },
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    static_libs: [
+        "libpropertyinfoparser",
+        "libbase",
+    ],
+}
+
+cc_library_static {
+    name: "libpropertyinfoserializer",
+    defaults: ["propertyinfoserializer_defaults"],
+    srcs: [
+        "property_info_serializer.cpp",
+        "trie_builder.cpp",
+        "trie_serializer.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "propertyinfoserializer_tests",
+    defaults: ["propertyinfoserializer_defaults"],
+    srcs: [
+        "trie_builder_test.cpp",
+        "property_info_serializer_test.cpp",
+    ],
+    static_libs: ["libpropertyinfoserializer"],
+}
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
new file mode 100644
index 0000000..f7e708e
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+struct PropertyInfoEntry {
+  PropertyInfoEntry() {}
+  template <typename T, typename U, typename V>
+  PropertyInfoEntry(T&& name, U&& context, V&& schema, bool exact_match)
+      : name(std::forward<T>(name)),
+        context(std::forward<U>(context)),
+        schema(std::forward<V>(schema)),
+        exact_match(exact_match) {}
+  std::string name;
+  std::string context;
+  std::string schema;
+  bool exact_match;
+};
+
+bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
+               const std::string& default_context, const std::string& default_schema,
+               std::string* serialized_trie, std::string* error);
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer.cpp b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
new file mode 100644
index 0000000..656c96e
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 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 "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <set>
+
+#include "trie_builder.h"
+#include "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
+               const std::string& default_context, const std::string& default_schema,
+               std::string* serialized_trie, std::string* error) {
+  // Check that names are legal first
+  auto trie_builder = TrieBuilder(default_context, default_schema);
+
+  for (const auto& [name, context, schema, is_exact] : property_info) {
+    if (!trie_builder.AddToTrie(name, context, schema, is_exact, error)) {
+      return false;
+    }
+  }
+
+  auto trie_serializer = TrieSerializer();
+  *serialized_trie = trie_serializer.SerializeTrie(trie_builder);
+  return true;
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
new file mode 100644
index 0000000..46c2d06
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -0,0 +1,890 @@
+//
+// Copyright (C) 2017 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 "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace properties {
+
+TEST(propertyinfoserializer, TrieNodeCheck) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"test.", "1st", "1st", false},     {"test.test", "2nd", "2nd", false},
+
+      {"test.test1", "3rd", "3rd", true}, {"test.test2", "3rd", "3rd", true},
+      {"test.test3", "3rd", "3rd", true}, {"this.is.a.long.string", "4th", "4th", true},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  // Initial checks for property area.
+  EXPECT_EQ(1U, property_info_area->current_version());
+  EXPECT_EQ(1U, property_info_area->minimum_supported_version());
+
+  // Check the root node
+  auto root_node = property_info_area->root_node();
+  EXPECT_STREQ("root", root_node.name());
+  EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
+  EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
+
+  EXPECT_EQ(0U, root_node.num_prefixes());
+  EXPECT_EQ(0U, root_node.num_exact_matches());
+
+  ASSERT_EQ(2U, root_node.num_child_nodes());
+
+  // Check the 'test'. node
+  TrieNode test_node;
+  ASSERT_TRUE(root_node.FindChildForString("test", 4, &test_node));
+
+  EXPECT_STREQ("test", test_node.name());
+  EXPECT_STREQ("1st", property_info_area->context(test_node.context_index()));
+  EXPECT_STREQ("1st", property_info_area->schema(test_node.schema_index()));
+
+  EXPECT_EQ(0U, test_node.num_child_nodes());
+
+  EXPECT_EQ(1U, test_node.num_prefixes());
+  {
+    auto prefix = test_node.prefix(0);
+    EXPECT_STREQ("test", serialized_trie.data() + prefix->name_offset);
+    EXPECT_EQ(4U, prefix->namelen);
+    EXPECT_STREQ("2nd", property_info_area->context(prefix->context_index));
+    EXPECT_STREQ("2nd", property_info_area->schema(prefix->schema_index));
+  }
+
+  EXPECT_EQ(3U, test_node.num_exact_matches());
+  {
+    auto match1 = test_node.exact_match(0);
+    auto match2 = test_node.exact_match(1);
+    auto match3 = test_node.exact_match(2);
+    EXPECT_STREQ("test1", serialized_trie.data() + match1->name_offset);
+    EXPECT_STREQ("test2", serialized_trie.data() + match2->name_offset);
+    EXPECT_STREQ("test3", serialized_trie.data() + match3->name_offset);
+
+    EXPECT_STREQ("3rd", property_info_area->context(match1->context_index));
+    EXPECT_STREQ("3rd", property_info_area->context(match2->context_index));
+    EXPECT_STREQ("3rd", property_info_area->context(match3->context_index));
+
+    EXPECT_STREQ("3rd", property_info_area->schema(match1->schema_index));
+    EXPECT_STREQ("3rd", property_info_area->schema(match2->schema_index));
+    EXPECT_STREQ("3rd", property_info_area->schema(match3->schema_index));
+  }
+
+  // Check the long string node
+  auto expect_empty_one_child = [](auto& node) {
+    EXPECT_EQ(-1U, node.context_index());
+    EXPECT_EQ(0U, node.num_prefixes());
+    EXPECT_EQ(0U, node.num_exact_matches());
+    EXPECT_EQ(1U, node.num_child_nodes());
+  };
+
+  // Start with 'this'
+  TrieNode long_string_node;
+  ASSERT_TRUE(root_node.FindChildForString("this", 4, &long_string_node));
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'is'
+  ASSERT_TRUE(long_string_node.FindChildForString("is", 2, &long_string_node));
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'a'
+  ASSERT_TRUE(long_string_node.FindChildForString("a", 1, &long_string_node));
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'long'
+  ASSERT_TRUE(long_string_node.FindChildForString("long", 4, &long_string_node));
+  EXPECT_EQ(0U, long_string_node.num_prefixes());
+  EXPECT_EQ(1U, long_string_node.num_exact_matches());
+  EXPECT_EQ(0U, long_string_node.num_child_nodes());
+
+  auto final_match = long_string_node.exact_match(0);
+  EXPECT_STREQ("string", serialized_trie.data() + final_match->name_offset);
+  EXPECT_STREQ("4th", property_info_area->context(final_match->context_index));
+  EXPECT_STREQ("4th", property_info_area->schema(final_match->schema_index));
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"test.", "1st", "1st", false},       {"test.test", "2nd", "2nd", false},
+      {"test.test2.", "6th", "6th", false}, {"test.test", "5th", "5th", true},
+      {"test.test1", "3rd", "3rd", true},   {"test.test2", "7th", "7th", true},
+      {"test.test3", "3rd", "3rd", true},   {"this.is.a.long.string", "4th", "4th", true},
+      {"testoneword", "8th", "8th", true},  {"testwordprefix", "9th", "9th", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  // Sanity check
+  auto root_node = property_info_area->root_node();
+  EXPECT_STREQ("root", root_node.name());
+  EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
+  EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
+
+  const char* context;
+  const char* schema;
+  property_info_area->GetPropertyInfo("abc", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("abc.abc", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("123.abc", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+
+  property_info_area->GetPropertyInfo("test.a", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("test.b", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("test.c", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+
+  property_info_area->GetPropertyInfo("test.test", &context, &schema);
+  EXPECT_STREQ("5th", context);
+  EXPECT_STREQ("5th", schema);
+  property_info_area->GetPropertyInfo("test.testa", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("test.testb", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("test.testc", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+
+  property_info_area->GetPropertyInfo("test.test.a", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("test.test.b", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("test.test.c", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+
+  property_info_area->GetPropertyInfo("test.test1", &context, &schema);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", schema);
+  property_info_area->GetPropertyInfo("test.test2", &context, &schema);
+  EXPECT_STREQ("7th", context);
+  EXPECT_STREQ("7th", schema);
+  property_info_area->GetPropertyInfo("test.test3", &context, &schema);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", schema);
+
+  property_info_area->GetPropertyInfo("test.test11", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("test.test22", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("test.test33", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+
+  property_info_area->GetPropertyInfo("this.is.a.long.string", &context, &schema);
+  EXPECT_STREQ("4th", context);
+  EXPECT_STREQ("4th", schema);
+
+  property_info_area->GetPropertyInfo("this.is.a.long", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("this.is.a", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("this.is", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("this", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+
+  property_info_area->GetPropertyInfo("test.test2.a", &context, &schema);
+  EXPECT_STREQ("6th", context);
+  EXPECT_STREQ("6th", schema);
+
+  property_info_area->GetPropertyInfo("testoneword", &context, &schema);
+  EXPECT_STREQ("8th", context);
+  EXPECT_STREQ("8th", schema);
+
+  property_info_area->GetPropertyInfo("testwordprefix", &context, &schema);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", schema);
+
+  property_info_area->GetPropertyInfo("testwordprefixblah", &context, &schema);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", schema);
+
+  property_info_area->GetPropertyInfo("testwordprefix.blah", &context, &schema);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", schema);
+}
+
+TEST(propertyinfoserializer, RealProperties) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      // Contexts from system/sepolicy/private/property_contexts
+      {"net.rmnet", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.gprs", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.ppp", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.qmi", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.lte", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.cdma", "u:object_r:net_radio_prop:s0", "string", false},
+      {"net.dns", "u:object_r:net_dns_prop:s0", "string", false},
+      {"sys.usb.config", "u:object_r:system_radio_prop:s0", "string", false},
+      {"ril.", "u:object_r:radio_prop:s0", "string", false},
+      {"ro.ril.", "u:object_r:radio_prop:s0", "string", false},
+      {"gsm.", "u:object_r:radio_prop:s0", "string", false},
+      {"persist.radio", "u:object_r:radio_prop:s0", "string", false},
+
+      {"net.", "u:object_r:system_prop:s0", "string", false},
+      {"dev.", "u:object_r:system_prop:s0", "string", false},
+      {"ro.runtime.", "u:object_r:system_prop:s0", "string", false},
+      {"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0", "string", false},
+      {"hw.", "u:object_r:system_prop:s0", "string", false},
+      {"ro.hw.", "u:object_r:system_prop:s0", "string", false},
+      {"sys.", "u:object_r:system_prop:s0", "string", false},
+      {"sys.cppreopt", "u:object_r:cppreopt_prop:s0", "string", false},
+      {"sys.powerctl", "u:object_r:powerctl_prop:s0", "string", false},
+      {"sys.usb.ffs.", "u:object_r:ffs_prop:s0", "string", false},
+      {"service.", "u:object_r:system_prop:s0", "string", false},
+      {"dhcp.", "u:object_r:dhcp_prop:s0", "string", false},
+      {"dhcp.bt-pan.result", "u:object_r:pan_result_prop:s0", "string", false},
+      {"bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
+
+      {"debug.", "u:object_r:debug_prop:s0", "string", false},
+      {"debug.db.", "u:object_r:debuggerd_prop:s0", "string", false},
+      {"dumpstate.", "u:object_r:dumpstate_prop:s0", "string", false},
+      {"dumpstate.options", "u:object_r:dumpstate_options_prop:s0", "string", false},
+      {"log.", "u:object_r:log_prop:s0", "string", false},
+      {"log.tag", "u:object_r:log_tag_prop:s0", "string", false},
+      {"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0", "string", false},
+      {"security.perf_harden", "u:object_r:shell_prop:s0", "string", false},
+      {"service.adb.root", "u:object_r:shell_prop:s0", "string", false},
+      {"service.adb.tcp.port", "u:object_r:shell_prop:s0", "string", false},
+
+      {"persist.audio.", "u:object_r:audio_prop:s0", "string", false},
+      {"persist.bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"persist.debug.", "u:object_r:persist_debug_prop:s0", "string", false},
+      {"persist.logd.", "u:object_r:logd_prop:s0", "string", false},
+      {"persist.logd.security", "u:object_r:device_logging_prop:s0", "string", false},
+      {"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
+      {"logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
+      {"persist.log.tag", "u:object_r:log_tag_prop:s0", "string", false},
+      {"persist.mmc.", "u:object_r:mmc_prop:s0", "string", false},
+      {"persist.netd.stable_secret", "u:object_r:netd_stable_secret_prop:s0", "string", false},
+      {"persist.sys.", "u:object_r:system_prop:s0", "string", false},
+      {"persist.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
+      {"ro.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
+      {"persist.sys.audit_safemode", "u:object_r:safemode_prop:s0", "string", false},
+      {"persist.service.", "u:object_r:system_prop:s0", "string", false},
+      {"persist.service.bdroid.", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"persist.security.", "u:object_r:system_prop:s0", "string", false},
+      {"persist.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
+      {"ro.boot.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
+      {"ro.boottime.", "u:object_r:boottime_prop:s0", "string", false},
+      {"ro.serialno", "u:object_r:serialno_prop:s0", "string", false},
+      {"ro.boot.btmacaddr", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"ro.boot.serialno", "u:object_r:serialno_prop:s0", "string", false},
+      {"ro.bt.", "u:object_r:bluetooth_prop:s0", "string", false},
+      {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
+      {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
+      {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
+      {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+
+      {"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
+
+      {"vold.", "u:object_r:vold_prop:s0", "string", false},
+      {"ro.crypto.", "u:object_r:vold_prop:s0", "string", false},
+
+      {"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0", "string", false},
+
+      {"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0", "string",
+       false},
+
+      {"ctl.bootanim", "u:object_r:ctl_bootanim_prop:s0", "string", false},
+      {"ctl.dumpstate", "u:object_r:ctl_dumpstate_prop:s0", "string", false},
+      {"ctl.fuse_", "u:object_r:ctl_fuse_prop:s0", "string", false},
+      {"ctl.mdnsd", "u:object_r:ctl_mdnsd_prop:s0", "string", false},
+      {"ctl.ril-daemon", "u:object_r:ctl_rildaemon_prop:s0", "string", false},
+      {"ctl.bugreport", "u:object_r:ctl_bugreport_prop:s0", "string", false},
+      {"ctl.console", "u:object_r:ctl_console_prop:s0", "string", false},
+      {"ctl.", "u:object_r:ctl_default_prop:s0", "string", false},
+
+      {"nfc.", "u:object_r:nfc_prop:s0", "string", false},
+
+      {"config.", "u:object_r:config_prop:s0", "string", false},
+      {"ro.config.", "u:object_r:config_prop:s0", "string", false},
+      {"dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
+      {"ro.dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
+
+      {"wlan.", "u:object_r:wifi_prop:s0", "string", false},
+
+      {"lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
+      {"ro.lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
+
+      {"hwservicemanager.", "u:object_r:hwservicemanager_prop:s0", "string", false},
+      // Contexts from device/lge/bullhead/sepolicy/property_contexts
+      {"wc_transport.", "u:object_r:wc_transport_prop:s0", "string", false},
+      {"sys.listeners.", "u:object_r:qseecomtee_prop:s0", "string", false},
+      {"sys.keymaster.", "u:object_r:qseecomtee_prop:s0", "string", false},
+      {"radio.atfwd.", "u:object_r:radio_atfwd_prop:s0", "string", false},
+      {"sys.ims.", "u:object_r:qcom_ims_prop:s0", "string", false},
+      {"sensors.contexthub.", "u:object_r:contexthub_prop:s0", "string", false},
+      {"net.r_rmnet", "u:object_r:net_radio_prop:s0", "string", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "u:object_r:default_prop:s0", "string", &serialized_trie,
+                        &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  auto properties_and_contexts = std::vector<std::pair<std::string, std::string>>{
+      // Actual properties on bullhead via `getprop -Z`
+      {"af.fast_track_multiplier", "u:object_r:default_prop:s0"},
+      {"audio_hal.period_size", "u:object_r:default_prop:s0"},
+      {"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
+      {"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapmaxfree", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapminfree", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapsize", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heapstartsize", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm64.features", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.isa.arm64.variant", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.lockprof.threshold", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.stack-trace-file", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.usejit", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.usejitprofiles", "u:object_r:dalvik_prop:s0"},
+      {"debug.atrace.tags.enableflags", "u:object_r:debug_prop:s0"},
+      {"debug.force_rtl", "u:object_r:debug_prop:s0"},
+      {"dev.bootcomplete", "u:object_r:system_prop:s0"},
+      {"drm.service.enabled", "u:object_r:default_prop:s0"},
+      {"gsm.current.phone-type", "u:object_r:radio_prop:s0"},
+      {"gsm.network.type", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.alpha", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.iso-country", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.isroaming", "u:object_r:radio_prop:s0"},
+      {"gsm.operator.numeric", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.operator.alpha", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.operator.iso-country", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.operator.numeric", "u:object_r:radio_prop:s0"},
+      {"gsm.sim.state", "u:object_r:radio_prop:s0"},
+      {"gsm.version.baseband", "u:object_r:radio_prop:s0"},
+      {"gsm.version.ril-impl", "u:object_r:radio_prop:s0"},
+      {"hwservicemanager.ready", "u:object_r:hwservicemanager_prop:s0"},
+      {"init.svc.adbd", "u:object_r:default_prop:s0"},
+      {"init.svc.atfwd", "u:object_r:default_prop:s0"},
+      {"init.svc.audioserver", "u:object_r:default_prop:s0"},
+      {"init.svc.bootanim", "u:object_r:default_prop:s0"},
+      {"init.svc.bullhead-sh", "u:object_r:default_prop:s0"},
+      {"init.svc.cameraserver", "u:object_r:default_prop:s0"},
+      {"init.svc.cnd", "u:object_r:default_prop:s0"},
+      {"init.svc.cnss-daemon", "u:object_r:default_prop:s0"},
+      {"init.svc.cnss_diag", "u:object_r:default_prop:s0"},
+      {"init.svc.configstore-hal-1-0", "u:object_r:default_prop:s0"},
+      {"init.svc.console", "u:object_r:default_prop:s0"},
+      {"init.svc.devstart_sh", "u:object_r:default_prop:s0"},
+      {"init.svc.drm", "u:object_r:default_prop:s0"},
+      {"init.svc.dumpstate-1-0", "u:object_r:default_prop:s0"},
+      {"init.svc.flash-nanohub-fw", "u:object_r:default_prop:s0"},
+      {"init.svc.fps_hal", "u:object_r:default_prop:s0"},
+      {"init.svc.gatekeeperd", "u:object_r:default_prop:s0"},
+      {"init.svc.gralloc-2-0", "u:object_r:default_prop:s0"},
+      {"init.svc.healthd", "u:object_r:default_prop:s0"},
+      {"init.svc.hidl_memory", "u:object_r:default_prop:s0"},
+      {"init.svc.hostapd", "u:object_r:default_prop:s0"},
+      {"init.svc.hwservicemanager", "u:object_r:default_prop:s0"},
+      {"init.svc.imsdatadaemon", "u:object_r:default_prop:s0"},
+      {"init.svc.imsqmidaemon", "u:object_r:default_prop:s0"},
+      {"init.svc.installd", "u:object_r:default_prop:s0"},
+      {"init.svc.irsc_util", "u:object_r:default_prop:s0"},
+      {"init.svc.keystore", "u:object_r:default_prop:s0"},
+      {"init.svc.lmkd", "u:object_r:default_prop:s0"},
+      {"init.svc.loc_launcher", "u:object_r:default_prop:s0"},
+      {"init.svc.logd", "u:object_r:default_prop:s0"},
+      {"init.svc.logd-reinit", "u:object_r:default_prop:s0"},
+      {"init.svc.media", "u:object_r:default_prop:s0"},
+      {"init.svc.mediadrm", "u:object_r:default_prop:s0"},
+      {"init.svc.mediaextractor", "u:object_r:default_prop:s0"},
+      {"init.svc.mediametrics", "u:object_r:default_prop:s0"},
+      {"init.svc.msm_irqbalance", "u:object_r:default_prop:s0"},
+      {"init.svc.netd", "u:object_r:default_prop:s0"},
+      {"init.svc.netmgrd", "u:object_r:default_prop:s0"},
+      {"init.svc.per_mgr", "u:object_r:default_prop:s0"},
+      {"init.svc.per_proxy", "u:object_r:default_prop:s0"},
+      {"init.svc.perfd", "u:object_r:default_prop:s0"},
+      {"init.svc.qcamerasvr", "u:object_r:default_prop:s0"},
+      {"init.svc.qmuxd", "u:object_r:default_prop:s0"},
+      {"init.svc.qseecomd", "u:object_r:default_prop:s0"},
+      {"init.svc.qti", "u:object_r:default_prop:s0"},
+      {"init.svc.ril-daemon", "u:object_r:default_prop:s0"},
+      {"init.svc.rmt_storage", "u:object_r:default_prop:s0"},
+      {"init.svc.servicemanager", "u:object_r:default_prop:s0"},
+      {"init.svc.ss_ramdump", "u:object_r:default_prop:s0"},
+      {"init.svc.start_hci_filter", "u:object_r:default_prop:s0"},
+      {"init.svc.storaged", "u:object_r:default_prop:s0"},
+      {"init.svc.surfaceflinger", "u:object_r:default_prop:s0"},
+      {"init.svc.thermal-engine", "u:object_r:default_prop:s0"},
+      {"init.svc.time_daemon", "u:object_r:default_prop:s0"},
+      {"init.svc.tombstoned", "u:object_r:default_prop:s0"},
+      {"init.svc.ueventd", "u:object_r:default_prop:s0"},
+      {"init.svc.update_engine", "u:object_r:default_prop:s0"},
+      {"init.svc.usb-hal-1-0", "u:object_r:default_prop:s0"},
+      {"init.svc.vndservicemanager", "u:object_r:default_prop:s0"},
+      {"init.svc.vold", "u:object_r:default_prop:s0"},
+      {"init.svc.webview_zygote32", "u:object_r:default_prop:s0"},
+      {"init.svc.wifi_hal_legacy", "u:object_r:default_prop:s0"},
+      {"init.svc.wificond", "u:object_r:default_prop:s0"},
+      {"init.svc.wpa_supplicant", "u:object_r:default_prop:s0"},
+      {"init.svc.zygote", "u:object_r:default_prop:s0"},
+      {"init.svc.zygote_secondary", "u:object_r:default_prop:s0"},
+      {"keyguard.no_require_sim", "u:object_r:default_prop:s0"},
+      {"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0"},
+      {"logd.logpersistd.enable", "u:object_r:logpersistd_logging_prop:s0"},
+      {"media.aac_51_output_enabled", "u:object_r:default_prop:s0"},
+      {"media.recorder.show_manufacturer_and_model", "u:object_r:default_prop:s0"},
+      {"net.bt.name", "u:object_r:system_prop:s0"},
+      {"net.lte.ims.data.enabled", "u:object_r:net_radio_prop:s0"},
+      {"net.qtaguid_enabled", "u:object_r:system_prop:s0"},
+      {"net.tcp.default_init_rwnd", "u:object_r:system_prop:s0"},
+      {"nfc.initialized", "u:object_r:nfc_prop:s0"},
+      {"persist.audio.fluence.speaker", "u:object_r:audio_prop:s0"},
+      {"persist.audio.fluence.voicecall", "u:object_r:audio_prop:s0"},
+      {"persist.audio.fluence.voicecomm", "u:object_r:audio_prop:s0"},
+      {"persist.audio.fluence.voicerec", "u:object_r:audio_prop:s0"},
+      {"persist.camera.tnr.preview", "u:object_r:default_prop:s0"},
+      {"persist.camera.tnr.video", "u:object_r:default_prop:s0"},
+      {"persist.data.iwlan.enable", "u:object_r:default_prop:s0"},
+      {"persist.hwc.mdpcomp.enable", "u:object_r:default_prop:s0"},
+      {"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0"},
+      {"persist.media.treble_omx", "u:object_r:default_prop:s0"},
+      {"persist.qcril.disable_retry", "u:object_r:default_prop:s0"},
+      {"persist.radio.adb_log_on", "u:object_r:radio_prop:s0"},
+      {"persist.radio.always_send_plmn", "u:object_r:radio_prop:s0"},
+      {"persist.radio.apm_sim_not_pwdn", "u:object_r:radio_prop:s0"},
+      {"persist.radio.custom_ecc", "u:object_r:radio_prop:s0"},
+      {"persist.radio.data_con_rprt", "u:object_r:radio_prop:s0"},
+      {"persist.radio.data_no_toggle", "u:object_r:radio_prop:s0"},
+      {"persist.radio.eons.enabled", "u:object_r:radio_prop:s0"},
+      {"persist.radio.eri64_as_home", "u:object_r:radio_prop:s0"},
+      {"persist.radio.mode_pref_nv10", "u:object_r:radio_prop:s0"},
+      {"persist.radio.process_sups_ind", "u:object_r:radio_prop:s0"},
+      {"persist.radio.redir_party_num", "u:object_r:radio_prop:s0"},
+      {"persist.radio.ril_payload_on", "u:object_r:radio_prop:s0"},
+      {"persist.radio.snapshot_enabled", "u:object_r:radio_prop:s0"},
+      {"persist.radio.snapshot_timer", "u:object_r:radio_prop:s0"},
+      {"persist.radio.use_cc_names", "u:object_r:radio_prop:s0"},
+      {"persist.speaker.prot.enable", "u:object_r:default_prop:s0"},
+      {"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0"},
+      {"persist.sys.dalvik.vm.lib.2", "u:object_r:system_prop:s0"},
+      {"persist.sys.debug.color_temp", "u:object_r:system_prop:s0"},
+      {"persist.sys.preloads.file_cache_expired", "u:object_r:system_prop:s0"},
+      {"persist.sys.timezone", "u:object_r:system_prop:s0"},
+      {"persist.sys.usb.config", "u:object_r:system_prop:s0"},
+      {"persist.sys.webview.vmsize", "u:object_r:system_prop:s0"},
+      {"persist.tom", "u:object_r:default_prop:s0"},
+      {"persist.tom2", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.ab-ota", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.bg-dexopt", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.boot", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.first-boot", "u:object_r:default_prop:s0"},
+      {"pm.dexopt.install", "u:object_r:default_prop:s0"},
+      {"qcom.bluetooth.soc", "u:object_r:default_prop:s0"},
+      {"radio.atfwd.start", "u:object_r:radio_atfwd_prop:s0"},
+      {"ril.ecclist", "u:object_r:radio_prop:s0"},
+      {"ril.nosim.ecc_list_1", "u:object_r:radio_prop:s0"},
+      {"ril.nosim.ecc_list_count", "u:object_r:radio_prop:s0"},
+      {"ril.qcril_pre_init_lock_held", "u:object_r:radio_prop:s0"},
+      {"rild.libpath", "u:object_r:default_prop:s0"},
+      {"ro.allow.mock.location", "u:object_r:default_prop:s0"},
+      {"ro.audio.flinger_standbytime_ms", "u:object_r:default_prop:s0"},
+      {"ro.baseband", "u:object_r:default_prop:s0"},
+      {"ro.bionic.ld.warning", "u:object_r:default_prop:s0"},
+      {"ro.board.platform", "u:object_r:default_prop:s0"},
+      {"ro.boot.baseband", "u:object_r:default_prop:s0"},
+      {"ro.boot.bootloader", "u:object_r:default_prop:s0"},
+      {"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0"},
+      {"ro.boot.dlcomplete", "u:object_r:default_prop:s0"},
+      {"ro.boot.emmc", "u:object_r:default_prop:s0"},
+      {"ro.boot.flash.locked", "u:object_r:default_prop:s0"},
+      {"ro.boot.hardware", "u:object_r:default_prop:s0"},
+      {"ro.boot.hardware.sku", "u:object_r:default_prop:s0"},
+      {"ro.boot.revision", "u:object_r:default_prop:s0"},
+      {"ro.boot.serialno", "u:object_r:serialno_prop:s0"},
+      {"ro.boot.verifiedbootstate", "u:object_r:default_prop:s0"},
+      {"ro.boot.veritymode", "u:object_r:default_prop:s0"},
+      {"ro.boot.wificountrycode", "u:object_r:default_prop:s0"},
+      {"ro.bootimage.build.date", "u:object_r:default_prop:s0"},
+      {"ro.bootimage.build.date.utc", "u:object_r:default_prop:s0"},
+      {"ro.bootimage.build.fingerprint", "u:object_r:default_prop:s0"},
+      {"ro.bootloader", "u:object_r:default_prop:s0"},
+      {"ro.bootmode", "u:object_r:default_prop:s0"},
+      {"ro.boottime.adbd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.atfwd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.audioserver", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.bootanim", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.bullhead-sh", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cameraserver", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cnd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cnss-daemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.cnss_diag", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.configstore-hal-1-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.console", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.devstart_sh", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.drm", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.dumpstate-1-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.flash-nanohub-fw", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.fps_hal", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.gatekeeperd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.gralloc-2-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.healthd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.hidl_memory", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.hwservicemanager", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.imsdatadaemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.imsqmidaemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init.cold_boot_wait", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init.mount_all.default", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.init.selinux", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.installd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.irsc_util", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.keystore", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.lmkd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.loc_launcher", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.logd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.logd-reinit", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.media", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.mediadrm", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.mediaextractor", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.mediametrics", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.msm_irqbalance", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.netd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.netmgrd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.per_mgr", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.per_proxy", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.perfd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qcamerasvr", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qmuxd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qseecomd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.qti", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.ril-daemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.rmt_storage", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.servicemanager", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.ss_ramdump", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.start_hci_filter", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.storaged", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.surfaceflinger", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.thermal-engine", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.time_daemon", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.tombstoned", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.ueventd", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.update_engine", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.usb-hal-1-0", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.vndservicemanager", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.vold", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.webview_zygote32", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.wifi_hal_legacy", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.wificond", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.zygote", "u:object_r:boottime_prop:s0"},
+      {"ro.boottime.zygote_secondary", "u:object_r:boottime_prop:s0"},
+      {"ro.bt.bdaddr_path", "u:object_r:bluetooth_prop:s0"},
+      {"ro.build.characteristics", "u:object_r:default_prop:s0"},
+      {"ro.build.date", "u:object_r:default_prop:s0"},
+      {"ro.build.date.utc", "u:object_r:default_prop:s0"},
+      {"ro.build.description", "u:object_r:default_prop:s0"},
+      {"ro.build.display.id", "u:object_r:default_prop:s0"},
+      {"ro.build.expect.baseband", "u:object_r:default_prop:s0"},
+      {"ro.build.expect.bootloader", "u:object_r:default_prop:s0"},
+      {"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0"},
+      {"ro.build.flavor", "u:object_r:default_prop:s0"},
+      {"ro.build.host", "u:object_r:default_prop:s0"},
+      {"ro.build.id", "u:object_r:default_prop:s0"},
+      {"ro.build.product", "u:object_r:default_prop:s0"},
+      {"ro.build.tags", "u:object_r:default_prop:s0"},
+      {"ro.build.type", "u:object_r:default_prop:s0"},
+      {"ro.build.user", "u:object_r:default_prop:s0"},
+      {"ro.build.version.all_codenames", "u:object_r:default_prop:s0"},
+      {"ro.build.version.base_os", "u:object_r:default_prop:s0"},
+      {"ro.build.version.codename", "u:object_r:default_prop:s0"},
+      {"ro.build.version.incremental", "u:object_r:default_prop:s0"},
+      {"ro.build.version.preview_sdk", "u:object_r:default_prop:s0"},
+      {"ro.build.version.release", "u:object_r:default_prop:s0"},
+      {"ro.build.version.sdk", "u:object_r:default_prop:s0"},
+      {"ro.build.version.security_patch", "u:object_r:default_prop:s0"},
+      {"ro.camera.notify_nfc", "u:object_r:default_prop:s0"},
+      {"ro.carrier", "u:object_r:default_prop:s0"},
+      {"ro.com.android.dataroaming", "u:object_r:default_prop:s0"},
+      {"ro.config.alarm_alert", "u:object_r:config_prop:s0"},
+      {"ro.config.notification_sound", "u:object_r:config_prop:s0"},
+      {"ro.config.ringtone", "u:object_r:config_prop:s0"},
+      {"ro.config.vc_call_vol_steps", "u:object_r:config_prop:s0"},
+      {"ro.crypto.fs_crypto_blkdev", "u:object_r:vold_prop:s0"},
+      {"ro.crypto.state", "u:object_r:vold_prop:s0"},
+      {"ro.crypto.type", "u:object_r:vold_prop:s0"},
+      {"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
+      {"ro.debuggable", "u:object_r:default_prop:s0"},
+      {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+      {"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
+      {"ro.frp.pst", "u:object_r:default_prop:s0"},
+      {"ro.hardware", "u:object_r:default_prop:s0"},
+      {"ro.hwui.drop_shadow_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.gradient_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.layer_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.path_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.r_buffer_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_large_cache_height", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_large_cache_width", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_small_cache_height", "u:object_r:default_prop:s0"},
+      {"ro.hwui.text_small_cache_width", "u:object_r:default_prop:s0"},
+      {"ro.hwui.texture_cache_flushrate", "u:object_r:default_prop:s0"},
+      {"ro.hwui.texture_cache_size", "u:object_r:default_prop:s0"},
+      {"ro.min_freq_0", "u:object_r:default_prop:s0"},
+      {"ro.min_freq_4", "u:object_r:default_prop:s0"},
+      {"ro.oem_unlock_supported", "u:object_r:default_prop:s0"},
+      {"ro.opengles.version", "u:object_r:default_prop:s0"},
+      {"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0"},
+      {"ro.product.board", "u:object_r:default_prop:s0"},
+      {"ro.product.brand", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abi", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abilist", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abilist32", "u:object_r:default_prop:s0"},
+      {"ro.product.cpu.abilist64", "u:object_r:default_prop:s0"},
+      {"ro.product.device", "u:object_r:default_prop:s0"},
+      {"ro.product.first_api_level", "u:object_r:default_prop:s0"},
+      {"ro.product.locale", "u:object_r:default_prop:s0"},
+      {"ro.product.manufacturer", "u:object_r:default_prop:s0"},
+      {"ro.product.model", "u:object_r:default_prop:s0"},
+      {"ro.product.name", "u:object_r:default_prop:s0"},
+      {"ro.property_service.version", "u:object_r:default_prop:s0"},
+      {"ro.qc.sdk.audio.fluencetype", "u:object_r:default_prop:s0"},
+      {"ro.recovery_id", "u:object_r:default_prop:s0"},
+      {"ro.revision", "u:object_r:default_prop:s0"},
+      {"ro.ril.svdo", "u:object_r:radio_prop:s0"},
+      {"ro.ril.svlte1x", "u:object_r:radio_prop:s0"},
+      {"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0"},
+      {"ro.secure", "u:object_r:default_prop:s0"},
+      {"ro.serialno", "u:object_r:serialno_prop:s0"},
+      {"ro.sf.lcd_density", "u:object_r:default_prop:s0"},
+      {"ro.telephony.call_ring.multiple", "u:object_r:default_prop:s0"},
+      {"ro.telephony.default_cdma_sub", "u:object_r:default_prop:s0"},
+      {"ro.telephony.default_network", "u:object_r:default_prop:s0"},
+      {"ro.treble.enabled", "u:object_r:default_prop:s0"},
+      {"ro.vendor.build.date", "u:object_r:default_prop:s0"},
+      {"ro.vendor.build.date.utc", "u:object_r:default_prop:s0"},
+      {"ro.vendor.build.fingerprint", "u:object_r:default_prop:s0"},
+      {"ro.vendor.extension_library", "u:object_r:default_prop:s0"},
+      {"ro.wifi.channels", "u:object_r:default_prop:s0"},
+      {"ro.zygote", "u:object_r:default_prop:s0"},
+      {"security.perf_harden", "u:object_r:shell_prop:s0"},
+      {"sensors.contexthub.lid_state", "u:object_r:contexthub_prop:s0"},
+      {"service.adb.root", "u:object_r:shell_prop:s0"},
+      {"service.bootanim.exit", "u:object_r:system_prop:s0"},
+      {"service.sf.present_timestamp", "u:object_r:system_prop:s0"},
+      {"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0"},
+      {"sys.boot_completed", "u:object_r:system_prop:s0"},
+      {"sys.ims.QMI_DAEMON_STATUS", "u:object_r:qcom_ims_prop:s0"},
+      {"sys.listeners.registered", "u:object_r:qseecomtee_prop:s0"},
+      {"sys.logbootcomplete", "u:object_r:system_prop:s0"},
+      {"sys.oem_unlock_allowed", "u:object_r:system_prop:s0"},
+      {"sys.qcom.devup", "u:object_r:system_prop:s0"},
+      {"sys.sysctl.extra_free_kbytes", "u:object_r:system_prop:s0"},
+      {"sys.usb.config", "u:object_r:system_radio_prop:s0"},
+      {"sys.usb.configfs", "u:object_r:system_radio_prop:s0"},
+      {"sys.usb.controller", "u:object_r:system_prop:s0"},
+      {"sys.usb.ffs.aio_compat", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.ffs.max_read", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.ffs.max_write", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.ffs.ready", "u:object_r:ffs_prop:s0"},
+      {"sys.usb.mtp.device_type", "u:object_r:system_prop:s0"},
+      {"sys.usb.state", "u:object_r:system_prop:s0"},
+      {"telephony.lteOnCdmaDevice", "u:object_r:default_prop:s0"},
+      {"tombstoned.max_tombstone_count", "u:object_r:default_prop:s0"},
+      {"vidc.debug.perf.mode", "u:object_r:default_prop:s0"},
+      {"vidc.enc.dcvs.extra-buff-count", "u:object_r:default_prop:s0"},
+      {"vold.decrypt", "u:object_r:vold_prop:s0"},
+      {"vold.has_adoptable", "u:object_r:vold_prop:s0"},
+      {"vold.post_fs_data_done", "u:object_r:vold_prop:s0"},
+      {"wc_transport.clean_up", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.hci_filter_status", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.ref_count", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.soc_initialized", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.start_hci", "u:object_r:wc_transport_prop:s0"},
+      {"wc_transport.vnd_power", "u:object_r:wc_transport_prop:s0"},
+      {"wifi.interface", "u:object_r:default_prop:s0"},
+      {"wifi.supplicant_scan_interval", "u:object_r:default_prop:s0"},
+  };
+
+  for (const auto& [property, context] : properties_and_contexts) {
+    const char* returned_context;
+    property_info_area->GetPropertyInfo(property.c_str(), &returned_context, nullptr);
+    EXPECT_EQ(context, returned_context) << property;
+  }
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_without_dot) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.radio", "1st", "1st", false},
+      {"persist.radio.something.else.here", "2nd", "2nd", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  const char* context;
+  const char* schema;
+  property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.long.long.long.sub.property", &context,
+                                      &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here2", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here.after", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.nothere", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_with_dot_vs_without) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.", "1st", "1st", false},
+      {"persist.radio", "2nd", "2nd", false},
+      {"persist.radio.long.property.exact.match", "3rd", "3rd", true},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  const char* context;
+  const char* schema;
+  property_info_area->GetPropertyInfo("persist.notradio", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.long.property.prefix.match", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.long.property.exact.match", &context, &schema);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", schema);
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_empty_context_and_schema) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.", "1st", "", false},
+      {"persist.dot_prefix.", "2nd", "", false},
+      {"persist.non_dot_prefix", "3rd", "", false},
+      {"persist.exact_match", "", "", true},
+      {"persist.dot_prefix2.", "", "4th", false},
+      {"persist.non_dot_prefix2", "", "5th", false},
+  };
+
+  auto serialized_trie = std::string();
+  auto build_trie_error = std::string();
+  ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
+      << build_trie_error;
+
+  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
+
+  const char* context;
+  const char* schema;
+  property_info_area->GetPropertyInfo("notpersist.radio.something", &context, &schema);
+  EXPECT_STREQ("default", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("persist.nomatch", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("persist.dot_prefix.something", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("persist.non_dot_prefix.something", &context, &schema);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("persist.exact_match", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("default", schema);
+  property_info_area->GetPropertyInfo("persist.dot_prefix2.something", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("4th", schema);
+  property_info_area->GetPropertyInfo("persist.non_dot_prefix2.something", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("5th", schema);
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_builder.cpp b/property_service/libpropertyinfoserializer/trie_builder.cpp
new file mode 100644
index 0000000..feb753b
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.cpp
@@ -0,0 +1,105 @@
+//
+// Copyright (C) 2017 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 "trie_builder.h"
+
+#include <android-base/strings.h>
+
+using android::base::Split;
+
+namespace android {
+namespace properties {
+
+TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_schema)
+    : builder_root_("root") {
+  auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
+  builder_root_.set_context(context_pointer);
+  auto* schema_pointer = StringPointerFromContainer(default_schema, &schemas_);
+  builder_root_.set_schema(schema_pointer);
+}
+
+bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
+                            const std::string& schema, bool exact, std::string* error) {
+  auto* context_pointer = StringPointerFromContainer(context, &contexts_);
+  auto* schema_pointer = StringPointerFromContainer(schema, &schemas_);
+  return AddToTrie(name, context_pointer, schema_pointer, exact, error);
+}
+
+bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
+                            const std::string* schema, bool exact, std::string* error) {
+  TrieBuilderNode* current_node = &builder_root_;
+
+  auto name_pieces = Split(name, ".");
+
+  bool ends_with_dot = false;
+  if (name_pieces.back().empty()) {
+    ends_with_dot = true;
+    name_pieces.pop_back();
+  }
+
+  // Move us to the final node that we care about, adding incremental nodes if necessary.
+  while (name_pieces.size() > 1) {
+    auto child = current_node->FindChild(name_pieces.front());
+    if (child == nullptr) {
+      child = current_node->AddChild(name_pieces.front());
+    }
+    if (child == nullptr) {
+      *error = "Unable to allocate Trie node";
+      return false;
+    }
+    current_node = child;
+    name_pieces.erase(name_pieces.begin());
+  }
+
+  // Store our context based on what type of match it is.
+  if (exact) {
+    if (!current_node->AddExactMatchContext(name_pieces.front(), context, schema)) {
+      *error = "Duplicate exact match detected for '" + name + "'";
+      return false;
+    }
+  } else if (!ends_with_dot) {
+    if (!current_node->AddPrefixContext(name_pieces.front(), context, schema)) {
+      *error = "Duplicate prefix match detected for '" + name + "'";
+      return false;
+    }
+  } else {
+    auto child = current_node->FindChild(name_pieces.front());
+    if (child == nullptr) {
+      child = current_node->AddChild(name_pieces.front());
+    }
+    if (child == nullptr) {
+      *error = "Unable to allocate Trie node";
+      return false;
+    }
+    if (child->context() != nullptr || child->schema() != nullptr) {
+      *error = "Duplicate prefix match detected for '" + name + "'";
+      return false;
+    }
+    child->set_context(context);
+    child->set_schema(schema);
+  }
+  return true;
+}
+
+const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,
+                                                           std::set<std::string>* container) {
+  // Get a pointer to the string in a given set, such that we only ever serialize each string once.
+  auto [iterator, _] = container->emplace(string);
+  return &(*iterator);
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_builder.h b/property_service/libpropertyinfoserializer/trie_builder.h
new file mode 100644
index 0000000..f928e76
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.h
@@ -0,0 +1,124 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+struct PropertyEntryBuilder {
+  PropertyEntryBuilder() : context(nullptr), schema(nullptr) {}
+  PropertyEntryBuilder(const std::string& name, const std::string* context,
+                       const std::string* schema)
+      : name(name), context(context), schema(schema) {}
+  std::string name;
+  const std::string* context;
+  const std::string* schema;
+};
+
+class TrieBuilderNode {
+ public:
+  TrieBuilderNode(const std::string& name) : property_entry_(name, nullptr, nullptr) {}
+
+  TrieBuilderNode* FindChild(const std::string& name) {
+    for (auto& child : children_) {
+      if (child.name() == name) return &child;
+    }
+    return nullptr;
+  }
+
+  const TrieBuilderNode* FindChild(const std::string& name) const {
+    for (const auto& child : children_) {
+      if (child.name() == name) return &child;
+    }
+    return nullptr;
+  }
+
+  TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
+
+  bool AddPrefixContext(const std::string& prefix, const std::string* context,
+                        const std::string* schema) {
+    if (std::find_if(prefixes_.begin(), prefixes_.end(),
+                     [&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {
+      return false;
+    }
+
+    prefixes_.emplace_back(prefix, context, schema);
+    return true;
+  }
+
+  bool AddExactMatchContext(const std::string& exact_match, const std::string* context,
+                            const std::string* schema) {
+    if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {
+          return t.name == exact_match;
+        }) != exact_matches_.end()) {
+      return false;
+    }
+
+    exact_matches_.emplace_back(exact_match, context, schema);
+    return true;
+  }
+
+  const std::string& name() const { return property_entry_.name; }
+  const std::string* context() const { return property_entry_.context; }
+  void set_context(const std::string* context) { property_entry_.context = context; }
+  const std::string* schema() const { return property_entry_.schema; }
+  void set_schema(const std::string* schema) { property_entry_.schema = schema; }
+
+  const PropertyEntryBuilder property_entry() const { return property_entry_; }
+
+  const std::vector<TrieBuilderNode>& children() const { return children_; }
+  const std::vector<PropertyEntryBuilder>& prefixes() const { return prefixes_; }
+  const std::vector<PropertyEntryBuilder>& exact_matches() const { return exact_matches_; }
+
+ private:
+  PropertyEntryBuilder property_entry_;
+  std::vector<TrieBuilderNode> children_;
+  std::vector<PropertyEntryBuilder> prefixes_;
+  std::vector<PropertyEntryBuilder> exact_matches_;
+};
+
+class TrieBuilder {
+ public:
+  TrieBuilder(const std::string& default_context, const std::string& default_schema);
+  bool AddToTrie(const std::string& name, const std::string& context, const std::string& schema,
+                 bool exact, std::string* error);
+
+  const TrieBuilderNode builder_root() const { return builder_root_; }
+  const std::set<std::string>& contexts() const { return contexts_; }
+  const std::set<std::string>& schemas() const { return schemas_; }
+
+ private:
+  bool AddToTrie(const std::string& name, const std::string* context, const std::string* schema,
+                 bool exact, std::string* error);
+  const std::string* StringPointerFromContainer(const std::string& string,
+                                                std::set<std::string>* container);
+
+  TrieBuilderNode builder_root_;
+  std::set<std::string> contexts_;
+  std::set<std::string> schemas_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_builder_test.cpp b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
new file mode 100644
index 0000000..2b948f3
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
@@ -0,0 +1,129 @@
+//
+// Copyright (C) 2017 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 "trie_builder.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace properties {
+
+TEST(propertyinfoserializer, BuildTrie_Simple) {
+  auto trie_builder = TrieBuilder("default", "default_schema");
+
+  // Add test data to tree
+  auto error = std::string();
+  EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_schema", false, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_schema", false, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_schema", true, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_schema", true, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_schema", true, &error));
+  EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_schema", true, &error));
+
+  ASSERT_EQ(5U, trie_builder.contexts().size());
+  ASSERT_EQ(5U, trie_builder.schemas().size());
+
+  auto& builder_root = trie_builder.builder_root();
+
+  // Check the root node
+  EXPECT_EQ("root", builder_root.name());
+  ASSERT_NE(nullptr, builder_root.context());
+  EXPECT_EQ("default", *builder_root.context());
+  ASSERT_NE(nullptr, builder_root.schema());
+  EXPECT_EQ("default_schema", *builder_root.schema());
+
+  EXPECT_EQ(0U, builder_root.prefixes().size());
+  EXPECT_EQ(0U, builder_root.exact_matches().size());
+
+  ASSERT_EQ(2U, builder_root.children().size());
+
+  // Check the 'test.' node
+  auto* test_node = builder_root.FindChild("test");
+  EXPECT_EQ("test", test_node->name());
+  ASSERT_NE(nullptr, test_node->context());
+  EXPECT_EQ("1st", *test_node->context());
+  ASSERT_NE(nullptr, test_node->schema());
+  EXPECT_EQ("1st_schema", *test_node->schema());
+
+  EXPECT_EQ(0U, test_node->children().size());
+  EXPECT_EQ(1U, test_node->prefixes().size());
+  {
+    auto& property_entry = test_node->prefixes()[0];
+    EXPECT_EQ("test", property_entry.name);
+    ASSERT_NE(nullptr, property_entry.context);
+    EXPECT_EQ("2nd", *property_entry.context);
+    ASSERT_NE(nullptr, property_entry.schema);
+    EXPECT_EQ("2nd_schema", *property_entry.schema);
+  }
+  EXPECT_EQ(3U, test_node->exact_matches().size());
+  EXPECT_EQ("test1", test_node->exact_matches()[0].name);
+  EXPECT_EQ("test2", test_node->exact_matches()[1].name);
+  EXPECT_EQ("test3", test_node->exact_matches()[2].name);
+
+  ASSERT_NE(nullptr, test_node->exact_matches()[0].context);
+  ASSERT_NE(nullptr, test_node->exact_matches()[1].context);
+  ASSERT_NE(nullptr, test_node->exact_matches()[2].context);
+  EXPECT_EQ("3rd", *test_node->exact_matches()[0].context);
+  EXPECT_EQ("3rd", *test_node->exact_matches()[1].context);
+  EXPECT_EQ("3rd", *test_node->exact_matches()[2].context);
+
+  ASSERT_NE(nullptr, test_node->exact_matches()[0].schema);
+  ASSERT_NE(nullptr, test_node->exact_matches()[1].schema);
+  ASSERT_NE(nullptr, test_node->exact_matches()[2].schema);
+  EXPECT_EQ("3rd_schema", *test_node->exact_matches()[0].schema);
+  EXPECT_EQ("3rd_schema", *test_node->exact_matches()[1].schema);
+  EXPECT_EQ("3rd_schema", *test_node->exact_matches()[2].schema);
+
+  // Check the long string node
+  auto expect_empty_one_child = [](auto* node) {
+    ASSERT_NE(nullptr, node);
+    EXPECT_EQ(nullptr, node->context());
+    EXPECT_EQ(nullptr, node->schema());
+    EXPECT_EQ(0U, node->prefixes().size());
+    EXPECT_EQ(0U, node->exact_matches().size());
+    EXPECT_EQ(1U, node->children().size());
+  };
+
+  // Start with 'this'
+  auto* long_string_node = builder_root.FindChild("this");
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'is'
+  long_string_node = long_string_node->FindChild("is");
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'a'
+  long_string_node = long_string_node->FindChild("a");
+  expect_empty_one_child(long_string_node);
+
+  // Move to 'long'
+  long_string_node = long_string_node->FindChild("long");
+  EXPECT_EQ(0U, long_string_node->prefixes().size());
+  EXPECT_EQ(1U, long_string_node->exact_matches().size());
+  EXPECT_EQ(0U, long_string_node->children().size());
+
+  {
+    auto& property_entry = long_string_node->exact_matches()[0];
+    EXPECT_EQ("string", property_entry.name);
+    ASSERT_NE(nullptr, property_entry.context);
+    EXPECT_EQ("4th", *property_entry.context);
+    ASSERT_NE(nullptr, property_entry.schema);
+    EXPECT_EQ("4th_schema", *property_entry.schema);
+  }
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_node_arena.h b/property_service/libpropertyinfoserializer/trie_node_arena.h
new file mode 100644
index 0000000..5e0ef82
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_node_arena.h
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace properties {
+
+template <typename T>
+class ArenaObjectPointer {
+ public:
+  ArenaObjectPointer(std::string& arena_data, uint32_t offset)
+      : arena_data_(arena_data), offset_(offset) {}
+
+  T* operator->() { return reinterpret_cast<T*>(arena_data_.data() + offset_); }
+
+ private:
+  std::string& arena_data_;
+  uint32_t offset_;
+};
+
+class TrieNodeArena {
+ public:
+  TrieNodeArena() : current_data_pointer_(0) {}
+
+  // We can't return pointers to objects since data_ may move when reallocated, thus invalidating
+  // any pointers.  Therefore we return an ArenaObjectPointer, which always accesses elements via
+  // data_ + offset.
+  template <typename T>
+  ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {
+    uint32_t offset;
+    AllocateData(sizeof(T), &offset);
+    if (return_offset) *return_offset = offset;
+    return ArenaObjectPointer<T>(data_, offset);
+  }
+
+  uint32_t AllocateUint32Array(int length) {
+    uint32_t offset;
+    AllocateData(sizeof(uint32_t) * length, &offset);
+    return offset;
+  }
+
+  uint32_t* uint32_array(uint32_t offset) {
+    return reinterpret_cast<uint32_t*>(data_.data() + offset);
+  }
+
+  uint32_t AllocateAndWriteString(const std::string& string) {
+    uint32_t offset;
+    char* data = static_cast<char*>(AllocateData(string.size() + 1, &offset));
+    strcpy(data, string.c_str());
+    return offset;
+  }
+
+  void AllocateAndWriteUint32(uint32_t value) {
+    auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
+    *location = value;
+  }
+
+  void* AllocateData(size_t size, uint32_t* offset) {
+    size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
+
+    if (current_data_pointer_ + aligned_size > data_.size()) {
+      auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;
+      data_.resize(new_size, '\0');
+    }
+    if (offset) *offset = current_data_pointer_;
+
+    uint32_t return_offset = current_data_pointer_;
+    current_data_pointer_ += aligned_size;
+    return &data_[0] + return_offset;
+  }
+
+  uint32_t size() const { return current_data_pointer_; }
+
+  const std::string& data() const { return data_; }
+
+  std::string truncated_data() const {
+    auto result = data_;
+    result.resize(current_data_pointer_);
+    return result;
+  }
+
+ private:
+  std::string data_;
+  uint32_t current_data_pointer_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.cpp b/property_service/libpropertyinfoserializer/trie_serializer.cpp
new file mode 100644
index 0000000..5326537
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.cpp
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2017 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 "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+// Serialized strings contains:
+// 1) A uint32_t count of elements in the below array
+// 2) A sorted array of uint32_t offsets pointing to null terminated strings
+// 3) Each of the null terminated strings themselves packed back to back
+// This returns the offset into arena where the serialized strings start.
+void TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {
+  arena_->AllocateAndWriteUint32(strings.size());
+
+  // Allocate space for the array.
+  uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());
+
+  // Write offset pointers and strings; these are already alphabetically sorted by virtue of being
+  // in an std::set.
+  auto it = strings.begin();
+  for (unsigned int i = 0; i < strings.size(); ++i, ++it) {
+    uint32_t string_offset = arena_->AllocateAndWriteString(*it);
+    arena_->uint32_array(offset_array_offset)[i] = string_offset;
+  }
+}
+
+uint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {
+  uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
+                               ? serialized_info()->FindContextIndex(property_entry.context->c_str())
+                               : ~0u;
+  uint32_t schema_index = property_entry.schema != nullptr && !property_entry.schema->empty()
+                              ? serialized_info()->FindSchemaIndex(property_entry.schema->c_str())
+                              : ~0u;
+  uint32_t offset;
+  auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
+  serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
+  serialized_property_entry->namelen = property_entry.name.size();
+  serialized_property_entry->context_index = context_index;
+  serialized_property_entry->schema_index = schema_index;
+  return offset;
+}
+
+uint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {
+  uint32_t trie_offset;
+  auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);
+
+  trie->property_entry = WritePropertyEntry(builder_node.property_entry());
+
+  // Write prefix matches
+  auto sorted_prefix_matches = builder_node.prefixes();
+  // Prefixes are sorted by descending length
+  std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });
+
+  trie->num_prefixes = sorted_prefix_matches.size();
+
+  uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());
+  trie->prefix_entries = prefix_entries_array_offset;
+
+  for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {
+    uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);
+    arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;
+  }
+
+  // Write exact matches
+  auto sorted_exact_matches = builder_node.exact_matches();
+  // Exact matches are sorted alphabetically
+  std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });
+
+  trie->num_exact_matches = sorted_exact_matches.size();
+
+  uint32_t exact_match_entries_array_offset =
+      arena_->AllocateUint32Array(sorted_exact_matches.size());
+  trie->exact_match_entries = exact_match_entries_array_offset;
+
+  for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {
+    uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);
+    arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;
+  }
+
+  // Write children
+  auto sorted_children = builder_node.children();
+  std::sort(sorted_children.begin(), sorted_children.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });
+
+  trie->num_child_nodes = sorted_children.size();
+  uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());
+  trie->child_nodes = children_offset_array_offset;
+
+  for (unsigned int i = 0; i < sorted_children.size(); ++i) {
+    arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);
+  }
+  return trie_offset;
+}
+
+TrieSerializer::TrieSerializer() {}
+
+std::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {
+  arena_.reset(new TrieNodeArena());
+
+  auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);
+  header->current_version = 1;
+  header->minimum_supported_version = 1;
+
+  // Store where we're about to write the contexts.
+  header->contexts_offset = arena_->size();
+  SerializeStrings(trie_builder.contexts());
+
+  // Store where we're about to write the schemas.
+  header->schemas_offset = arena_->size();
+  SerializeStrings(trie_builder.schemas());
+
+  // We need to store size() up to this point now for Find*Offset() to work.
+  header->size = arena_->size();
+
+  uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());
+  header->root_offset = root_trie_offset;
+
+  // Record the real size now that we've written everything
+  header->size = arena_->size();
+
+  return arena_->truncated_data();
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.h b/property_service/libpropertyinfoserializer/trie_serializer.h
new file mode 100644
index 0000000..e4d3343
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2017 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 PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+
+#include <string>
+#include <vector>
+
+#include "property_info_parser/property_info_parser.h"
+
+#include "trie_builder.h"
+#include "trie_node_arena.h"
+
+namespace android {
+namespace properties {
+
+class TrieSerializer {
+ public:
+  TrieSerializer();
+
+  std::string SerializeTrie(const TrieBuilder& trie_builder);
+
+ private:
+  void SerializeStrings(const std::set<std::string>& strings);
+  uint32_t WritePropertyEntry(const PropertyEntryBuilder& property_entry);
+
+  // Writes a new TrieNode to arena, and recursively writes its children.
+  // Returns the offset within arena.
+  uint32_t WriteTrieNode(const TrieBuilderNode& builder_node);
+
+  const PropertyInfoArea* serialized_info() const {
+    return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
+  }
+
+  std::unique_ptr<TrieNodeArena> arena_;
+};
+
+}  // namespace properties
+}  // namespace android
+
+#endif
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 777fdb0..492d63a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -36,70 +36,6 @@
 
 include $(BUILD_PREBUILT)
 
-# Modules for asan.options.X files.
-
-ASAN_OPTIONS_FILES :=
-
-define create-asan-options-module
-include $$(CLEAR_VARS)
-LOCAL_MODULE := asan.options.$(1)
-ASAN_OPTIONS_FILES += asan.options.$(1)
-LOCAL_MODULE_CLASS := ETC
-# The asan.options.off.template tries to turn off as much of ASAN as is possible.
-LOCAL_SRC_FILES := asan.options.off.template
-LOCAL_MODULE_PATH := $(TARGET_OUT)
-include $$(BUILD_PREBUILT)
-endef
-
-# Pretty comprehensive set of native services. This list is helpful if all that's to be checked is an
-# app.
-ifeq ($(SANITIZE_LITE_SERVICES),true)
-SANITIZE_ASAN_OPTIONS_FOR := \
-  adbd \
-  ATFWD-daemon \
-  audioserver \
-  bridgemgrd \
-  cameraserver \
-  cnd \
-  debuggerd \
-  dex2oat \
-  drmserver \
-  fingerprintd \
-  gatekeeperd \
-  installd \
-  keystore \
-  lmkd \
-  logcat \
-  logd \
-  lowi-server \
-  media.codec \
-  mediadrmserver \
-  media.extractor \
-  mediaserver \
-  mm-qcamera-daemon \
-  mpdecision \
-  netmgrd \
-  perfd \
-  perfprofd \
-  qmuxd \
-  qseecomd \
-  rild \
-  sdcard \
-  servicemanager \
-  slim_daemon \
-  surfaceflinger \
-  thermal-engine \
-  time_daemon \
-  update_engine \
-  vold \
-  wpa_supplicant \
-  zip
-endif
-
-ifneq ($(SANITIZE_ASAN_OPTIONS_FOR),)
-  $(foreach binary, $(SANITIZE_ASAN_OPTIONS_FOR), $(eval $(call create-asan-options-module,$(binary))))
-endif
-
 # ASAN extration.
 ASAN_EXTRACT_FILES :=
 ifeq ($(SANITIZE_TARGET_SYSTEM),true)
@@ -189,6 +125,18 @@
 bcp_md5 :=
 bcp_dep :=
 
+# If PLATFORM_VNDK_VERSION is defined and not "current", generate versioned
+# module names for ld.config.txt, llndk.libraries.txt and vndksp.libraries.txt
+# files.
+define versioned_module_name
+$(strip \
+  $(if $(filter-out current,$(PLATFORM_VNDK_VERSION)), \
+    $(basename $(LOCAL_MODULE)).$(PLATFORM_VNDK_VERSION)$(suffix $(LOCAL_MODULE)), \
+    $(LOCAL_MODULE) \
+  ) \
+)
+endef
+
 #######################################
 # ld.config.txt
 include $(CLEAR_VARS)
@@ -205,22 +153,22 @@
 LOCAL_MODULE := ld.config.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
 include $(BUILD_SYSTEM)/base_rules.mk
 
-llndk_libraries := $(subst $(space),:,$(addsuffix .so,\
+llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
 $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
 
-private_llndk_libraries := $(subst $(space),:,$(addsuffix .so,\
+private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
 $(filter $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
 
-vndk_sameprocess_libraries := $(subst $(space),:,$(addsuffix .so,\
+vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
 $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_SAMEPROCESS_LIBRARIES))))
 
-vndk_core_libraries := $(subst $(space),:,$(addsuffix .so,\
+vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
 $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
 
-sanitizer_runtime_libraries := $(subst $(space),:,$(addsuffix .so,\
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
 $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
 $(UBSAN_RUNTIME_LIBRARY) \
 $(TSAN_RUNTIME_LIBRARY) \
@@ -251,13 +199,14 @@
 
 LOCAL_MODULE := ld.config.txt
 ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
-LOCAL_SRC_FILES := etc/ld.config.txt
+  LOCAL_SRC_FILES := etc/ld.config.txt
+  LOCAL_MODULE_STEM := $(call versioned_module_name)
 else
-LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+  LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+  LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 endif
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
 endif
 
@@ -267,7 +216,7 @@
 LOCAL_MODULE := llndk.libraries.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
 include $(BUILD_SYSTEM)/base_rules.mk
 $(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
 $(LOCAL_BUILT_MODULE):
@@ -283,7 +232,7 @@
 LOCAL_MODULE := vndksp.libraries.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
 include $(BUILD_SYSTEM)/base_rules.mk
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
 $(LOCAL_BUILT_MODULE):
diff --git a/rootdir/asan.options.off.template b/rootdir/asan.options.off.template
deleted file mode 100644
index 59a1249..0000000
--- a/rootdir/asan.options.off.template
+++ /dev/null
@@ -1,7 +0,0 @@
-quarantine_size_mb=0
-max_redzone=16
-poison_heap=false
-poison_partial=false
-poison_array_cookie=false
-alloc_dealloc_mismatch=false
-new_delete_type_mismatch=false
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 4d058db..d55ec57 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -12,5 +12,14 @@
 
 [legacy]
 namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}:/vendor/${LIB}:/odm/${LIB}
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/odm/${LIB}:/odm/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 3183b80..b86104d 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -3,9 +3,11 @@
 # Bionic loader config file.
 #
 
-# Don't change the order here.
+# Don't change the order here. The first pattern that matches with the
+# absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
 dir.vendor = /data/nativetest/odm
@@ -16,6 +18,7 @@
 dir.vendor = /data/nativetest64/vendor
 dir.vendor = /data/benchmarktest/vendor
 dir.vendor = /data/benchmarktest64/vendor
+
 dir.system = /data/nativetest
 dir.system = /data/nativetest64
 dir.system = /data/benchmarktest
@@ -27,25 +30,21 @@
 ###############################################################################
 # "default" namespace
 #
-# Framework-side code runs in this namespace. Anything from /vendor partition
-# can't be loaded in this namespace.
+# Framework-side code runs in this namespace. However, libs from other
+# partitions are also allowed temporarily.
 ###############################################################################
 namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}:/odm/${LIB}:/vendor/${LIB}
-namespace.default.permitted.paths = /system/${LIB}:/odm/${LIB}:/vendor/${LIB}
 
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
 
-# TODO(b/37013858): remove all dependencies to /vendor/lib from system processes
-# When this is done, comment out following three lines and remove the three
-# lines above
-#namespace.default.isolated = true
-#namespace.default.search.paths = /system/${LIB}
-#namespace.default.permitted.paths = /system/${LIB}
-#
-#namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-#namespace.default.asan.permitted.paths = /data/asan/system/${LIB}:/system/${LIB}
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
 
 ###############################################################################
 # "sphal" namespace
@@ -62,21 +61,56 @@
 ###############################################################################
 namespace.sphal.isolated = true
 namespace.sphal.visible = true
-namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
-namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp${VNDK_VER}/hw
 
-namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths  = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths +=           /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
 namespace.sphal.links = default,vndk,rs
 
 # WARNING: only NDK libs can be listed here.
-namespace.sphal.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so:libz.so
+namespace.sphal.link.default.shared_libs  = libc.so
+namespace.sphal.link.default.shared_libs += libEGL.so
+namespace.sphal.link.default.shared_libs += libGLESv1_CM.so
+namespace.sphal.link.default.shared_libs += libGLESv2.so
+namespace.sphal.link.default.shared_libs += libdl.so
+namespace.sphal.link.default.shared_libs += liblog.so
+namespace.sphal.link.default.shared_libs += libm.so
+namespace.sphal.link.default.shared_libs += libnativewindow.so
+namespace.sphal.link.default.shared_libs += libstdc++.so
+namespace.sphal.link.default.shared_libs += libsync.so
+namespace.sphal.link.default.shared_libs += libvndksupport.so
+namespace.sphal.link.default.shared_libs += libz.so
 
 # WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
-namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+namespace.sphal.link.vndk.shared_libs  = android.hardware.renderscript@1.0.so
+namespace.sphal.link.vndk.shared_libs += android.hardware.graphics.common@1.0.so
+namespace.sphal.link.vndk.shared_libs += android.hardware.graphics.mapper@2.0.so
+namespace.sphal.link.vndk.shared_libs += android.hidl.memory@1.0.so
+namespace.sphal.link.vndk.shared_libs += libbase.so
+namespace.sphal.link.vndk.shared_libs += libc++.so
+namespace.sphal.link.vndk.shared_libs += libcutils.so
+namespace.sphal.link.vndk.shared_libs += libhardware.so
+namespace.sphal.link.vndk.shared_libs += libhidlbase.so
+namespace.sphal.link.vndk.shared_libs += libhidlmemory.so
+namespace.sphal.link.vndk.shared_libs += libhidltransport.so
+namespace.sphal.link.vndk.shared_libs += libhwbinder.so
+namespace.sphal.link.vndk.shared_libs += libion.so
+namespace.sphal.link.vndk.shared_libs += libutils.so
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -91,15 +125,68 @@
 ###############################################################################
 namespace.rs.isolated = true
 namespace.rs.visible = true
-namespace.rs.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}
-namespace.rs.permitted.paths = /vendor/${LIB}:/data
 
-namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
+namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
+namespace.rs.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths  = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths +=           /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths +=           /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths +=           /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
 
 namespace.rs.links = default,vndk
-namespace.rs.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libvndksupport.so:libz.so:libft2.so
-namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+
+namespace.rs.link.default.shared_libs  = libc.so
+namespace.rs.link.default.shared_libs += libEGL.so
+namespace.rs.link.default.shared_libs += libGLESv1_CM.so
+namespace.rs.link.default.shared_libs += libGLESv2.so
+namespace.rs.link.default.shared_libs += libdl.so
+namespace.rs.link.default.shared_libs += liblog.so
+namespace.rs.link.default.shared_libs += libm.so
+namespace.rs.link.default.shared_libs += libnativewindow.so
+namespace.rs.link.default.shared_libs += libstdc++.so
+namespace.rs.link.default.shared_libs += libsync.so
+namespace.rs.link.default.shared_libs += libvndksupport.so
+namespace.rs.link.default.shared_libs += libz.so
+# These two libs are private LLNDK libs but are exceptionally visible
+# in this 'rs' namespace because RenderScript framework libraries
+# which are loaded into this namespace are using them.
+namespace.rs.link.default.shared_libs += libft2.so
+namespace.rs.link.default.shared_libs += libmediandk.so
+
+namespace.rs.link.vndk.shared_libs  = android.hardware.renderscript@1.0.so
+namespace.rs.link.vndk.shared_libs += android.hardware.graphics.common@1.0.so
+namespace.rs.link.vndk.shared_libs += android.hardware.graphics.mapper@2.0.so
+namespace.rs.link.vndk.shared_libs += android.hidl.memory@1.0.so
+namespace.rs.link.vndk.shared_libs += libbase.so
+namespace.rs.link.vndk.shared_libs += libc++.so
+namespace.rs.link.vndk.shared_libs += libcutils.so
+namespace.rs.link.vndk.shared_libs += libhardware.so
+namespace.rs.link.vndk.shared_libs += libhidlbase.so
+namespace.rs.link.vndk.shared_libs += libhidlmemory.so
+namespace.rs.link.vndk.shared_libs += libhidltransport.so
+namespace.rs.link.vndk.shared_libs += libhwbinder.so
+namespace.rs.link.vndk.shared_libs += libion.so
+namespace.rs.link.vndk.shared_libs += libutils.so
 
 ###############################################################################
 # "vndk" namespace
@@ -108,17 +195,47 @@
 ###############################################################################
 namespace.vndk.isolated = true
 namespace.vndk.visible = true
-namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
 
-namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
+namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.permitted.paths  = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
 
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
 namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs = android.hidl.memory@1.0-impl.so:libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so:libz.so
+namespace.vndk.link.default.shared_libs  = android.hidl.memory@1.0-impl.so
+namespace.vndk.link.default.shared_libs += libEGL.so
+namespace.vndk.link.default.shared_libs += libc.so
+namespace.vndk.link.default.shared_libs += libdl.so
+namespace.vndk.link.default.shared_libs += liblog.so
+namespace.vndk.link.default.shared_libs += libm.so
+namespace.vndk.link.default.shared_libs += libnativewindow.so
+namespace.vndk.link.default.shared_libs += libstdc++.so
+namespace.vndk.link.default.shared_libs += libsync.so
+namespace.vndk.link.default.shared_libs += libvndksupport.so
+namespace.vndk.link.default.shared_libs += libz.so
 
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
@@ -128,6 +245,34 @@
 ###############################################################################
 [vendor]
 namespace.default.isolated = false
-namespace.default.search.paths = /odm/${LIB}/hw:/odm/${LIB}/egl:/odm/${LIB}:/vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/system/${LIB}/vndk${VNDK_VER}:/odm/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}
 
-namespace.default.asan.search.paths = /data/asan/odm/${LIB}/hw:/odm/${LIB}/hw:/data/asan/odm/${LIB}/egl:/odm/${LIB}/egl:/data/asan/odm/${LIB}:/odm/${LIB}:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/odm/${LIB}/vndk-sp${VNDK_VER}:/odm/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}:/system/${LIB}
+namespace.default.search.paths  = /odm/${LIB}
+namespace.default.search.paths += /odm/${LIB}/vndk
+namespace.default.search.paths += /odm/${LIB}/vndk-sp
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /vendor/${LIB}/vndk
+namespace.default.search.paths += /vendor/${LIB}/vndk-sp
+
+# Access to system libraries are allowed
+namespace.default.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.default.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.search.paths += /system/${LIB}
+
+namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index 2d05fca..df26f90 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -7,11 +7,18 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
+
+dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
 dir.vendor = /data/nativetest/vendor
 dir.vendor = /data/nativetest64/vendor
 dir.vendor = /data/benchmarktest/vendor
 dir.vendor = /data/benchmarktest64/vendor
+
 dir.system = /data/nativetest
 dir.system = /data/nativetest64
 dir.system = /data/benchmarktest
@@ -27,13 +34,45 @@
 # can't be loaded in this namespace.
 ###############################################################################
 namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
-# /vendor/app, /vendor/framework were added since libart should be able to dlopen
-# the odex files from the directory.
-namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
 
-namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
+namespace.default.search.paths = /system/${LIB}
+
+# We can't have entire /system/${LIB} as permitted paths because doing so
+# makes it possible to load libs in /system/${LIB}/vndk* directories by
+# their absolute paths (e.g. dlopen("/system/lib/vndk/libbase.so");).
+# VNDK libs are built with previous versions of Android and thus must not be
+# loaded into this namespace where libs built with the current version of
+# Android are loaded. Mixing the two types of libs in the same namespace can
+# cause unexpected problem.
+namespace.default.permitted.paths  = /system/${LIB}/drm
+namespace.default.permitted.paths += /system/${LIB}/extractors
+namespace.default.permitted.paths += /system/${LIB}/hw
+# These are where odex files are located. libart has to be able to dlopen the files
+namespace.default.permitted.paths += /system/framework
+namespace.default.permitted.paths += /system/app
+namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /vendor/framework
+namespace.default.permitted.paths += /vendor/app
+namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /oem/app
+namespace.default.permitted.paths += /data
+namespace.default.permitted.paths += /mnt/expand
+
+namespace.default.asan.search.paths  = /data/asan/system/${LIB}
+namespace.default.asan.search.paths +=           /system/${LIB}
+
+namespace.default.asan.permitted.paths  = /data
+namespace.default.asan.permitted.paths += /system/${LIB}/drm
+namespace.default.asan.permitted.paths += /system/${LIB}/extractors
+namespace.default.asan.permitted.paths += /system/${LIB}/hw
+namespace.default.asan.permitted.paths += /system/framework
+namespace.default.asan.permitted.paths += /system/app
+namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /vendor/framework
+namespace.default.asan.permitted.paths += /vendor/app
+namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /mnt/expand
 
 ###############################################################################
 # "sphal" namespace
@@ -50,20 +89,30 @@
 ###############################################################################
 namespace.sphal.isolated = true
 namespace.sphal.visible = true
-namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
-namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp${VNDK_VER}/hw
 
-namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+
+namespace.sphal.permitted.paths  = /odm/${LIB}
+namespace.sphal.permitted.paths += /vendor/${LIB}
+
+namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.search.paths +=           /odm/${LIB}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
 namespace.sphal.links = default,vndk,rs
 
-# WARNING: only NDK libs can be listed here.
-namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES%
+namespace.sphal.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
-# WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
 namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
 # Renderscript gets separate namespace
@@ -79,17 +128,42 @@
 ###############################################################################
 namespace.rs.isolated = true
 namespace.rs.visible = true
-namespace.rs.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}
-namespace.rs.permitted.paths = /vendor/${LIB}:/data
 
-namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}:/vendor/${LIB}
-namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
+namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
+namespace.rs.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths  = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths +=           /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths +=           /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths +=           /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
 
 namespace.rs.links = default,vndk
-namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.rs.link.default.shared_libs  =  %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 # Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
 # namespace because RS framework libs are using them.
 namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
+
 namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
 ###############################################################################
@@ -99,17 +173,43 @@
 ###############################################################################
 namespace.vndk.isolated = true
 namespace.vndk.visible = true
-namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
 
-namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
+namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.permitted.paths  = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+
+namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
+
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}/hw
 
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
 namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%:%SANITIZER_RUNTIME_LIBRARIES%
+namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
@@ -133,13 +233,38 @@
 namespace.default.isolated = true
 namespace.default.visible = true
 
-namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}
-namespace.default.permitted.paths = /vendor
+namespace.default.search.paths  = /odm/${LIB}
+namespace.default.search.paths += /odm/${LIB}/vndk
+namespace.default.search.paths += /odm/${LIB}/vndk-sp
+namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /vendor/${LIB}/vndk
+namespace.default.search.paths += /vendor/${LIB}/vndk-sp
 
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor
+namespace.default.permitted.paths  = /odm
+namespace.default.permitted.paths += /vendor
+
+namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
+namespace.default.asan.search.paths +=           /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths +=           /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.default.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+
+namespace.default.asan.permitted.paths  = /data/asan/odm
+namespace.default.asan.permitted.paths +=           /odm
+namespace.default.asan.permitted.paths += /data/asan/vendor
+namespace.default.asan.permitted.paths +=           /vendor
 
 namespace.default.links = system
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%:%VNDK_SAMEPROCESS_LIBRARIES%:%VNDK_CORE_LIBRARIES%
+namespace.default.link.system.shared_libs  = %LLNDK_LIBRARIES%
+namespace.default.link.system.shared_libs += %VNDK_SAMEPROCESS_LIBRARIES%
+namespace.default.link.system.shared_libs += %VNDK_CORE_LIBRARIES%
 
 ###############################################################################
 # "system" namespace
@@ -148,6 +273,14 @@
 # a vendor process.
 ###############################################################################
 namespace.system.isolated = false
-namespace.system.search.paths = /system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}
 
-namespace.system.asan.search.paths = /data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/system/${LIB}:/system/${LIB}
+namespace.system.search.paths  = /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.system.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.system.search.paths += /system/${LIB}
+
+namespace.system.asan.search.paths  = /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.system.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.system.asan.search.paths += /data/asan/system/${LIB}/vndk${VNDK_VER}
+namespace.system.asan.search.paths +=           /system/${LIB}/vndk${VNDK_VER}
+namespace.system.asan.search.paths += /data/asan/system/${LIB}
+namespace.system.asan.search.paths +=           /system/${LIB}